Helper method or Model or Controller? - ruby-on-rails

Say I have a form_for with a select menu to assign a User on a belongs_to association:
...
form.select :user_id, #users, :prompt => "Select a User"
...
Currently I have #users in the controller as follows:
#users = User.all.map { |u| [u.full_name, u.id] }
I feel like this logic should maybe moved into a helper or even to the model.
But I am confused as to where to deal with this and how.

The general answer depends on how often you're going to use it:
helper: used often but only in views or controllers
model: used often anywhere the model can be used (other models)
controller: used rarely and only for specific actions.
However in your case, the answer is none of the above, and stop trying to reinvent the wheel. About 95% of the things people try to do with Rails are tasks that others have already done. There's a very good chance that it's either in the Rails Core or exist in either gem or plugin form.
What you're trying to do has already been done, and is built into the Rails core. It's a ActionView::Helpers::FormOpitionsHelper method called collection_select
collection_select does exactly what you want to do, it's also much more robust than a single purpose method.
It has the form of
collection_select(object, method, collection, value_method,
text_method, select_options = {}, html_options)
value_method and text_method are sent to each item in collection to get the select value and the display text for each select option. It is not required that either are column names.
Use it like this:
<% form_for #whatever do |form| %>
<%= form.collection_select :user_id, User.all, :id,
:full_name, :prompt => "Select a User" %>
<% end %>

You should put this in a model as it's logic oriented and by the way you should never do
#users = User.all.map { |u| [u.full_name, u.id] }
but
#users = User.all(:select => "full_name, id")
and if full_name is a method, something like that :
#users = User.all(:select => "last_name, first_name, id").map{|u| [User.full_name(u.first_name, u.last_name), u.id]}

That would be a model method since it has logic for the model. Helper methods should have UI level logic (whether to display a link or not) and HTML helpers (methods to generate links for example)

i think moving that to a helper is the best thing, since it just does help you with creating options for a select box, which is UI level.
But unless you would use that piece of code again, then to the model it must go! :)

The model:
def self.select_display
all(:select => "id, first_name, last_name").map { |u| [u.name, u.id] }
end
The view:
select :user_id, User.select_display

I had a similar problem and ended up using a module, in order to stay as DRY as possible (most of my models had a name and an id)
The module looked like this:
#lib/all_for_select.rb
module AllForSelect
def all_for_select(permission = :read)
#used declarative authorization for checking permissions
#replace first line with self.find(:all, if not using it
with_permissions_to(permission).find( :all,
:select =>"#{table_name}.id, #{table_name}.name",
:order => "#{table_name}.name ASC"
)
end
end
On your model, you just extend the module:
class Client < ActiveRecord::Base
extend AllForSelect
...
end
On your controller, you can call Client.all_for_select. I usually do this on a before_filter
class SalesController < ApplicationController
before_filter :fill_selects, :only => [:new, :edit, :update, :create]
...
private
def fill_selects
#clients = Client.all_for_select
end

Related

ActiveAdmin passing variable in controller

