Single Table Inheritance Routing with Rails 3.1 - ruby-on-rails-3

I am having a bit of trouble with getting routing to work with single table inheritance in my Rails 3.1 application. Right now I have two models that I am dealing with: a lesson and a segment. My application consists of lessons that have multiple different types of segments. These segments are of many different types which is why I am using single table inheritance. I have been able to confirm that my inheritance is working through the Rails Console but when I try to view different pages I run into problems. One of these pages is in the Lessons#show view. I get this error:
NoMethodError in Lessons#show
Showing web/app/views/lessons/show.html.erb where line #21 raised:
undefined method `web_segment_path' for #<#<Class:0x007f8faf1366b8>:0x007f8faf118320>
Here is the chunk of code where I get this error:
<% #segments.each do |segment| %>
<tr>
<td><%= segment.title %></td>
<td><%= segment.segmenttype %></td>
<td><%= segment.type %></td>
<td><%= segment.url %></td>
<td><%= segment.filepath %></td>
<td><%= link_to 'Show', #segment %></td> <!-- This is the line with the error, if I remove this and the following two than everything is displayed properly -->
<td><%= link_to 'Edit', edit_segment_path(segment) %></td>
<td><%= link_to 'Destroy', segment, confirm: 'Are you sure?', method: :delete %></td>
</tr>
<% end %>
I am confused because I never defined a route for web_segment_path, and I want it to route to segment as before, and just display every segment available regardless of types. I have set up the following models:
lesson.rb
class Lesson < ActiveRecord::Base
belongs_to :course
has_many :segments
end
segment.rb
class Segment < ActiveRecord::Base
TYPE = ['FileSegment','WebSegment','MediaSegment','QuizSegment']
belongs_to :lesson
end
web_segment.rb
class WebSegment < Segment
end
I know that I am missing inheritances for other types but right now I am focusing on just getting the WebSegment type working. Does anyone know why Rails would try to find the method 'web_segment_path' when I reference #segment? Like I said, the page will display if I remove the links to "show", "edit", and "delete." Why would this be?
Thank you for any help!

Ok I figured this out myself and it was pretty obvious. I forgot to do some simple routing in my routes.rb file. In my case all I needed to add was one line:
resources :web_segment, :controller => 'segments'

Related

Error using nested_form gem with empty association

In my rails app, I have two models, a ClientPage and a ContentSection, where ClientPage has_many :content_sections. I'm using the nested_form gem to both models to be edited with the same form. This works fine as long as the ClientPage has at least one ContentSection, but if there are no associated ClientSections, the using nested_form's link_to_add method throws the following NoMethodError:
undefined method `values_at' for nil:NilClass
The form is structured as follows:
<%= nested_form_for page, form_options do |f| %>
# ClientPage fields
# ClientSections
<%= f.link_to_add "Add new section", :content_sections %>
<% end %>
As long as there is at least one ClientSection associated with the page, this works fine. As soon as there isn't, the error is thrown. Removing the link_to_add also stops the error from being thrown. (There's actually a second nested model under ContentSection, and the same issue arises if there are no associated models.)
Not sure what I'm fairly obvious thing I'm missing, but any pointers or suggestions would be greatly appreciated.
Finally worked this out -- the error was due to the fact that I was using the gem in a slightly non-standard way. Within the form, instead of rendering all of the content sections the standard way:
<%= f.fields_for :content_sections do |section_form| %>
# section fields
<% end %>
I put it inside a loop, as I needed the index of each item (which is not stored within the model itself):
<% page.content_sections.each_with_index do |section, index| %>
<%= f.fields_for :content_sections, section do |section_form| %>
# section fields
<% end %>
<% end %>
The issue doing it this way is that the fields_for method does not get called if the association is empty, and as such the gem cannot build the blueprint for the object (which is used to add in the extra item when link_to_add is called).
The solution was to make sure fields_for got called even if the association was empty:
<% if page.content_sections.empty? %>
<%= f.fields_for :content_sections do |section_form| %>
# section fields
<% end %>
<% end %>

Rails cache not updating properly

I am using the cache-digests gem and following the instructions as per the Railscast, it creates and reads from a cache as you would expect, but the cache does not seem to be updating properly in relation to an associated record.
When moving a listing from one category to another, the category.live_entries count stays the same for the category I move it from, but goes up for the one I move it to.
So it sounds like I need a touch: all type method so it touches the one I am moving it from as well as the one it is moving to?
_category.html.erb
<% cache category do %>
<li>
<%= link_to category.name, category %>
<% if category.live_entries > 0 %>
(<%= category.live_entries %>)
<% end %>
- <%= category.desc %>
</li>
<% end %>
category.rb
class Category < ActiveRecord::Base
has_many :listings
def live_entries
listings.where(verified: true).count
end
end
listing.rb
class Listing < ActiveRecord::Base
belongs_to :category, touch: true
Any ideas on how to tackle this?
Guess I could create a before_update callback to touch the old category - but is there a better way?
Ok just adding this as an answer - but if anyone has a better solution please feel free to share.
I just added an after_update to touch the old category:
def touch_old_category(listing)
cat = listing.category_id_was
Category.find(cat).touch if cat
end

Ruby on Rails - image_tag links not working in posts

