Rails Rating System with strange helper method - ruby-on-rails

there's an issue, that is bothering me.
I'm following this "Ruby on Rails"-Tutorial to implement an ajaxified rating system
http://eighty-b.tumblr.com/post/1569674815/creating-an-ajaxified-star-rating-system-in-rails-3
Die author uses a self written helper method called "rating_ballot" which seems pretty redundant to me, because it checks if a rating has been given yet and otherwise forms a new one with
current_user.ratings.new
But that actually is being done more or less in the ratingscontroller
using this helper method the form looks like this
= form_for(rating_ballot, :html => { :class => 'rating_ballot' }) do |f|
But any other form (for example posting reviews) uses the instance variable instead of a helper method.
I want the form_for tag to look like this
= form_for(#rating, :html => { :class => 'rating_ballot' }) do |f|
but this only works for updating ratings, not creating new ones.
why is this "rating_ballot" so important ?

Take a look on that part
def rating_ballot
if #rating = current_user.ratings.find_by_photo_id(params[:id])
#rating
else
current_user.ratings.new
end
end
It try to find out #rating if exists and create new instance if not exsits
you can do it in you controller for example:
#rating = current_user.ratings.find_by_photo_id(params[:id])
#rating ||= current_user.ratings.new # create instance if did not find existing once
ans then use it in form like you wrote

I'm guessing the value of #rating is nil, which is why using this form for the #create action isn't working. The first argument should be a new object, or an object that represents an existing record, in order to create or update, respectively.
Another alternative way of using the form_for method is to supply a symbol representing the name of the class and also specifying the :url argument according to how your routes are specified. This is only good for creating though since the symbol doesn't represent an existing record.

Related

Affecting resulting params set from form_for

Relative newbie here to Ruby on Rails.
Using the standard form_for method in the view for my SomeobjController#new action
= form_for #someobj do |f|
.
.
.
%p.submits
= f.submit "Submit", :class => "submit"
a submission param[] array is produced that contains a hash of #someobj for all the fields set in the form, such that
param[someobj] => { "field1" => "val1", "field2" => "val2", ... }
I would prefer to put a different value, the result of someobj.to_s to param[someobj] for the SomeobjController#create to work with, such that
param[someobj] => "strvalfromtos"
I doubt it's relative, but just in case, the model underlying this #new action is not persistent in the database (i.e., Someobj is not derived from ActiveRecord::Base, though some portions of ActiveModel are included.)
I haven't had luck trying to adjust this until after #create is invoked, but its the submission from #new to #create that I want to amend. It's not clear to me if I should be focusing more on the form_for statement or doing something special in the controller (I'm guessing the form_for is the right focus).
And, yes, this whole thing is probably a bit OCD of me, but the actual fieldnames are long (appropriately for the model) but data needed by #create is very small.
Is there a relatively painless way to do this, assuming that someobj.to_s has already been written?
Many thanks,
Richard
Change
When you submit the form, your controller will receive the params hash, as you've stated (Rails params explained?)
That means you can change any value in the hash that you wish:
def create
#has access to the params hash
params[:owner][:key] = value
end
As the create method receives the hash object, you'll have to change it in here. But because it's a standard hash (which has already been declared), you should be able to alter it as required
Add
If you want to add values to the params hash, you can use the .merge method, like this:
def create
#has access to the params hash
params[:key].merge(user_id: current_user.id)
end

Rails Forms for custom actions

I'm trying to link the input of a form to a specific action in my rails app.
Currently if I go to www.myapp.com/check/:idNumber, I'll be able to trigger the action just fine (which means routes is setup properly?). This action is basically a function call to a ruby/rails script with the parameter "idNumber" being passed to it. If the function is successful, it would return a newly created "Person" object and nil otherwise. This is different than the standard new operation as it determines the the attributes based on some information that it obtained from a database somewhere else.
Rake routes does give me the following:
check /check/:idNumber(.:format) person#check {:id=>/\d+/}
What I'm having trouble implementing is the form itself.
<%= form_tag("/check", :method => "get") do %>
<%= text_field_tag(:idNumber) %>
<% end %>
Controller action:
def check
regCheck = RegCheck.new
#person = regCheck.check_id(params[:idNumber])
if #person.name == nil
redirect_to root_path
end
end
submitting the form above would bring me to myapp.com/check?utf8=✓&idNumber=1234 instead. Can someone tell me what am I doing wrong?
I believe that using the check_path helper that is generated from the routes file is your best bet.
The form should look like this then.
<%= form_tag(check_path) do %>
<%= text_field_tag(:idNumber) %>
<% end %>
Rails forms can be finicky, especially when trying to build really customized forms.
This line
= form_for [#object]
Determines where the form goes, as well as the object that is being implemented. If you want to route the form to a different place, you can user the :url option. This options determines the path of the form, however you must keep in mind that the method is determined by the #object. If it is a new object, the method will be POST, an existing object will use a PUT method.
Let's suppose you want to update an existing object, but you want to send in data for a new object belonging to the existing object. That would look like
= form_for [#object], :as => #child_object, :url => my_optional_custom_path do |f|
# etc...
This generates a form sending a PUT request to the custom url (or the update path for #object if no custom url is supplied. The PUT request is sent with the parameter params[:child_object].
Hopefully this helps!
Best,
-Brian
I don't think it's possible the way you're trying.. The URL for the form is created before the user inputs any data.. So you need to remove the :idNumber from your routing..
If you do you get the following route:
check /check(.:format) person#check
Because the regex is removed now, you need to do this in you're controller:
def check
# Make sure ID is digits only
idNumber = params[:idNumber].gsub(/[^\d]/, '')
regCheck = RegCheck.new
#person = regCheck.check_id(idNumber)
if #person.name == nil
redirect_to root_path
end
end
You're form is allright, but you may want to use check_path like TheBinaryhood suggests..
If you really want it to be check/:idNumber you may also be able to submit the form to another action and redirect it to the right path from there..

form_for :subject

I've just started learning ruby, so this question is simple.
I created #subject in controller.
Why is :subject used in form_for (instead of #subject)?
<%= form_for(:subject, :url => {:action => 'create'}) do |f| %>
It is similar to the assigns in rspec rails testing (you can look it up, assigns(:subject) will seek out for a #subject in your controller). Correct me if I am wrong but I think it is because the symbol :subject will try to seek out for an instance variable in your controller that correspond with #subject. So to prove it, rename your #subject in your controller to something else, like #subjectz and I think it won't work anymroe
According to the documentation, you could use either (http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for).
Passing in the symbol :subject will create a generic form for a resource named 'subject'. Passing in the instance variable #subject will create a form for the specific instance of the Subject class and figure out the correct url for you (assuming you're following the standard rails conventions). The documentation mentions that using the latter method with the instance variable is the preferred method.

Rails - link_to, routes and nested resources

As my understanding on nested resources, on edge Rails, should not
link_to 'User posts', #user.posts
point to
/users/:id/posts
?
The routes.rb file contains
map.resources :users, :has_many => :posts
If this is not the default behavior, can it be accomplished doing something else?
Along the same lines as Rishav:
link_to "User Posts", [#user, :posts]
Here's an explanation from my blog.
Really early on in Rails, you would write routes like this:
redirect_to :controller => "posts", :action => "show", :id => #post.id
What this would do is dutifully redirect to the show action inside the PostsController and pass along the id parameter with a
value of whatever #post.id returns. Typical 302 response.
Then Rails 1.2 came along and allowed you to use routing helpers, like this:
redirect_to post_path(#post)
And the people rejoiced.
This would do effectively the same thing. post_path here would build a route using the #post object that would look something
like /posts/1 and then redirect_to would send back a 302 response to that route and the browser would follow it.
Then later versions (I can't remember which one), allowed syntax like this:
redirect_to #post
And the people rejoiced a second time.
Magic, but not really
Any sufficiently advanced technology is indistinguishable from magic.
While this seems like magic, it's not. What this is doing is actually very, very neat. The redirect_to method, much like its cousins link_to and form_for all use a common method to build URLs, called url_for. The url_for method takes many different
varieties of objects, such as strings, hashes or even instances of models, like in the example above.
What it does with these objects then, is quite neat. In the case of the redirect_to #post call above, it inspects the #post
object, sees that it is an object of the Post class (we assume, anyway) and checks to see if that object has been persisted in a
database somewhere by calling persisted? on it.
By "persisted", I mean that a Ruby object has a matching record in the database somewhere. The persisted? method in Active Record is implemented like this:
def persisted?
!(new_record? || destroyed?)
end
If the object wasn't created through a call such as Model.new then it won't be a new record, and if it hasn't had the destroy method called on it won't be
destroyed either. If both of these cases are true, then that makes the object has most likely been persisted to the database in the form of a record.
If it has been persisted, then url_for knows that this object can be found
somewhere, and that the place it can be found is most likely under a method called post_path. So it calls this method, and passes
in the to_param value of this object which is usually the id.
In short, it's effectively doing this:
#{#post.class.downcase}_path(#post.to_param)
Which comes out to being this:
post_path(1)
And when that method is called you would get this little string:
"/posts/1"
Lovely!
This is called polymorphic routing. You can pass an object to methods like redirect_to, link_to and form_for and it will
attempt to work out the correct URL of what to use.
The form of form_for
Now, when you're coding Rails you may have used form_for like this a very long time ago:
<% form_for #post, :url => { :controller => "posts", :action => "create" } do |f| %>
Of course, with advancements in Rails you could simplify it to this:
<% form_for #post, :url => posts_path do |f| %>
Because the form is going to default to having a POST HTTP method and therefore a request to posts_path is going to go to the
create action of PostsController, rather than the index action, which is what would result if it were a GET request.
But why stop there? Why not just write this?
<%= form_for #post do |f| %>
Personally, I see no reason not to... if it's something as simple as this. The form_for method uses url_for underneath, just like
redirect_to to work out where the form should go. It knows that the #post object is of the Post class (again, we assume) and it
checks to see if the object is persisted. If it is, then it will use post_path(#post). If it's not, then posts_path.
The form_for method itself checks to see if the object passed in is persisted also, and if it is then it'll default to a PUT HTTP
method, otherwise a POST.
So this is how form_for can be flexible enough to have an identical syntax on both a new and edit view. It's becoming more and
more common these days for people to even put their whole form_for tags into a single partial and include it in both the new and
edit pages.
A more complex form
So form_for is fairly simple for when you pass a normal object, but what happens if you pass an array of objects? Like this, for
instance:
<%= form_for [#post, #comment] do |f| %>
Well, both url_for and form_for have you covered there too.
The url_for method detects that this is an array and separates out each part and inspects them individually. First, what is this
#post thing? Well, in this case let's assume it's a Post instance that is persisted and has the id of 1. Second, what is this
#comment object? It's a Comment instance that has not yet been persisted to the database.
What url_for will do here is build up the URL helper method piece by piece by placing each part in an array, joining it into a routing method and then calling that routing method with the necessary arguments.
First, it knows that the #post object is of the Post class and is persisted, therefore the URL helper will begin with post. Second, it knows that the #comment object is of the Comment class and is not persisted, and therefore comments will follow post in the URL helper build. The parts that url_for now knows about are [:post, :comments].
The url_for method combines these individual parts with an underscore, so that it becomes post_comments and then appends _path
to the end of that, resulting in post_comments_path. Then it passes in just the persisted objects to the call to that method, resulting in a call like this:
post_comments_path(#post)
Calling that method results in this:
"/posts/1/comments"
Best part? form_for will still know to use POST if the #comment object is not a persisted object, and PUT if it is. A good
thing to remember is that the form_for is always for the last object specified in the array. The objects prior to it are just its
nesting, nothing more.
The more objects that are added, the more times url_for will do the hard yards and build the path out... although I recommend that
you keep it to just two parts.
A symbolic form
Now that we've covered using an array containing objects for form_for, let's take a look at another common use. An array containing
at least one Symbol object, like this:
<%= form_for [:admin, #post, #comment] do |f| %>
What the url_for method does here is very simple. It sees that there's a Symbol and takes it as it is. The first part of the
url will simply be the same as the symbol: admin. The URL that url_for knows of at this point is just [:admin].
Then url_for goes through the remaining parts of the array. In this case, let's assume both #post and #comment are persisted
and that they have the ids of 1 and 2 respectively. Same classes as before. url_for then adds post to the URL that it's building,
and comment too, resulting in [:admin, :post, :comment].
Then the joining happens, resulting in a method of admin_post_comment_path, and because both #post and #comment are persisted here,
they're passed in, resulting in this method call:
admin_post_comment_path(#post, #comment)
Which (usually) turns into this path:
/admin/posts/1/comments/2
You can use the array form of polymorphic routing with the redirect_to, link_to and form_for methods. There's probably other
methods that I'm not remembering right now that can do it too... it's generally anything in Rails that would normally take a URL.
There's no need to build your URLs in any Rails version greater-than 2 using hashes; that's pretty old school.
Instead, experiment with your new knowledge of polymorphic routing and use it to the best of your advantage.
This should work:
link_to "User Posts", user_posts_path(#user)
for more details visit:
http://guides.rubyonrails.org/routing.html
link_to uses url_for which uses polymorphic_url.
polymorphic_url:
builds the helper method, using the class name of active record objects
calls the helper with the active record objects as arguments
Therefore, as others said, you should use:
link_to 'User Posts', [#user, :posts]
for which the path is:
user_posts_path(#user)
^^^^ ^^^^^ ^^^^^
1 2 3
class of #user because it is an active record
convert to string because symbol
add as call argument because active record
That builds the good helper method.
This is how to link to a nested resource in the latest Rails:
link_to 'Destroy Comment', post_comment_path(comment.post, comment)
Note: This is in a partial so there isn't a #.

accessing POST parameters

When I add a new "product" using my scaffold create rails app, the following line properly adds a new product
#product = Product.new(params[:product])
When I try to add a new product using the following URL (trying to POST data up from a java program).
http://localhost:3000/products?serial=555&value=111
The product is not created, however I can access the "serial" and "value" values like this:
#product = Product.new
#product.serial=params[:serial]
#product.value=params[:value]
#product.save
To further confuse me, if I use the rails app to add a new product, the params[:serial] and params[:value] variables are empty.
Can someone please point me in the right direction.
Thanks
The Model.new method takes a hash.
params[:product] actually contains something like {:serial => 555, :value => 111}
The url you would want to use is:
http://localhost:3000/products?product[serial]=555&product[value]=111
(Make sure that you are indeed using POST)
If you want to keep your current url scheme you would have to use:
#product = Product.new({:serial => params[:serial], :value => params[:value]})
You can also determine exactly what is available inside of params by printing it out to console using:
p params
Good luck!

Resources