I have a permit and vehicle model. I am trying to update the AA create controller to work how I have it in my rails app. That is taking the vehicle license_number entered and inputting it into the permit table, then also taking the inputted permit_id and inputting it into the permits attribute of the vehicle it is related to in the vehicle table.
admin/permit.rb
permit_params :permit_id, :vehicle, :date_issued, :issued_by, :date_entered, :entered_by
form do |f|
f.inputs do
f.input :permit_id
f.input :vehicle, :collection => Vehicle.all.map{ |vehicle| [vehicle.license_number]}
f.input :date_issued, as: :date_picker
f.input :issued_by
end
f.actions
end
controller do
def new
#permit = Permit.new
#vehicle = #permit.build_vehicle
#vehicle = Vehicle.all
super
end
def create
vehicle = Vehicle.find_by(license_number: permit_params[:vehicle_attributes][:license_number])
#permit = current_user.permit.build(permit_params.merge(date_entered: Date.today,
entered_by: current_user_admin.email))
super
end
end
My errors that I am getting, is that it is inputting the license_number in for the permit_id and then it is also saying the permit_params is not a defined variable. Any help would be great, thanks!
You have an interesting case here: it is confusing because you have a model called Permit, and usually in Rails you name the params method something like permit_params. Turns out, permit_params is the general method signature for ActiveRecord to implement strong params: https://activeadmin.info/2-resource-customization.html
With that, instead of calling permit_params in your create action, you need to call permitted_params[:vehicle_attributes][:license_number]. That’s why it’s considering permit_params as an undefined variable. Again, those two method signatures will be the same for all your ActiveAdmin forms.
Additionally, I’m not sure if this is a typo but you define #vehicle twice in your new method. I’m not sure you need to build a vehicle for the permit form unless you’re doing nested forms. Either way, I think the last line should read #vehicles = Vehicle.all Then you can use that in your form, which also could use an update in the collection part of your form field:
form do |f|
f.inputs do
f.input :permit_id
f.input :vehicle, collection: #vehicles.map { |vehicle| [vehicle.license_number, vehicle.id] }
f.input :date_issued, as: :date_picker
f.input :issued_by
end
f.actions
end
The collection_select form tag will take the first item in the array as the value that appears in the form, and the second value will be the value passed through in the params (https://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/collection_select).
Then in your create action, you can find the Vehicle with the id:
Vehicle.find(permitted_params[:vehicle_attributes][:vehicle_id])
I would avoid Permit as a model name, try using VehiclePermit.

Rails association - what controller code gets executed?

I'm trying to make a change to some controller code. But, I don't understand where to put it.
I have the following in a new Contact form:
<%= f.association :location, :label_method => :name, :label => 'Location:' %>
I assumed that code would execute the index code in the location's controller.
But, I just deleted all of the code in the location index and the Contact form with the association to Location still has data in it.
I want the following code to execute at the Contact association stmt, but I don't know where to put it:
#locations = Location.ordered_by_ancestry_and(:name).map { |l| [" " * l.depth + l.name, l.id] }
UPDATE1
This is the development.log
Processing by ContactsController#new as HTML
Location Load (0.2ms) SELECT "locations".* FROM "locations" ORDER BY (case when locations.ancestry is null then 0 else 1 end), locations.ancestry, name
UPDATE2
I changed the ContactsController#new to this for testing:
# GET /contacts/new
# GET /contacts/new.json
def new
#locations = Location.first
And I still got all the locations in the select box.
The SimpleFormFor association helper method will generate a list (as a select list by default, but can be modified to radio or checkbox list) that acts as a nested attribute for that association. When submitted this field will be passed to the same controller action as the parent form - in this case the ContactsController#create action.
As far as your customized list of locations, you can pass this as a collection option to the association method:
<%= f.association :location, :collection => #locations, :label_method => :name, :label => 'Location:' %>
The actual place to build this #locations list would be in any action that may need to access it - that includes new and edit, as well as create and update (in case an error prevents your form from submitting). You can use a before_filter to eliminate any duplication.
The code may look something like:
class ContactsController < ApplicationController
before_filter :load_locations, :only => [:new, :edit, :create, :update]
#... actions go here
private
def load_locations
#locations = Location.ordered_by_ancestry_and(:name).map do |l|
[" " * l.depth + l.name, l.id]
end
end
end

Rails nested form with accepts_nested_attributes_for with an unfortunate model name

I have a Parent model named "Controller" (Mature app, and not my decision)
belongs_to :controller
accepts_nested_attributes_for :controller
Form:
= f.fields_for :controller do |c|
= c.hidden_field :id, :value => #controller.id
= c.text_field :slw_type
which doesn't get displayed.
= f.fields_for :literally_anything_else do |c|
= c.hidden_field :id, :value => #controller.id
= c.text_field :slw_type
if change the variable name to anything else, the form builds. I have a hunch that it's a rails specific reserved name.
Question:
What's the problem? and how can I make this work?
SOLVED:
The issue was that the parent model wasn't associated with the child model yet. My mistake for not providing all the information necessary.
This worked.
def new
#controller = Controller.find(params[:controller_id])
#inspection = Inspection.new(:controller => #controller)
Therefore my fields_for form builder also worked.
Pick some innocuous variable name. not_really_a_controller or whatever. Use that for your variable and your form. Then, in your actual controller (e.g. ActionController::Base descendent), rename the incoming param so the model doesn't know any different, like so:
before_filter :filter_params
private
def filter_params
if params[:not_really_a_controller]
params[:controller] = params.delete(:not_really_a_controller)
end
end
I've used this strategy for similar reasons in the past, though not specifically for controller. Worth a try though!

Retain Search Form Data Ruby On Rails

Trying to build a search on my homepage with simple_form (Pretty much same as formtastic). The search works fine and im getting my results but after submission I want to retain the vales with what the user submitted.
I am using a namespace for my form so how can I retain the data for the form. Here is some code which may help.
Controller
def index
#results = Property.search(params[:search])
end
View
%h1 Search Form
= simple_form_for(:search) do |f|
= f.input :location, :as => :select, :collection => Location.all.asc(:name)
= f.input :type, :collection => PropertyType.all.asc(:name)
= f.input :bedrooms, :collection => 1..10,
%p.box
= f.button :submit
-if #results
%h1 Search Results
.results
- #results.each do |property|
.result
%h1= property.title
Within the Index controller I have tried all sorts of things ie
#search = params[:search]
But each time I try something the search breaks.
What am I doing wrong ?
Hope you can advise
One approach is to do as Xavier Holt suggested, and pass in values to each input. The simpleform doco suggests:
= f.input :remember_me, :input_html => { :value => '1' }
The other approach is to have simpleform do it for you. SimpleForm will automatically populate the fields with values if you give it something like an activerecord object.
In this case, that means creating a model object:
class PropertySearchCriteria
attr_accessor :location, :type, :bedrooms
def initialize(options)
self.location = options[:location]
self.type = options[:bedrooms]
self.bedrooms = options[:bedrooms]
end
end
Then, change your controller:
def index
#property_search_criteria = PropertySearchCriteria.new(params[:search])
#results = Property.search(#property_search_criteria)
end
(you'll have to change the Property.search method as well)
Then, change your simple_form_for:
= simple_form_for(:search, #property_search_criteria) do |f|
And if you do all that, and get the stars to align just right, then simpleform will pre-populate the form fields all by itself. You may have to add some stuff to PropertySearchCriteria to get simpleform to be properly happy.
This is a lot of stuffing around just to get the values showing up, but it'll keep you sane if you need to add validations.
I'm doing something similar in the app I'm working on (I'm not using formtastic, but this should be at least very close to something that works for you). I got around it by making sure #search was a hash in the controller:
#search = params[:search] || {}
And then using #search[:key] as the :value option in all my search inputs (There's a chance you'll need to set #search.default = '' to get this working):
<%= text_field_tag :name, :value => #search[:name] %>
And that's all it took. As my app is getting more complicated and AJAXy, I've been thinking of moving the search parameters into the session information, which you might want to do now to stay ahead, but if you're just looking for a simple solution, this worked great for me.
Hope this helps!
you can try storing your parameters in session like so:
def index
#results = Property.search(params[:search])
store_search
end
def store_search
session[:search] = params[:search]
end
just be sure when you are done with the parameters that you clean them up
...
clear_search if session[:search]
def clear_search
session[:search] = nil
end

Create select based on routing, how?

I am trying to implement navigation like in Tree Based Navigation but based on URLs defined in routes.rb (named routes, resources, ...).
Is it possible to retreive a collection of all routes defined in routes.rb?
So I can use it in a select like this:
<%= f.collection_select :url, Route.all, :url, :name %>
Tnx!
ActionController::Routing::Routes.routes
Will list available routes for the application. Will require some parsing to pull out applicable details.
Thanx to the hint of David Lyod I solved it!
Here is my code:
helper-method
# Methods added to this helper will be available to all templates in the application.
module ApplicationHelper
def routes_url
routes = ActionController::Routing::Routes.routes.collect do |route|
segs = route.segments.inject("") { |str,s| str << s.to_s }
segs.chop! if segs.length > 1
segs.chomp("(.:format)")
end
routes.delete_if {|x| x.index(':id')}
return routes.compact.uniq.sort
end
end
and in my view I put:
<%= select("page", "url", options_for_select(routes_url), {:include_blank => true}) %>

Resources