trying to make link to my recent uploaded image
<%= link_to (image_tag (post.image_url(:thumb))), post.image.url(:original), :class => 'postimage' %>
how ever it's not working, at all...
<% #post.image do |image| %>
<%= link_to (image_tag (post.image_url(:thumb))), post.image.url(:original), :class => 'postimage' %>
<% end %>
the fun part is that
<%= #post.image %>
works. but only shows /uploads/post/image/3/eKoh3.jpg
full code here https://gist.github.com/4332533
This line looks wrong to me:
<% #post.image do |image| %>
(btw your gist actually says #post.image.each do |image|, which I'm assuming is what you meant to do above)
If you are mounting an uploader on your Post model's :image attribute, then this makes no sense. A mounted uploader allows you to upload one image, and you can't iterate over it using each.
I'm not sure what you're trying to do. Are you trying to iterate over all the versions? Try post.image.versions.each
Are you trying to upload multiple images? Carrierwave can't help you with that directly. You'll need to create a new model, Image, and mount your uploader there. Your Post will need a line like
has_many :images
And your Image model will need to belong_to :post. You'll also need to figure out how to upload and manage images in that new table.

Using scopes and checkboxes to filter index

Like many questions here on SO, I'm attempting to build a form where checkboxes filter my index view. This would ideally occur with AJAX so I don't reload anything. The problem is that I'm as much of a beginner to Rails and programming as can be so I need help on how to set this up.
The index I want to filter is /users, where the values of the checkboxes are attached to the current_user's profile. For example, consider the following:
<% form_for(current_user.profile) do |f| %>
<tr>
<td class="normal"><%= current_user.profile.current_city %></td>
<td><%= check_box_tag :current_city %></td>
</tr>
<tr>
<td class="normal"><%= current_user.profile.hometown %></td>
<td><%= check_box_tag :hometown %></td>
</tr>
<% end %>
If this worked properly with my model and database, clicking either or both of the checkboxes would render the appropriate profiles in my index partial.
I believe I need to use scopes but don't know to do that with so many profile attributes.
UsersController:
class UsersController < ApplicationController
before_filter :authenticate, :only => [:edit, :update]
def index
#user = User.all
#profile = Profile.all
end
end
User model:
class User < ActiveRecord::Base
attr_accessor :password
has_one :profile, :dependent => :destroy
end
Profile model:
class Profile < ActiveRecord::Base
belongs_to :user
accepts_nested_attributes_for :user
end
Can anyone point me in the right direction here? I've looked at MetaSearch but I want to learn how to do it without a gem for the moment.
So, I think the answer to this question is going to be very similar to the answer on your related question, How to make parts of Profile searchable or not
Basically, you need to tie the check boxes to the setting of either per-column visibility flags, or the creation/destruction of visibility records, depending on how fine-grained your access rights need to be.
That is, checking a check box should set the appropriate visibility flag/record for that user (you can do that via AJAX), and un-checking it will clear/destroy the appropriate visibility flag/record.
IF, on the other hand, the form you're talking is a global, "What partials do I render for my user index page?" kind of form that an admin is editing, then setup a table called whatever you want (partial_renderings?) that contains boolean columns, one for each partial, that get set/cleared when check boxes are checked/un-checked.
Then, the if statement around a partial rendering is something like:
...
<% if PartalRenderings.show_hometown %>
<%= render :partial => "user_hometown", :locals => {:user => #user}
<% end %>
...
The partial_renderings table would probably just have a single record, since it's just a set of flags.
Then the PartialRenderings model would basically be empty, because all you'll be doing is checking boolean values.
Finally, the partial_renderings table could be structured like this:
id | show_hometown | show_phone_number | show_real_name | ...etc...
--------------------------------------------------------------------
1 | true | false | false | ...
If partial rendering is on a PER-USER basis, then the partial_renderings table would need an additional user_id field to tie it to a given user, and therefore allow users to decide which things get rendered, and which don't. In this case you'd have more than a single record - you'd have one per user.

How do I access an instance variable from another controller?

So I have two models & controllers. Projects and Designers.
In my designers index view, I want it to show a list of all the projects that the designer has.
However, when I do a simple query like this:
<% #projects.each do |project| %>
<tr>
<td><%= link_to 'Show', project %></td>
<td><%= link_to 'Edit', edit_project_path(project) %></td>
<td><%= link_to 'Destroy', project, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
From the index.html.erb in the Designers view, it gives me the following error:
NoMethodError in Designers#index
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.each
However, when I run that EXACT code from the index.html.erb file in the projects view, it works perfectly.
So how am I able to access that controller or at least data from the view of another controller? Do I have to add a projects variable (where it queries the db) to my index object in my designers controller?
Thanks.
UPDATED TO USE A SINGLE DESIGNER RECORD
You should use associations. Add this to your models.
class Designer < ActiveRecord::Base
has_many :projects
end
class Project < ActiveRecord::Base
belongs_to :designer
end
Your view should look like this:
<% #designer.projects.each do |project| %>
<% end %>
More info on associations here:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
Figured out one way to do it. All I had to do was add an instance variable in my designers controller:
#projects = Project.all
But...that's not very DRY. Does anyone have a more elegant 'DRY' solution, so if I want to access other variables in other controllers I can do that easily without having to re-create them in the current controller?
Thanks.
In your designers controller, you need to set the #projects instance variable to only those projects belonging to the signed in designer, correct?
You'll need something like this:
def index
#projects = Project.where(:user_id => where ever you've stored your userid)
end
I hope this helps.