How to get all associations for a model - ruby-on-rails-4

Is there any way in rails to get a list of all associated models for a given model?
I'm thinking in something like:
post_associations = Post.associations()
Thanks.

Post.reflections
#=> { :author => #<ActiveRecord::Reflection::AssociationReflection:0x0...>,
:comments => #<ActiveRecord::Reflection::AssociationReflection:0x0...> }
Post.reflections.keys
#=> [:author, :comments]

Related

Rails how to access a hash in model from console

Using Rails 4. I have a hash in a model and I'd like to be able to access it via the rails console for testing some code. How do I do this?
foo = {'one' => 'ONE', 'two' => 'TWO'}
I've tried the following, but it gives me an "undefined method 'foo' in class..."
ModelName.foo['one']
Thanks for helping.
Are you trying to use foo as a constant? If so, you can set it and then reference it as:
class ModelName
FOO = {'one' => 'ONE', 'two' => 'TWO'}
end
ModelName::FOO['one']
An alternative to keep the reference as you have it:
class ModelName
def self.foo
{'one' => 'ONE', 'two' => 'TWO'}
end
end

Rails 4 - Thinking Sphinx filter using field on related model

In my Rails 4 app I have models like:
'AgencyGroup'
has_many :group_agencies
has_many :agencies, :through => :group_agencies
Where I keep 'token' field
'Agency' model
has_many :group_agencies
has_many :agency_groups, :through => :group_agencies
has_many :advertisements
'Advertisement' model
belongs_to :agency
I use Thinking Sphinx and it works really greate but now I got new requirement to filter 'Advertisements' by AgencyGroup token fields.
Basically I need to find advertisment with some parameters but only for agencies that are in agency group with posted token.
if params[:search]
#results = Advertisement.search Riddle::Query.escape(params[:search]), :star => true, :page => params[:page], :per_page => 6
end
To get results I run http query like this:
http://api.localhost.local:3000/v1/advertisements?token=JHW_tdXn5g-vQY1f_ZzLuw&search=Nissim
What I'm missing? How to use relation between models in TS?
I think the best approach here involves a few steps:
Step 1: Add AgencyGroup IDs as an attribute in your Advertisement index. If you're using SQL-backed indices (:with => :active_record), it's a one-liner:
has agency.group_agencies.agency_group.id, :as => :agency_group_ids
If you're using real-time indices, then you'll want a method in Advertisement that returns all of those IDs:
def agency_group_ids
agency.agency_group_ids
end
And your attribute definition will look like this:
has agency_group_ids, :type => :integer, :multi => true
Step 2: Because you've changed your index structure, don't forget to rebuild your indices:
# for SQL-backed indices:
rake ts:rebuild
# or, for real-time indices
rake ts:regenerate
Step 3: In your controller, find the agency group for the given token:
agency_group = AgencyGroup.find_by :token => params[:token]
Step 4: Finally, use that agency group's id in your search call:
#results = Advertisement.search Riddle::Query.escape(params[:search]),
:star => true,
:page => params[:page],
:per_page => 6,
:with => {:agency_group_ids => agency_group.id}

Rails create multiple interrelated objects in one form

I have a Vehicles model where each vehicle has a make and model. When a user creates a vehicle, they can either select from currently available makes and models, or they can create a new make and model. Both make and model contain no extra data, so they are stored in the CommonLookup model I use for dynamic enumerations.
I want to limit model choices through ajax based on the currently selected make. To do this, I've created a blongs_to relationship on the CommonLookup model to itself; in other words, any record of that type can optionally reference a parent record of the same type in a many-to-one relationship.
The problem I'm running into is actually saving the relationship. My model code, which works for creating non-related make and model records, is as follows:
class Vehicle < ActiveRecord::Base
belongs_to :make, :class_name => "CommonLookup", :foreign_key => "make_id"
belongs_to :model, :class_name => "CommonLookup", :foreign_key => "model_id"
attr_accessor :new_make_name
attr_accessor :new_model_name
before_save :create_make_from_name, :create_model_from_name
def create_make_from_name
create_make(
:value => new_make_name
)
end
def create_model_from_name
create_model(
:value => new_model_name
)
end
end
This code successfully creates a vehicle with the associated new make and model, but the new make and model are not associated with each other as I need them to be. I need a many-models to one-make relationship that I can use to easily limit choices. To be clear, this question has nothing to do with the ajax part that is necessary for limiting choices; I'm focusing on the creation of the model instances themselves so that they are related, all from a single form.
I've attempted to set up code in the create_model_from_name callback but to no avail; there is no accessible reference to the object created in the first callback that could be used to set up the relationship. What I tried:
def create_model_from_name
create_model(
:value => new_model_name,
:parent => :make
)
end
But this didn't work. Any help would be greatly appreciated.
I solved this by combining the before_save callbacks and including a little more logic:
class Vehicle < ActiveRecord::Base
belongs_to :make, :class_name => "CommonLookup", :foreign_key => "make_id"
belongs_to :model, :class_name => "CommonLookup", :foreign_key => "model_id"
attr_accessor :new_make_name
attr_accessor :new_model_name
before_save :create_make_and_model_from_names
def create_make_and_model_from_names
if not new_model_name.blank?
if not new_make_name.blank?
create_model(
:value => new_model_name,
:parent => create_parent(
:value => new_make_name
)
)
else
create_model(
:value => new_model_name,
:parent => model
)
end
end
end
end
This met my requirements by setting up a way of creating new makes and creating and associating new models with both new makes and existing makes. The end result is that my ajax request is much easier to set up because I can easily locate the models associated with each make and place these in the options list.

