I'm learning RoR by building my first app (yay!). I gotta a question thought as rails guides do not cover this topic:
How to render unique results on #show to a user without storing any data in a model?
Steps I want to take:
Create a basic index view with a form_tag that will allow user to submit a link (string) and click submit button
Write Service Objects that will allow me to parse that link and create a response I want user to see
I want to write a #show method in a separate controller that will allow me to display all the data. (I also want to parse my params[:link] in that method using Service Objects.
I want to finally display this data in a table in #show view (probably I need to create a unique #show/[:id] for each user?
Here's what my app looks like at the moment (more or less):
Static Controller (just to render index.html.erb with a form)
class StaticController < ApplicationController
def index
end
end
Static Index view (yup, parsing imgur link here)
<h1>Hello Rails!</h1>
<%= form_tag("/images", method: "post") do %>
<p>
<%= label_tag(:imgur_link) %><br>
<%= text_field_tag(:imgur) %>
</p>
<p>
<%= submit_tag("Get my cards") %>
</p>
<% end %>
Images Controller (where all the magic SHOULD happen)
class ImagesController < ApplicationController
def show
#collection = params[:imgur_link]
#service1 = service1.new(*args).call
#service2 = service2.new(*args).call
...
end
end
Images Show view
Empty as I'm stuck with the Images controller at the moment.
Any help would be more than appreciated.
Thanks!
There is no reason you should put something into storage just in order to display it. If you get to a point when you have the results in your controller, you could just pass them to view in some #variable
As I see, you have set up the form for step 1. If you also have routes.rb call 'images#show' for POST /images, then you will have params[:imgur_link] available in your show action. This should do:
# config/routes.rb
YourApplication.routes.draw do
# ...
post '/images' => 'images#show'
end
Now you have to somehow process that link. Since I don't know what your results should be, I'm going to assume that you have two classes, Service1 and Service2, both of which accept an URL and return collection of results, and both collections hold the elements of the same class. Then you can leave only unique results for your show view, like this:
# app/controllers/images_controller.rb
class ImagesController < ApplicationController
def show
link = params[:imgur_link]
results1 = Service1.new(link).results
results2 = Service2.new(link).results
#results = (results1 + results2).uniq
end
end
Then you can do something with #results in your show view. E.g.
# app/views/images/show.html.erb
<% #results.each do |result| %>
<%= result.inspect %>
<% end %>
Related
I'm building a Rails app where I have individual entries called films. I would like to display the latest entry's link on the homepage (separate controller) and I'm struggling to make it work.
My films_controller.rb is as follows (excerpt):
def show
#film = Film.find(params[:id])
end
My home_controller.rb only has the following:
def index
end
And my view file (index.html.erb) has the following:
<%= link_to #film.last.filmTitle, film_path(#film) %>
I'm getting the following error:
Couldn't find Film with 'id'=#<Film::ActiveRecord_Relation:0x007fc93f2d1fd0>
With the #film.find(params[:id]) highlighted.
Thanks!
The last method:
Find the last record (or last N records if a parameter is supplied). If no order is defined it will order by primary key.
source
You can add a #last_film instance variable in your index controller and use it in the view.
def index
#films = Film.all
#last_film = Film.last
end
and in your index.html.erb
<%= link_to #last_film.filmTitle, film_path(#last_film) %>
The index method need something, currently, it didn't connect with ActiveRecord like model or table, that's why
Couldn't find Film with 'id'=#<Film::ActiveRecord_Relation:0x007fc93f2d1fd0>
So if you need to show recent posts in the index then you could something like this
def index
#films = Film.limit(10).order(created_at: :desc) #=> or you can use id
end
it will show last 10 records, for this in the index.html.erb like this
<% #films.each do |film| %>
<%= link_to film.filmTitle, film_path(film) %>
<% end %>
In the other hand if you need to show only one post which is the last then you should modify this query like this like limit(10) to limit(1) or you can use use the last method like this
def index
#film = Film.last
#or
##films = Film.limit(1).order(created_at: :desc) #=> or you can use id
end
if you use this #film = Film.last then your index file will like this
<%= link_to #film.filmTitle, film_path(#film) %>
otherwise, you need to use each method which describes before.
I stored all the tablename I've created to Menu table. And every time I add the table in Menu, it will automatically create a link under Menu list
see below.
I want each table in Menu to have a Listing, New, Edit, and Delete.
see below.
I have a controller prj_menus_controller, I will just pass the id of the table from Menu table.
here is the code for index and new in my controller.
Class PrjMenusController < ApplicationController
def index
#prj_menus = Menu.find(params[:id]).tablename.singularize.classify.constantize.all
end
def new
#prj_menu = Menu.find(params[:id]).tablename.singularize.classify.constantize.new
end
def create
#prj_menu = Menu.find(params[:id]).tablename.singularize.classify.constantize.new(prj_menu_params)
if #prj_menu.save
redirect_to :action => 'index'
else
render :new
end
end
private
def prj_menu_params
params.require("HERE IS MY PROBLEM").permit(:name)
end
end
and in my
new.html.erb
<%= simple_form_for (#prj_menu),:url => prj_menus_path, :method => :post do |f| %>
<%= f.input :name %>
<%= f.submit 'Save', class: 'btn btn-primary' %>
<%= link_to "Cancel", :back, {:class=>"btn btn-default"} %>
<% end %>
I can get the list in my index.html.erb, it is working. My problem is that I don't know how to get all params when I click the submit in new.html.erb. I got this hash
{"sample1_table"=>{"name"=>"test 6"}, "commit"=>"Save","controller"=>"prj_menus", "action"=>"create"}
It is correct but I don't know what to put in my controller. I tried this params.require(["#{#prj_menu}"]).permit(:name), it creates new record but params[:name] does not save.
I am still a noob to Ruby On Rails and I don't know what to search for this.
I think you are mostly confused on what parameter whitelisting does and how parameters are passed from the form to the controller.
I does not really matter if the name of the form hash matches the name of the database table. It just does in most cases since that makes the most sense. It's simply representative of the REST interface of your app.
Let's say you have a action which creates Pets:
POST /pets
And in our form we have a bunch of inputs like so:
<input name="pet[name]">
Rails will map create a params[:pet] hash { name: 'Spot' }. But we want to save the pets as an Animal.
class PetsController < ApplicationController
def new
#pet = Animal.new()
end
def create
#pet = Animal.new(pet_params)
if #pet.save
# ...
end
def pet_params
params.require(:pet).permit(:name)
end
end
Animal does not care what the params key is, it just gets a hash. But we also need to tell simple_form what parameter key we want to use since it looks at the model_name attribute.
simple_form_for(#pet, as: :pet)
Gives us pet[name] instead of animal[name].
I don't get why you are so adamant about making things so difficult for yourself though unless you are creating a database administration tool in the vein of PHP_MyAdmin. And even that case you don't even want to be altering the schema of the app database at runtime.
You are going to run into huge problems when it comes to creating effective queries for getting all the menus.
Fresh learning rails, be gentle. No programming experience, but learning.
Building simple app: An app that asks "what's the most important thing you can do right now," gives you an answer field, submits it, and then displays the stored important things.
Ideally, they won't be stored on the index page, but for learning purposes, I'm trying to get them to do this.
Controller code:
class FacilitatesController < ApplicationController
def index
#facilitate = Facilitate.all
end
def new
#facilitate = Facilitate.new
end
def create
#facilitate = Facilitate.new(params[:facilitates])
#facilitate.save
redirect_to #facilitate
end
private
def facilitate_params
params.require(:facilitate).permit(:answer)
end
def show
#facilitate = Facilitate.find(params[:id])
end
end
Index View code:
<h1>Impactful Task Elicitation</h1>
<h1>Listing Stored To dos</h1>
<table>
</table>
<%= link_to 'Store impactful tasks', new_facilitate_path %>
NEW view code:
<h1>What is the most impactful task?</h1>
<p>Store below, motherbiatch</p>
<%= form_for :facilitate, url: facilitates_path do |f| %>
<p>
<%= f.label :answer %><br>
<%= f.text_area :answer %>
</p>
<p>
<%=f.submit 'Save Task' %>
</p>
<% end %>
So far, I can navigate from index, to facilitates/new, and answer the question, to store my important to do. It then takes me to facilitates/33 (ID I'm assuming, or the number that I'm on, task wise)
I'd like to display these tasks both on the facilitates/33 (or whatever number it ends up being) page, as well as the index page.
I've followed directions on a similar type of form here: http://guides.rubyonrails.org/getting_started.html but, I still can't get my stored To do's to display anywhere.
Any help would be awesome.
In your controller, you have the show method below the private line. That means that it can only be called from inside the controller, so you are being sent to the show template without that method ever being called (#facilitate will be nil).
Move the def show method up above the private line.
It then takes me to facilitates/33 (ID I'm assuming, or the number that I'm on, task wise)
The line redirect_to #facilitate, means that after the facilitate is created, go to it's show method and page. The 33 is just a database reference for that particular facilitate, that it can be looked up again with Facilitate.find(params[:id]).
You didn't post what app/views/facilitates/show.html.erb looks like, but if you want to display the newly created facilitate, then it should have a line like this:
<%= #facilitate.answer %>
I'd like to display these tasks both on the facilitates/33 (or whatever number it ends up being) page, as well as the index page.
If you only care about the listing, and not individual facilitates pages, then after creation you can redirect back to the index in the create method by changing redirect_to #facilitate to redirect_to facilitates_path (which translates to '/facilitates').
EDIT:
The <%= #facilitate.answer %> example was meant for the show view, not index.
On index, you'd do something more like this:
<% #facilitate.each do |facilitate| %>
<%= facilitate.answer %><br>
<% end %>
To list them all.
So I've got two records from Model, a and b. I want to do this:
def do_codependant_stuff(a,b)
a.attribute += b.attribute2
b.attribute = b.stuff+a.stuff
end
I keep hearing fat model, skinny controller, and no business logic in views, so I've put this in my Model model. Based on what a user clicks in one of my views, I want to call do_codependant_stuff(a, b) or do_codependant_stuff(b,a).
I've been just using the basic crud controller actions up to this point, and I'm not quite sure how to make this happen. Do I add this logic to my ModelController update action? Because it's technically updating them, just in a more specific way. Or make another action in the controller? How do I call it/set it up? Most of the default new, update etc are somehow called behind the scenes based on their respective views.
And is updating two things at a time bad practice? Should I split the do_codependant_stuff method into two instance methods and call them on each record with the other as a parameter?
Thanks for reading.
Edit:
Alright, real world code. I'm displaying on the home page of my app two pictures. The user selects the one they like most. The ratings of these pictures change based on a chess ranking algorithm. This is the relevant section of my picture class.
class Picture < ActiveRecord::Base
...
...
def expected_first_beats_second(picture first, picture second)
1/(1+10**((second.rating-first.rating)/400))
end
def first_beat_second(picA,picB)
Ea = expected_first_beats_second(picA,picB)
picA.rating += 50*(1-Ea)
picB.rating += 50*(-(1-Ea))
end
end
The partial I'm using for the view just displays two pictures at random so far with
<% picA = Picture.offset(rand(Picture.count)).first %>
<% picB = Picture.offset(rand(Picture.count)).first %>
<div class = "row">
<div class = "col-md-5">
<%= image_tag picA.url, :class => "thumbnail" %>
</div>
<div class = "col-md-5">
<%= image_tag picB.url, :class => "thumbnail" %>
</div>
</div>
I need to somehow link the onclick of those images to the method in the model.
Here's some code to get you started. Note the comments, they're ruby and rails best practices
Controller:
class PC < AC
...
def compare
# count query once, save the number
count = Picture.count
#pic_a = Picture.offset(rand(count)).first
#pic_b = Picture.offset(rand(count)).first
end
def compare_submit
# Note variables are snake cased, not camel cased
pic_a = Picture.find(params[:winner_id])
pic_b = Picture.find(params[:loser_id])
pic_a.beats(pic_b)
redirect to compare_pictures_path # Or wherever
end
...
end
Any sort of querying should be done in the controller or model. You essentially should never directly access a model in your view. At this point your view has access to the instance variables set in the controller: #pic_a and #pic_b
View:
<%= link_to compare_submit_pictures_path(winner_id: #pic_a.id, loser_id: #pic_b.id) do %>
<%= image_tag #pic_a.url, :class => "thumbnail" %>
<% end %>
<%= link_to compare_submit_pictures_path(winner_id: #pic_b.id, loser_id: #pic_a.id) do %>
<%= image_tag #pic_b.url, :class => "thumbnail" %>
<% end %>
So we just linked to a new path (see routes below) that will pass two parameters: winner_id and loser_id so whichever picture the user clicks on you'll know which one they chose and which one they didn't choose.
Model:
class Picture < AR::Base
# Method arguments don't need a type declaration
def beats(loser)
# Always lowercase variables
ea = 1/(1+10**((loser.rating-self.rating)/400))
self.rating += 50*(1-ea)
loser.rating += 50*(-(1-ea))
self.save
loser.save
end
end
This explicitly uses self for clarity, which isn't necessary. Calling save or rating = ... implicitly calls it on self since we're in the context of an instance method on the picture Model.
Routes:
resource :pictures do
collection do
get :compare
get :compare_submit
end
end
There doesn't appear to be a gem for this, and I think a CMS is overkill as the client only wants to edit the welcome message on the home page!
Here's what I think I should do:
1) Create Page model:
rails g model Page name:string
2) Create Field model:
rails g model Field name:string content:string page_id:integer
3) Create relationship, Page h1:b2 Field
4) Create rake task to set up the message field that belongs to the welcome page:
namespace :seeder do
namespace :initial_seed do
task pages: :environment do
p = Page.create(name: "Welcome")
p.fields.create(name: "welcomemessage", content: "everything goes here. The long rambling welcome!")
end
end
end
5) Create a 'static' controller for the 'static'-ish pages. The home, the about us etc...
class Static < ApplicationController
def home
#fields = Page.where().fields
end
end
6) In the view, populate the welcome message from the database (I'll create a helper for this):
<% field = #fields.find {|x| x[:name] == 'welcomemessage' } %>
<%= field.content %>
So that's the reading done. Now onto the creation, updation and deletion:
6) Create a control panel controller:
class Panel < ApplicationController
def pages
#pages = Page.all
end
end
7) Display fields in the view at panel/pages.html.erb: (I'll use partials here)
<% #pages.each do |page| %>
Title: <%= page.name %>
<% page.fields.each do |field|%>
Field: <%= field.name %>
<% form_for(field) do |f| %>
<% f.text_area :content%>
<% f.submit %>
<%= end %>
<% end %>
<% end %>
Now this is just a rough run down of what I want to do. There are a few problems I want to query, though.
Is this sort of how you would do this?
How should I configure my routes? What is a clever way of populating the #fields variable (see step 5) with the fields for the page we're viewing?
If I do have a panel/pages.html.erb view, should it simply display all of the editable fields in text areas? How should it update these areas? Multiple submit buttons inside multiple forms? What if someone wants to edit many fields at once and submit them all at once?
Where should these forms go? Should I create multiple RESTful actions all inside the Panel controller like this?:
class Panel < ApplicationController
# new and create not present as the pages have to be created manually
# Enabling the user to create their own pages with their own layouts is a bit insane
def pages
#pages = Page.all
end
def pages_update
end
def pages_destroy
end
end
Multiple restful routes in one controller doesn't strike me as organised, but it would make it easier to lock down the panel controller with a before_action hook to redirect if not admin...
Also, I'm nearing the end of a big job, and all I need to do is add the ability to edit one field on one page and them I'm done and I really don't want to have to figure out alchemy_cms or whatever. In future, yes, but, please, please, please someone give me some small pointers here.
I would strongly advise against building your own CMS. It's fraught with difficulties, and it seems like you're running up against some of those now. You should go and check out something like AlchemyCMS.