How to pass a parameter inside a simple_form group_method? - ruby-on-rails

I'm trying to display active projects per party in a drop down list. active_projects is a method within the Party model. The grouped_collection_select code below works however, when I attempt to convert my form into a simple_form, my active_projects method is no longer recognised.
Below are my two code extracts. The first working correctly while the other causes an error.
# rails default
<%= f.grouped_collection_select(:project_id,
Party.all,
:"active_projects(#{date.strftime("%Y%m%d")})",
:party_name,
:id, :project_name) %>
# simple form
<%= f.input :project_id,
collection: Party.all, as: :grouped_select,
group_method: :"active_projects(#{date})" %>

I know this one is a little old but I have a solution to this problem using simple_form. I am not sure if it is the best solution but it does work.
Basically, the issue comes down to passing in a value to the group_method. In my case I had a class that needed to get the current_users company that he/she belongs to. My model/database structure was like this:
Type -> Category
In my case the Type records were global and did not belong to a specific company. However, the category model records did belong to a specific company. The goal is to show a grouped select with global types and then company-specific categories underneath them. Here is what I did:
class Type < ActiveRecord::Base
has_many :categories
attr_accessor :company_id
# Basically returns all the 'type' records but before doing so sets the
# company_id attribute based on the value passed. This is possible because
# simple_form uses the same instance of the parent class to call the
# group_by method on.
def self.all_with_company(company_id)
Type.all.each do |item|
item.company_id = company_id
end
end
# Then for my group_by method I added a where clause that reuses the
# attribute set when I originally grabbed the records from the model.
def categories_for_company
self.categories.where(:company_id => self.company_id)
end
end
So the above is a definition of the type class. For reference here is my definition of the category class.
class Category < ActiveRecord::Base
belongs_to :company
belongs_to :type
end
Then on my simple_form control I did this:
<%= f.association :category, :label => 'Category', :as => :grouped_select, :collection => Type.all_with_company(company_id), :group_method => :categories_for_company, :label_method => :name %>
Basically instead of passing in the value we want to filter on in the :group_method property we pass it in on the :collection property. Even though it will not be used to get the parent collection it is just being stored for later use in the class instance. This way, when we call another method on that class it has the value we need to do our filtering on the child.

Related

Correct way to construct view when using nested attributes

I'm having difficulty understanding how I should build my view to ensure the update of my project model also updates the appropriate related records
I have the following models: Project, User and AuthorisedUser
Each project has a list of authorised users who are permitted access, and the selection of authorised users is made from a drop-down picklist of all users within the project edit view.
I'm keen to make use of as much of the Rails 'magic' as possible, so my understanding is that in order for the project.update method to deal with saving the collection of authorised_users for the project, I need the following associations:
(Project Model)
has_many :authorised_users
has_many :users, through: :authorised_users
accepts_nested_attributes_for :authorised_users
(User Model)
has_many :authorised_users
has_many :projects, through: :authorised_users
(Authorised User Model)
belongs_to :project
belongs_to :user
What I'm having difficulty understanding is how to construct my view such that the authorised users (i.e. those selected from the list of all users) will appear as required in the params presented to the controller- I think I need somehow to include a reference to the AuthorisedUser model, but I've only been able to find examples of this where the fields_for helper is used, for example:
= project_form.fields_for :authorised_users do |auf|
- selected = #project.authorised_users
= auf.label :user_id, 'Authorised Users'
= auf.collection_select('authusers', User.all, :id, :username, {:prompt => 'Blah', :selected => selected}, {multiple: true, class: 'form-control'})
Although this does result in the appearance of authorised_user_attributes within the controller params, this isn't quite right since the block will (obviously) repeat the selection for every authorised user- I just want one selection box to appear, from which selected users can be saved as 'authorised' against the project
This probably isn't as difficult as I seem to be making it, but I'd be grateful for some clarity on the best approach- for example:
Can this be done implicity by Rails as part of the project.update, or must I iterate through the collection of authorised_users in the project controller, manually updating the associated records?
It's easier than what you're trying to do.
You don't need accepts_nested_attributes_for :authorised_users in the Project model, since you want only to update user_ids. Therefore, you don't need fields_for.
- selected = #project.authorised_users
= project_form.label :user_ids, 'Authorised Users'
= project_form.collection_select :user_ids, User.all, :id, :username, {:prompt => 'Blah', :selected => selected}, {multiple: true, class: 'form-control'})
Don't forget to add user_ids: [] to permitted parameters and remove any unused parameters from the first implementation.