rails 4 nested attributes won't create has_many model

I'm new to rails and have spent way too many hours on this. Thanks a lot, in advance, for any help!
I can't seem to get fields_for and/or accepts_nested_attributes_for to work for my nested attributes.
I have a smash_client that has_many contracts and a form that tries to create a smash_client with a parameter and at the same time it tries to also set a parameter on the contract object. The contract belongs_to the smash_client.
I've tried a lot of different solutions and have read the docs but I'm still missing something. I get this in my params hash, in the smash_clients_controller.rb
..., "smash_client"=>{"name"=>"fasdf", "user"=>"adam"}, "smash_client_id"=>{"instance_type"=>"spot"},...
from
= form_for #smash_client do |f|
.field
= f.label :name
= f.text_field :name
.field
= fields_for :smash_client_id do |c|
%p
= c.radio_button :instance_type, 'spot'
= c.label :instance_type, 'spot'
= c.radio_button :instance_type, 'on_demand'
= c.label :instance_type, 'on demand'
.actions
= f.submit 'Save'
and
class SmashClient < ActiveRecord::Base
has_many :contracts, dependent: :destroy
accepts_nested_attributes_for :contracts, allow_destroy: true,
reject_if: proc { |attributes| attributes[:instance_type].blank? }
...
def new
#smash_client = SmashClient.new
3.times { #smash_client.contracts.build }
end
...
def smash_client_params
#smash_client_params = params.require(:smash_client).
permit( :user, :name, contracts_attributes: [:instance_type] )
end
end
and
class Contract < ActiveRecord::Base
belongs_to :smash_client
after_create :determine_instance_type_and_start
before_destroy :stop_instances
...
end
I think the nested params would work if I hard coded it because if I try something like this, in the console, I don't get errors and I get a new SmashClient and Contract.
smash_client_params = {name: 'something', user: 'blah', contracts_attributes: [{instance_type: 'spot'}]}
SmashClient.create( smash_client_params )
I tried using :contracts, #smash_client.contracts and a few other things in the fields_for section. Also tried using select and collection_select but I can't seem to nail down the form.
sorry for the long post. Hopefully I got all the useful info with nothing extra in the question.
I'd really appreciate some direction or answers.
Thanks in advance.
I finally found it. The :instance_type had to be whitelisted in the Contract model. Thanks again, kalyani. I appreciate the help. Here's the changes to the code above:
.field
= fields_for :contracts do |c|
= c.label :instance_type, 'spot instance'
= c.radio_button :instance_type, 'spot', checked: true
= c.label :instance_type, 'on demand instance'
= c.radio_button :instance_type, 'on_demand'
and
def contract_params
params.require(:contract).
permit(:id, :name, :instance_id, :smash_client_id, :instance_type)
end
Instead of : fields_for :smash_client_id do |c|
write it as: fields_for :contracts do |c|
Refer:
1. http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for
http://railscasts.com/episodes/196-nested-model-form-part-1
Rails 4 Nested Attributes Unpermitted Parameters ---- refer this for writing the code in controller and view the correct way

Why am I getting "Can't mass-assign protected attributes" after adding a field to a product on spree?

I'm trying to add a field to products on spree which is just a checkbox which is just ment to mark products if they are for sale or they're internal products.
I've added the migration and finally figured out how to add the checkbox on the form, but when I click Update I get Can't mass-assign protected attributes: for_sale
This is the migration
class AddProductForSaleField < ActiveRecord::Migration
def up
add_column :spree_products, :for_sale, :boolean
end
def down
remove_column :spree_products, :for_sale
end
end
Here's the field being added
Deface::Override.new(:virtual_path => "spree/admin/products/_form",
:name => "for_sale",
:insert_before => "code[erb-silent]:contains('track_inventory_levels')",
:partial => "spree/admin/products/for_sale")
And this is the partial
<%= f.field_container :for_sale do %>
<%= f.label :for_sale, t(:for_sale) %>
<%= f.check_box :for_sale, { :checked => true } %>
<% end %>
got it, was missing the model part
Spree::Product.class_eval do
attr_accessible :for_sale
end
Mass Assignment is the name Rails gives to the act of constructing your object with a parameters hash. It is "mass assignment" in that you are assigning multiple values to attributes via a single assignment operator.
The following snippets perform mass assignment of the name and topic attribute of the Post model:
Post.new(:name => "John", :topic => "Something")
Post.create(:name => "John", :topic => "Something")
Post.update_attributes(:name => "John", :topic => "Something")
In order for this to work, your model must allow mass assignments for each attribute in the hash you're passing in.
There are two situations in which this will fail:
You have an attr_accessible declaration which does not include :name
You have an attr_protected which does include :name
It recently became the default that attributes had to be manually white-listed via a attr_accessible in order for mass assignment to succeed. Prior to this, the default was for attributes to be assignable unless they were explicitly black-listed attr_protected or any other attribute was white-listed with attr_acessible.
If it's a permission issue then you can add :
Spree::Product.class_eval do
attr_accessible :variable_1, :variable_2 :as => [:default, :product]
end
Marking it as default for a specific model, will remove the mass-assignment warning message !