Rails: Retrieving polymorphic data from nested model - ruby-on-rails-3

I'm trying to retrieve data for all items from a box, a box can have compartments and I'd like to get all compartment info at the box level. items are made polymorphic as boxes won't necessarily have compartments.
MODEL
class Box < ActiveRecord::Base
has_many :compartments
has_many :items, :as => :itemable
end
In my Controller I can get results back with:
#box = Box.find(params[:id])
#itemable = #box.compartments.first
#itemable = #box.compartments.last
VIEW
<% #items.each do |item| %>
<%= item.name %>
<% end %>
but if I then try
#itemable = #box.compartments
OR
#itemable = #box.compartments.find(:all)
I get the error
undefined method `items' for #<ActiveRecord::Array>
OR
undefined method `items' for #<ActiveRecord::Relation>
Can anyone help with getting results back from all compartments?

So in compartments you have
belongs_to :box
has_many :items, :as => :itemable
Is this the case? #box.compartments should return an array of compartments right? It sounds like items is somehow getting called on #box.compartments somehow

Related

Why isn't my search method working in Ruby on Rails?

In my Ruby on Rails application, I have a cinema system and am trying to return the screen a showing is in when a user searches for the showing.
To display the search drop down I am using this code in my _application.html.erb:
<%= render( :partial => '/screen_lookup', :locals => {:showings => #showings = Showing.all, :my_path => '/screens/display_screens_by_showing' })%>
Which renders the search from the _screen_lookup.html.erb:
<%= form_tag my_path, :method=>'post', :multipart => true do %>
<%= select_tag ('showings_id'),
options_from_collection_for_select(#showings, :id, :showing_times, 0 ),
:prompt => "Showings" %>
<%= submit_tag 'Search' %>
<% end %>
And uses the display_screens_by_showing in the screens_controller:
def display_screens_by_showing
#screens = Screen.showing_search(params[:showing_id])
if #screens.empty?
# assign a warning message to the flash hash to be displayed in
# the div "feedback-top"
flash.now[:alert] = "There are no films of that genre."
# return all products, in alphabetical order
#screens = Screen.all
end
render :action => "index"
end
And this searches using the method in the screen.rb model:
def self.showing_search(showing_id)
screen = Showing.where("id = ?", showing_id).screen_id
self.where("id = ?", screen)
end
Now, the problem I am having is that because a showing belongs_to a screen, and a screen has_many showings, I need to be able to search for the showing, and store that showing's screen_id in a variable to search for the screen that showing is in with, which I have tried doing in the model:
screen = Showing.where("id = ?", showing_id).screen_id
self.where("id = ?", screen)
But the error I am getting is:
NoMethodError in ScreensController#display_screens_by_showing
undefined method `screen_id' for #<ActiveRecord::Relation []>
These are the model relationships:
showing.rb:
class Showing < ActiveRecord::Base
belongs_to :screen
end
screen.rb:
class Screen < ActiveRecord::Base
has_many :showings
end
What code will get my search working?
The problem is that where doesn't return a record, it returns a relation that can be enumerated or chained, instead you want to use find or find_by to return a single record, which is kind equivalent to where + first
screen = Showing.find(showing_id).screen_id
which is sort of like doing
screen = Showing.where(id: showing_id).first.screen_id
If you want to pass a hash you can use find_by which will be like this
screen = Showing.find_by(id: showing_id).screen_id
PS:
I'm not sure what you're doing exactly, but i think those two lines can be merged into a single query ( not sure what it should be returning, but I'm assuming a screen )
def self.showing_search(showing_id)
Showing.find(showing_id).screen
end

If a Rails model has_many children, how do I store the id of the first?

I'm new to Rails, so it's possible I'm overlooking something simple. I have a Rails model called a story. Each story has_many segments, and each segment belongs_to a story. I use the same form to create both the story and its first segment by using a fields_for section of the form and setting the story model to accepts_nested_attributes_for :segments. I am currently able to use the form to create both a story and a segment simultaneously.
The problem is that each story also needs to store the id of its first segment, but when I'm saving the story, the segment hasn't yet been saved, so it doesn't yet have an id to store in the story, and I haven't been able to find a handle for the segment after the form is submitted so that I can save the segment first before the story is created. So my question is how do I save a record of the first_segment_id inside the story?
The following code may be relevant:
in app/models/story.rb
class Story < ActiveRecord::Base
attr_accessible :segments_attributes
has_many :segments
accepts_nested_attributes_for :segments
end
in app/models/segment.rb
class Segment < ActiveRecord::Base
attr_accessible :words
belongs_to :story
end
in app/views/stories/_ form.html.erb
<%= form_for(#story) do |f| %>
#...stories fields...
<%= f.fields_for :segments do |segment_form| %>
<div class="field">
<%= segment_form.label :words %><br />
<%= segment_form.text_area :words %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
in app/controllers/stories_ controller.rb
def new
#story = Story.new
#segment = #story.segments.build
# If I try replacing the above with #segment = #story.segments.create
# then I get the error message "You cannot call create unless the
# parent is saved," which is problematic because I need to find some way
# to get the id of #segment to save in the parent story, but the segment
# won't have an id until after it has been saved.
respond_to do |format|
format.html # new.html.erb
format.json { render json: #story }
end
end
def create
#story = Story.new(params[:story])
# #segment.save
# #story.first_segment_id = #segment.id
# If I uncomment the above two lines, I get the error message
# "undefined method `save' for nil:NilClass". It appears that
# #segment hasn't been passed from the "new" method above to
# this method as a handle of the first segment created, so I'm not
# able to save it to get an id for it before saving the story.
# Is there some way to save the segment here?
respond_to do |format|
#...if #story.save...
end
end
The params hash submitted by the form looks something like:
{ "story"=>{ Various_other_story_fields,
"segments_attributes"=>{"0"=>{"words"=>"dfsdsa"}}},
"commit"=>"Create Story"}
Is there a way to save the first segment's id in the story? I think perhaps I need to add a before_create inside my story model instead, but I'm not sure how do to this.
I would approach this differently, adding a numeric sort_order column to Segment so that you're not relying on Segment ids to determine what order they go in. Then you can define something like the following on your Story model rather than explicitly referencing the first segment in the database:
def first_segment
segments.order(:sort_order).first
end
If you're certain you need to store the first segment ID in the story, you can .save the story so that it knows its ID, saves its children, and knows their IDs. Something like:
def create
#story = Story.new(params[:story])
#story.save # Save the story and its child segment so that they both have IDs
#story.first_segment_id = #story.segments.first.id
#story.save
...
end
You should be able to do something like:
class Story < ActiveRecord::Base
attr_accessible :segments_attributes
has_many :segments
has_one :first_segment
accepts_nested_attributes_for :segments
end
def new
#story = Story.new
#segment = #story.segments.build
#story.first_segment = #segment
...
You'll have to add story_id to your segment table.

Using nested attributes to easily select associations in a form

I am trying to create a nested attribute form to create a model which is primarily an association "connector" between two other models. In my case, the models represent books, awards, and the "connector" model book_awards. When I am editing a book, I want to be able to quickly select which awards it has won.
I've been using
http://railscasts.com/episodes/196-nested-model-form-part-1
to help me get started, but I'm afraid I'm pretty much stuck.
Another SO question which seems similar is
accepts_nested_attributes_for with find_or_create? Unfortunately, it's also not quite what I'm doing and I haven't been able to adapt it.
My models look like this. Each model has additional attributes and validations etc, but I've removed them for clarity.
class Book < ActiveRecord::Base
has_many :book_awards
accepts_nested_attributes_for :book_awards, :allow_destroy => true
end
class Award < ActiveRecord::Base
has_many :book_awards
end
class BookAward < ActiveRecord::Base
belongs_to :book, :award
end
In my book controller methods for edit and new, and the failure cases for create and update I have a line #awards = Award.all.
In my view, I would like to see a list of all awards with check boxes next to them. When I submit, I would like to either update, create, or destroy a book_award model. If the check box is selected, I would like to update an existing model or create a new one if it doesn't exist. If the check box isn't selected, then I would like to destroy an existing model or do nothing if the award never existed. I have a partial for book_awards. I'm not sure if the check box selector should be in this partial or not.
I think my check box will be my hook to :_destroy but with its polarity reversed. I think something like this will basically do it:
= f.check_box :_destroy, {}, 0, 1
Currently, I have this in my partial but I'm not sure where it really belongs.
Next comes my view which currently doesn't work, but maybe it will help demonstrate what I'm trying to do. I loop through the awards and use a fields_for to set nested attributes for anything that already exists. It's horribly ugly, but I think it somewhat works. However, I don't really know how to get started with the else case.
= f.label :awards
- #awards.each do |a|
- if f.object.awards && f.object.awards.include?(a)
= f.fields_for :book_awards, f.object.book_award.select{|bas| bas.award == a } do |ba|
= render 'book_awards', :f => ba, :a => a
- else
= fields_for :book_awards do |ba|
= render 'book_awards', :f => ba, :a => a
I would prefer the awards to be listed in the same order each time (my #awards assignment in the controller will probably specify the order) as opposed to listing the existing awards first or last.
I hate to answer my own question, but I finally figured out something which works. The first thing I needed to do was to update the "new" case based on the crazy object which was included in the railscast. Next, I needed to manually set the :child_index. Finally, I needed to manually set the :_destroy check box appropriately.
.field
= f.label :awards
- #awards.each_with_index do |a,i|
- if exists = (f.object.awards && f.object.awards.include?(a))
- new_ba = f.object.book_awards.select{|s| s.award == a}
- else
- new_ba = f.object.class.reflect_on_association(:book_awards).klass.new
= f.fields_for :book_awards, new_ba, :child_index => i do |ba|
= render 'book_awards', :f => ba, :a => a, :existing => exists
My partial looks like this:
.field
= f.check_box :_destroy, {:checked => existing}, 0, 1
= f.label a.name
= f.hidden_field :award_id, :value => a.id
= f.label :year
= f.number_field :year
It's not horribly pretty, but it seems to do exactly what I wanted.

Maintaining Many to Many relation with checkboxes (e.g. checkboxes for each category a post can be in)

We have a table with Categories containing unique categories. And our posts should link to any of these categories. I tried to add checkboxes for selecting the categories in the following manner:
Add the checkboxes containing the categories in a field called "post[categoriesss][]", containing as value the :id of the category.
Update the new and update method to take these these ids and collect the associated Categories with them, and then add each category to the categories attribute through the << operator as stated in the documentation.
We added the code to new and update to be sure not to have to do this in multiple controllers.
However we get the following error:
undefined property or relationship '<<' on Category
What are we doing wrong here? Is there another method to achieve our goal? Our code is below.
class Post
include DataMapper::Resource
property :id, Serial
has n, :categories, :through => Resource
def self.new(hash={})
if hash.key? :categoriesss
hash[:categoriesss].each do |category|
categories << Category.get(category)
end
hash.delete! :categoriesss
end
super hash
end
end
class Category
include DataMapper::Resource
property :id, Serial
property :name, String, :required => true
has n, :rekenservices, :through => Resource
end
<% Category.all.each do |category| %>
<label>
<input type="checkbox" name="post[categoriesss][]" value="<%= category.id %>" />
<%= category.name %>
</label>
<% end %>
UPDATE:
I now changed the names for the checkbox back to categories and updated the new and update methods to this:
def self.new(hash={})
if hash.key? :categories
hash[:categories].map! {|id| Category.get id}
end
super hash
end
def update(hash={})
if hash.key? :categories
hash[:categories].map! {|id| Category.get id}
end
super hash
end
Adding categories the first time works. When updating checking an extra category works great as well. Unchecking a category however yields an error message:
columns category_id, post_id are not unique

Simple Form Association with Text Field

I have a Rails app that is using the simple_form gem. I have two models that are related, trades and stocks. In the form for trades, I want users to be able to enter their stock ticker symbol in a text field. Currently, I'm using the association function which renders a select box. The problem is that I want a text field instead since I have about a thousand stocks to choose from.
Is there a way I can do this (with or without Simple Form)?
the models:
class Trade < ActiveRecord::Base
belongs_to :stock
end
class Stock < ActiveRecord::Base
has_many :trades
end
the form on trades#new
<%= simple_form_for(#trade) do |f| %>
<%= f.association :stock %>
<% end %>
You should be able to just use this syntax:
<%= f.input :stock_id, :label => 'Enter your ticker:' %>
The problem here is that the user will not know what :stock_id is, as it's a reference to one of your many Stock objects.
So you probably want to implement a simple jquery autocomplete interface that returns a list of stocks like so:
[{:ticker => 'AAPL', :name => 'Apple Inc', :id => 1}, {:ticker => 'IBM', :name => 'International Business Machines', :id => 2}, etc ]
You can then display something like this as autocomplete results:
AAPL - Apple Inc
IBM - International Business Machines
and allow the user to select the one they are looking for. Behind the scenes you capture the :id and use that as your associated :stock_id.
You will need to add a stocks_controller action that takes a string and looks up Stocks based on a partial ticker and returns a max-number of stocks like 20.
def search
ticker_query = "%#{params[:ticker]}%"
stocks = Stock.where('ticker LIKE ?', ticker_query).limit(20)
render :json => stocks
end