Setting Nested Attributes of an existing record based on specific parameters

My first post here so I hope I'm including all the necessary information! Happy I've managed to make it two months into learning without having to ask any questions yet. :-)
I have a Callsheets record with a nested Store record that contains a :lastvisit column that I would like to update each time a new Callsheet is submitted every month. The :lastvisit field should be updated where the Store :id == #callsheet.store_id which is already defined. The beginner in me thinks the correct code for the 'update' method would be
#callsheet.update(callsheet_params).where(#callsheet.store_id => #store.id)
but I'm not sure how to access #store in this instance, and this likely just creates a new record anyways.
Any help or points in the right direction are appreciated. Thanks!
I've been trying to get it running in the 'update' method, but would also like to get it running in the 'create' method if that's any different. Relevant info:
callsheet.rb:
class Callsheet < ActiveRecord::Base
belongs_to :store
accepts_nested_attributes_for :store
callsheets_controller.rb:
class CallsheetsController < ApplicationController
def update
#callsheet = Callsheet.find(params[:id])
if #callsheet.update(callsheet_params)
redirect_to callsheet_dashboard_path
else
render action: :edit
end
end
def callsheet_params
params.require(:callsheet).permit(:id, :user_id, :store_id, . . . , store_attributes: [:id, :lastvisit])
edit.html.erb:
<%= form_for #callsheet, url: callsheet_path, remote: true, :html => { :multipart => true} do |f| %>
<%= f.fields_for :store do |s| %>
<%= s.text_field :lastvisit, :id => 'lastvisitHidden' %>
<% end %>
<%= f.hidden_field :store_id, :id => 'storeSelectHidden' %>
//
If you have a store_id column on callsheet that's called a "foreign key", and you can write these associations on the models:
#callsheet
belongs_to :store
#store
has_many :callsheets
Then in your controller action, after #callsheet.update(callsheet_params):
#callsheet.store.update(last_visit: Time.now)
This is simplest, and is what I'd recommend. #callsheet.store returns a store instance (you always need to keep track of whether your variables are model instances, model queries, or arrays.)
You could alternatively say this, it's just showing another way to access the store:
Store.find_by(id: #callsheet.store_id).update(last_visit: Time.now)
find_by is known as a "query method" (see ActiveRecord query interface), but unlike most of the others it returns an instance and not a query. update is only callable on instances, and returns a boolean indicating whether the save was succesful. update also triggers the callback chain - see ActiveRecord Callbacks.
Another way to do it:
Store.where(id: #callsheet.store_id).update_all(last_visit: Time.now)
This uses the .where query method, which returns a query (in this case you know it will be one element), and then the update all query method (which does not trigger the callback chain).
The last is kind of confusing and I'd dissuade you from using it. The reason being that a callsheet has only a single store, so loading it as an array is misleading.

rails3-jquery-autocomplete passing parameter to scope query

I'm struggling to find an answer to a problem I have with the rails3-jquery-autocomplete gem.
I have 2 models. Environment & Property.
class Environment < ActiveRecord::Base
has_many :properties
name: string, envfile: string
class Property < ActiveRecord::Base
scope :with_environment_id, where(:environment_id => 14)
belongs_to :environment
name: string, value: string, environment_id: integer
I have a view which displays all the properties belonging to a particular environment and in that view the following code which auto searches based on the property name
<%= form_tag('tbd')%>
<%= autocomplete_field_tag :property_name, '', autocomplete_property_name_properties_path>
The properties controller
class PropertiesController < ApplicationController
autocomplete :property, :name, :scopes => [:with_environment_id]
and routes.rb has
get :autocomplete_property_name, :on => :collection
The autocomplete works fine but it returns returns all records in properties table. Where I would like only the properties belonging to the environment displayed in the view.
As you can see I've defined a scope in the property model in which I've hardcoded a valid environment_id to make this work as I would want it.
So my question is how do I pass the environment_id from the view/controller back into the scope?
Perhaps I'm tackling this from the wrong angle.
Apologies if it's a dumb question but it has stumped me all day.
def get_autocomplete_items(parameters)
super(parameters).where(:user_id => current_user.id)
end

Rails 3 - polymorphic_path - How to Create One given a table

i have table AuditLog with fields including: audited_id | audited_type
That results in data like:
108 | Photo
303 | Comment
What I want to do is create a link to the item, so for the example above:
here is the photo
I'm trying to use a polymorphic_path but am getting an error: "undefined method `model_name' for Fixnum:Class"
When using:
<%= link_to 'Here she is', polymorphic_path([audited_id, audited_type]) %>
Ideas? Thanks
Updating with code based on the answer by Luke below:
class NewsFeed < ActiveRecord::Base
default_scope :order => 'news_feeds.created_at DESC'
belongs_to :feeded, :polymorphic => true
end
class Note < ActiveRecord::Base
has_many :newsfeed, :as => :feeded
end
In the partial which is being passed the local storyitem:
<%= link_to 'Here she is', polymorphic_path(storyitem.feeded) %>
The DB migration file, contains the following line for CreateNewsFeeds
t.references :feeded, :polymorphic => true
You should have a method #auditable (or whatever your polymorphic association is called) on AuditLog objects. If you pass the result of that method to polymorphic_path it will return the correct path for you.
Update:
Assuming you have the following associations (or are using acts_as_auditable or something that sets up the relationships for you):
class AuditLog
belongs_to :auditable, :polymorphic => true
end
class AuditedObject
has_many :audits, :as => :auditable
end
You'll be able to call auditable on any instance of AuditLog, and it will return the associated audited object. So you can call
<%= link_to 'Here she is', polymorphic_path(audit_log.auditable) %>
to get a link to the audited object.
So, anywhere that you have a polymorphic association in a class, there is an instance method setup with the name of that association that will return the associated object.
Gosh, I'm hoping that makes sense. Let me know if you need me to clarify it further.
The problem with polymorphic_path it needs an object, so you first need to fetch the object from the database.
Depending on your use case this can be a big performance problem.
In case of a log viewer, where you have a list of for example 100 entries,
and just want to show links to the entires, you will fetch 100 objects, just to get their path.
I had a similar problem, my solution was to hack a method to construct the path from the class name and id:
class AuditLog
...
def audited_path
"/#{self.audited_type.tableize}/#{self.audited_id}"
end
The method will return for example "/photos/302". But of course it will work only for quite simple routings.

Elegant way to process collection_select in controller?

I am currently somewhat stuck figuring out an elegant solution to my following
problem:
Let's say I have the following classes:
class Event < ActiveRecord::Base
belongs_to :reg_template, :class_name => "EmailTemplate"
[...]
end
class EmailTemplate < ActiveRecord::Base
has_many :events
[...]
end
And a view that contains:
<%= f.collection_select(:reg_template_id, EmailTemplate.all, :id, :name) %>
What is the recommended way of processing this form field in an action
controller?
Having a 1:1 relationship between Event and EmailTemplate means that Rails
does not generate a reg_template_id and reg_template_id= method (as it would
do for a 1:n relationship), so attempts to read or assign this field will fail
with
unknown attribute: reg_template_id
when attempting to call
Event.update_attributes
Using
<%= f.collection_select(:reg_template, EmailTemplate.all, :id, :name) %>
instead also does not help much as it will fail with:
EmailTemplate(#70070455907700) expected, got String(#70070510199800)
I guess I must be missing something terribly obvious as I think is is rather
common to update a model instance with a reference to another object through a
collection_select.
If you have the column reg_template_id in the events table, then the following code should work:
<%= f.collection_select(:reg_template_id, EmailTemplate.all, :id, :name) %>

Resources