Calling Rails helper inside a rabl collection - ruby-on-rails

I want to call a Rails helper method from my rabl template. I was able to it via Stack Overflow for one object.
This is my current setup (and it's working fine)
I have a helper
#app/helpers/application_helper.rb
module ApplicationHelper
def full_url(image)
"#{request.protocol}#{request.host_with_port}#{image.picture_url}" if recipe.image
end
end
#app/images/show.rabl
object #image
attributes :id, :picture_url
node(:picture_url) { full_url(#image) }
Now I want to do this for a list of image objects, but its not working and I cannot find it in rabl README.
The problem is it passes the #images list , I'm not sure how to call an the helper method on each individual object
#app/images/index.rabl
collection #images
attributes :id,
node(:picture_url) { full_url(#images) }

You can pass a param to your node block:
collection #images
attributes :id, :picture_url
node(:picture_url) {|image| full_url(image) }

Related

Ruby on Rails: What to call in view?

I have a next method in the model.
def self.next(comment, key = :id)
self.where("#{key} > ?", comment.send(key)).first
end
In my view I can say for example: (does not work)
= link_to "next", #comment.next(#comment)
What's the correct way to call this method?
routes.rb:
Rails.application.routes.draw do
resources :articles do
resources :comments do
end
end
end
You've defined next as a class method (vs an instance method), so you need:
= link_to "next", Comment.next(#comment)
If you want to be able to call #comment.next, define the instance method as:
def next(key = :id)
Comment.where("#{key} > ?", self.send(key)).first
end
It is not good style that the model knows this, you should put it in the controller. You should try a gem called kaminari, this gem lets you paginate over the elements, so in your comments controller you could have something like:
def show
#comment = Comment.order(id: :asc).page(params[:page]).per(1)
end
Then in your view, by just adding this kaminari helper:
<%= paginate #comment %>
You get the pagination bar below and everything works fine (gem's magic).
If you don't like this you could try to add that next method in the controller or find both next and current elements and link to the next element.
In my opinion the model is just a class that knows how to save and get information from the database and maybe some calculations with it's information, so all that logic related to the view should be elsewhere.

Calling Rails helper from rabl Rails

I'm trying to call a Rails helper from my rabl template, but it's not working.
I have an image object and I want to pass its pass through my api. So then image path should have the full url (Ex: http://<my url>/uploads/myimage.jpg). The host part I want to add through a Rails helper.
#my rabl file
object #image
attributes :id, :picture_url
What I want is something like (I tried this but doesn't work)
#my rabl file
object #image
attributes :id, full_url(:picture_url)
#app/helpers/application_helper.rb
module ApplicationHelper
def full_url(path)
"#{request.host}#{path}" #something like this
end
end
But when I do this, it completely drop the :picture_url attribute from the json object
I believe this might work for you:
object #image
attributes :id
node(:picture_url) { full_url(:picture_url) }
node allows you to define things that do not directly map to object attributes.
EDIT 1
It looks like I got the node syntax wrong. Try this instead:
node(:picture_url) { |i| full_url(i.picture_url) }
By calling full_url(:picture_url), attributes gets a string that doesn't correspond to a method on the #image object, i.e., #image.id returns something, while #image.http://... isn't even valid :).
I would suggest you leave the view as it was first, and set the value of the #image.picture_url using your helper in the controller.

How do I initialize or show a nested form in rails?

I follow the complex nested forms in railscast and I made it work, a few tweaks here and there. But question is, how do I automatically load a nested form without using the controller?
def new
#model = Model.new
#model.child.build
end
many thanks!
how do I automatically load a nested form without using the
controller?
The simple (incorrect) answer is to include a Partial which will define the ActiveRecord objects as above, like this:
#app/views/controllers/_your_partial.html.erb
<% model = Model.new %>
<% mode.child.build %>
<%= form_for model do |f| %>
...
The problem with this is that it goes against the MVC programming pattern, which is the equivalent of God in the Rails world
--
MVC
Your question is rather peculiar, as it goes against one of the core elements of Rails:
This means every request you make to your Rails application (through the URL) will have to be catered for by a controller action. The role of the controller is to configure / collect the data required to run the app, and then populate the view for the user.
When you want to "call the form without a controller", you're basically saying you want to go against these conventions. Unless you create a gem (which will allow you to extrapolate the ActiveRecord definition code outside of your controller), you'll have to use the controller.
There is a trick though...
--
Class Method
You'll be able to take the functionality you're using in your controller & create a class method to handle it:
#app/models/model.rb
Class Model < ActiveRecord::Base
def self.build
model = self.new
model.child.build
model
end
end
This will allow you to call the following from your controller / partial:
#model = Model.build #-> instead of Model.new :)
module ApplicationHelper
def setup_person(person)
returning(person) do |p|
p.children.build if p.children.empty?
end
end
end
I found this on Ryan Daigle's site but I realize this is a bit outdated. I tried this but I get a returning is not defined or not a method.

Adding a hidden input field to all the forms in rails

Is there a way to add a hidden input field to all the form that are declared in views in rails. The hidden field needs to have a value passed by a public property in the controller. Essentially I want any form that is given as response by the web server to have an additional hidden input element.
How I do this? Can I override the form_for by some means ? Or can I go with a wrapper of form in partials and enforce everybody to use the partial?
EDIT: OK, my first pass on this didn't work because you can't define a value in an initializer that you're later going to pass in from a controller. So you can go about this one of two ways.
You can define a CustomFormBuilder class - put it in an initializer -
class CustomFormBuilder < ActionView::Helpers::FormBuilder
def submit(value, options = {})
#template.hidden_field_tag(options.delete(:custom_param)) + super
end
end
Then pass the :builder option to form_for
form_for #whatever, builder: CustomFormBuilder
and assuming you call submit(value, options) in the form, where options includes your custom_param, it will be overwritten by the custom method that inserts your hidden field with the value you want.
The alternative is to monkey patch monkey patch FormTagHelper instead:
module ActionView::Helpers::FormTagHelper
def extra_tags_for_form_with_custom_param(html_options)
hidden_field_tag(html_options.delete('custom_param') +
extra_tags_for_form_without_custom_param(html_options)
end
alias_method_chain :extra_tags_for_form, :custom_param
end
That's tweaking the code form_for uses to insert the authenticity token at the top of every form. Now you can pass that custom param to form_for after setting it as an instance variable in the controller:
form_for #object, custom_param: #custom_param do |f|
If you are using Ruby 2.0+ then you can use module prepend instead of the deprecated alias_method_chain e.g.
module CustomParamFormPatch
private
def extra_tags_for_form(html_options)
hidden_field_tag(html_options.delete('custom_param')) +
super
end
end
ActionView::Base.prepend(CustomParamFormPatch)

Automatically append parameters to *_url or *_path methods (Rails)

I have a particular set of views relating to one of my controllers, whereby I want any call to *_path or *_url to append a set of parameters.
Is there some magic method I can override that will let me do this? I have no idea where in the Rails code the *_path or *_url methods are even handled.
Edit for clarity: I'm looking for a way to do this such that I don't have to modify every link in every view where this needs to occur. I don't want every coder who touches this set of views to have to remember to append a parameter to every link they add to the page. The same parameter should be appended automatically. I consider a change to the *_url or *_path call a failure. Similarly, having to override every *_url or *_path call is considered a failure since a new method would have to be added/removed whenever a new link is added/removed.
You can do this by overriding url_for since all the routing methods call it.
module ApplicationHelper
def url_for(options = {})
options.reverse_merge!(#extra_url_for_options) if #extra_url_for_options
super
end
end
Now all you need to do is use a before_filter to set #extra_url_for_options to a hash to force all urls.
class MyController < ApplicationController
before_filter do { #extra_url_for_options = { :format => 'html' } }
end
Note that this will force all links to use the extra options.
Thanks to Samuel's answer, I was able to create a final working solution via a new helper, which I've included below.
module ExampleHelper
def url_for(options={})
options = case options
when String
uri = Addressable::URI.new
uri.query_values = #hash_of_additional_params
options + (options.index('?').nil? ? '?' : '&') + uri.query
when Hash
options.reverse_merge(#hash_of_additional_params)
else
options
end
super
end
end
You can try to use the with_options method. In your view you can do something like
<% with_options :my_param => "my_value" do |append| -%>
<%= append.users_path(1) %>
<% end %>
Assuming you have the users_path of course. my_param=value will be appended to the url
You could make a helper method:
def my_path(p)
"#{p}_path all the parameters I want to append"
end
and in the view use
<%= eval(my_path(whatever)) %>
Eval with give you dynamic scope, so every variable available in your view can be used in the helper. If your parameters are constant you can get rid of eval calls.

Resources