How to obtain an array of subclasses in Rails - ruby

I have a model object which subclasses ActiveRecord. Additionally, using STI, I have defined subclasses of this object, which define different types and behaviors. The structure looks something like this:
class AppModule < ActiveRecord::Base
belongs_to :app
end
class AppModuleList < AppModule
end
class AppModuleSearch < AppModule
end
class AppModuleThumbs < AppModule
end
Now, in a view where the user has the option to create new AppModules, I would like them to select from a dropdown menu. However I have not been able to get a list of subclasses of AppModule using the subclasses() method:
<% form_for(#app_module) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :type %><br />
<%= f.select(:type, options_from_collection_for_select(#app_module.subclasses().map{ |c| c.to_s }.sort)) %>
</p>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
I get:
NoMethodError: undefined method `subclasses' for #<AppModule:0x1036b76d8>
I'd appreciate any help. Thanks a lot!

It looks as though subclasses and the like is a recent addition (the method exists on various classes at various points in time, but kept getting shuffled around and removed; that link seems to be the earliest point that the method stuck around). If upgrading to the most recent version of RoR isn't an option, you can write your own subclasses and populate it using Class#inherited (which is what RoR's descendents_tracker does).

AppModule.descendants.map &:name is what you're looking for. As in:
<% form_for(#app_module) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :type %><br />
<%= f.select(:type, options_from_collection_for_select(AppModule.descendants.map(&:name).sort)) %>
</p>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>

Related

l18n::InvalidLocaleData Rails

Error occurs when you click on a url http://localhost:3000/articles/new/.
Showing c:/Sites/blog/app/views/articles/new.html.erb where line #8 raised:
can not load translations from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/activesupport-4.1.8/lib/active_support/locale/en.yml: expects it to return a hash, but does not
File blog.html.erp
<h1>New article</h1>
<%= form_for :article do |f| %>
<p>
<%= f.label :title%><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text%><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
Controller articles action new.
class ArticlesController < ApplicationController
def new
end
end
File config/routes.rb
Rails.application.routes.draw do
resource :articles
get 'welcome/index'
Rails 4.1.8
Ruby 2.1.5
Winddows 7
Delete all of the files C:\RailsInstaller\Ruby2.1.0\lib\ruby\gems\2.1.0\gems\activesupport-4.1.8\lib\active_support\locale\en.yml put - en:

RoR: How can I get my microposts to show up?

Here is the users show view where they are supposed to show up. ..
<section>
<div id= "purchases">
<%= render 'shared/micropost_form_purchase' %>
</div>
<div id="sales">
<%= render 'shared/micropost_form_sale' %>
</div>
</section>
<%= #sales %> <%# This is just to see if it outputs anything. It doesn't :( %>
<div id="purchases list">
<ol class="microposts">
<%= render #purchases unless #purchases.nil? %>
</ol>
</div>
<div id="sales list">
<ol class="microposts">
<%= render #sales unless #sales.nil? %>
</ol>
</div>
so the forms (partials) are loading fine, but then when I make a post, in either one, neither the purchases list nor the sales list shows up. I checked the database and they are being created along with an entry in the column indicating kind (either sale or purchase).
Here are the forms:
<%= form_for (#micropost) do |f| %>
<div class="field no-indent">
<%= f.text_area :content, placeholder: "What's something else you want to buy?" %>
<%= hidden_field_tag 'micropost[kind]', "purchase" %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
and
<%= form_for (#micropost) do |f| %>
<div class="field no-indent">
<%= f.text_area :content, placeholder: "What's something else you want to buy?" %>
<%= hidden_field_tag 'micropost[kind]', "sale" %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
also, here is the show part of the users_controller.rb
def show
#user = User.find(params[:id])
#micropost=Micropost.new
#microposts = #user.microposts.paginate(page: params[:page])
end
and here is the show part of the microposts_controller.rb
def show
#micropost = Micropost.find(params[:id])
#microposts = Micropost.where(:user_id => #user.id)
#purchases= #microposts.collect{ |m| m if m.kind == "purchase"}.compact
#sales = #microposts.collect{ |m| m if m.kind == "sale"}.compact
end
additionally, with the help of this post (http://stackoverflow.com/questions/12505845/ruby-error-wrong-number-of-arguments-0-for-1#12505865) the variables #microposts, #purchases, and #sales are all outputting correctly in the console.
can anyone help me out?
edit: using scopes as suggested by the answer given works in the console (it outputs everything correctly, but they still don't show up in the view. Does this mean it is something wrong with my syntax for the users show page?
edit 2:
Here is the view/microposts/_micropost.html.erb code
<li>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
confirm: "You sure?",
title: micropost.content %>
<% end %>
</li>
I'm making some assumptions without seeing more of your code, but it looks like you could
write what you've shown a little differently. I'm assuming your databases are migrating
and have the required columns, e.g., Micropost#kind, Micropost#user_id, etc.
You can use scopes to refine a collection of microposts more expressively. It might be helpful to read
up about ActiveRecord scopes: http://guides.rubyonrails.org/active_record_querying.html#scopes.
class Micropost < ActiveRecord::Base
belongs_to :user
scope :purchases, where(:kind => "purchase")
scope :sales, where(:kind => "sale")
# your code
end
I'm also assuming your user has many microposts:
class User < ActiveRecord::Base
has_many :microposts
# your code
end
For your forms, I'd suggest attaching your hidden field to the form object (f.hidden_field) so
you don't have to specify the name as 'micropost[kind]'.
<%= form_for(#micropost) do |f| %>
<div class="field no-indent">
<%= f.text_area :content, placeholder: "What's something else you want to buy?" %>
<%= f.hidden_field :kind, :value => "sale" %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
In MicropostsController#show, you can use your new scopes:
def show
#micropost = Micropost.find(params[:id])
#microposts = #user.microposts
#purchases = #microposts.purchases
#sales = #microposts.sales
end
You should also confirm that your MicropostsController#create action is actually adding
the microposts to the user sending the form (I'm assuming a current user method).
def create
#micropost = current_user.microposts.create(params[:micropost])
# yada
end
You can also confirm expected results on rails console after creating purchases or sales micropost with:
Micropost.purchases
Micropost.sales
Again, I could be missing something without seeing more of the code base.
Check Micropost.count, #purchases.count, #sales.count (by printing them in the controller, or some part of the view) to see if the records actually exist.
Also, if you want to render collections likes #sales and #purchases, you need to make sure that the model partial exists (_micropost.html.erb in your case). That is probably where you need to look for the view errors. For all you know, that file could be empty, thus no errors will show up at all.
The problem might also lie in your microposts#create (or whichever action that you are saving the micropost in), the micropost should be associated with the current_user:
#micropost = current_user.microposts.build(params[:micropost])
Taking this and your previous question into account, I suggest you go through the original code for the RoR tutorial again (and verify that all tests are passing) before taking it apart. You can always add new tests to it for your experiments and they will help in figuring out where you went wrong.

Bizarre behaviour in a nested form - need <%= not <%

I'm using rails 3.1.3
I was trying to make a nested form - I made it work in the end, so I've got all the accepts_nested_attributes_for bits right.
This is the form that worked:
<%= form_for(#article, :as => :article) do |f| %>
......some article fields in here
<%= f.fields_for :article_site_permissions do |builder| %>
<%= builder.label :name %><br />
<%= builder.text_field :name %>
<% end %>
<div class="actions">
<%= f.submit 'Update' %>
</div>
<% end %>
The weird bit is that the line <%= f.fields_for :article_site_permissions do |builder| %> needed <%= not <%. All the examples I've seen (eg http://railscasts.com/episodes/196-nested-model-form-part-1?view=asciicast) can use <% not <%=. Coincidently, later that day the same thing happened on a colleagues project, which prompted this question.
Does anyone know what's going on here?
From rails 3.0 onwards <%= is the right thing to do (see the release notes). The railscast you link to predates rails 3.0.

Getting undefined method `klass' for nil:nilclass - Railscast #197

Hi all I followed Ryan Bates' railscasts on nested models and form, but I am getting getting undefined method `klass' for nil:nilclass. I am pretty sure it is due to the the link_to_add_fields since everything was working prior. Below is my error and other relevant code and I'm using Rails 3.1. I did a lot of googling and did not find any to solve my problem, so if you guys could help me out I would really appreciated it. Thanks for your help.
_form.html.erb
<%= form_for(#organization) do |f| %>
<% if #organization.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#organization.errors.count, "error") %> prohibited this organization from being saved:</h2>
<ul>
<% #organization.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div id="restaurant_field" class="field">
<%= f.fields_for :restaurants do |builder| %>
<%= render 'organizations/partials/restaurant_fields', :f => builder %>
<% end %>
</div>
<div class="actions"><%= f.submit %></div>
<% end %>
_restaurant_fields.html.erb
<p class="fields">
<%= f.label :name, "Restaurant Name" %><br />
<%= f.text_field :name %>
<%= link_to_remove_fields "Remove", f %>
application_helper.rb
module ApplicationHelper
def link_to_remove_fields(name, f)
f.hidden_field(:_destroy) + link_to_function(name, "remove_fields(this)")
end
def link_to_add_fields(name, f, association)
new_object = f.object.class.reflect_on_association(association).klass.new
fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder|
render(association.to_s.singularize + "_fields", :f => builder)
end
link_to_function(name, h("add_fields(this, \"#{association}\", \"#{escape_javascript(fields)}\")"))
end
end
application.js
function remove_fields(link) {
$(link).prev("input[type=hidden]").val("1");
$(link).closest(".fields").hide();
}
function add_fields(link, association, content) {
var new_id = new Date().getTime();
var regexp = new RegExp("new_" + association, "g");
$(link).parent().before(content.replace(regexp, new_id));
}
I found that the link_to_add_fields helper won't work if the associated model is describe by a has_one. The has_one means the association does not get the klass object.
You can determine this is your problem by changing your relationship to has_many :object + s (your object name with an s) and passing your object in plural to link_to_add_fields.
You have done the same thing as this person here
Edit: (error on my part)
I am assuming you have an link_to_add_fields below this line
<%= link_to_remove_fields "Remove", f %>
as it seems that the _restaurant_fields.html.erb partial is incomplete. (no closing tag)
</p>
Remove the link_to_add_fields outside of the f.fields_for
That should solve the klass error.

Rails 3 Nested Form not being created

The models I'm working with look like this:
class ComplexAssertion < ActiveRecord::Base
has_many :expression_groups
has_many :expressions, :through => :expression_group
accepts_nested_attributes_for :expression_groups, :allow_destroy=>true
end
class ExpressionGroup < ActiveRecord::Base
belongs_to :complex_assertion
has_many :expressions
accepts_nested_attributes_for :expressions, :allow_destroy=>true
end
class Expression < ActiveRecord::Base
belongs_to :expression_group
end
My form looks like the following:
<%= form_for(#complex_assertion) do |f| %>
<div id="mainAssertionGroup" style="border:1px; border-style:solid; width:1000px; padding:5px">
<div class="field">
<%= f.label :title %>: <%= f.text_field :title, :size=>'10' %>
<%= f.label :description %>: <%= f.text_field :description, :size=>'25' %>
<%= f.label :scope %>: <%= f.text_field :scope, :size=>'1' %>
Test
Category: <%= collection_select(:complex_assertion, :assertion_category_id, AssertionCategory.all, :id, :name, {:include_blank=>"UNCATEGORIZED"}) %>
</div>
<div id="initialGroup" style="border:1px; margin-left:10px; margin-top:10px; border-style:solid; width:850px;">
<div class="childGroup1" style="padding:5px;">
<%= f.fields_for :expression_groups do |eg| %>
<%= eg.fields_for :expressions do |e| %>
Type: <%= e.collection_select :assertion_type_id, AssertionType.all, :id, :name %>
Attribute: <%= e.collection_select :attribute_name, Attribute.find_by_sql("select distinct a.name from attributes a "), :name, :name %>
<%= e.label :operator_type_id %>
: <%= e.collection_select :operator_type_id, OperatorType.all, :id, :value %>
Value: <%= e.text_field :value, :size=>'1' %>
<% end %>
<div id="innerOperator">
<%= eg.collection_select :logical_operator_type_id, LogicalOperatorType.all, :id, :value %>
</div>
<% end %>
</div>
</div>
</div>
<div id="createComplex" align="center">
<%= f.submit :value=>'Submit' %>
</div>
<% end %>
And my controller looks like:
def new_complex_assertion
#complex_assertion = ComplexAssertion.new
end
When I load the page, I only the ComplexAssertion portion of the form and get nothing back for the ExpressionGroups or the Expressions. It's as if there isn't anything available. But, if you see my controller, I did a ComplexAssertion.new which I though would create the dependent objects automagically; I assume I'm incorrect?
I'm debugging through RubyMine and when I evaluate the ComplexAssertion.new, I only see 5 attributes, the five that are defined for only that object, none of the relational objects. What am I doing incorrectly?
EDIT
Looks like if I do the following:
#complex_assertion = ComplexAssertion.new
#complex_assertion.expression_groups.build
#complex_assertion.expressions.build
And change my form to use:
<%= f.fields_for :expressions do |e| %>
instead of eg.fields_for, it shows the forms.
This DOES NOT give me the correct nesting. I thought I should be able to do:
#complex_assertion.expression_groups.expressions.build
but it tells me that expressions is an undefined method.
Yes, you have to explicitly instantiate the associated objects. It is not done for you.
#complex_assertion.expression_groups.expressions.build
Will not work because expression_groups is an array and not an individual expression group. So, after you create the expressions_groups do the following:
#complex_assertion.expressions_groups.each do |group|
group.expressions.build
end
Also, you could replace the 2nd line with the following as well to create multiple expressions
2.times do { group.expressions.build }
As for using fields_for with nested models, make your code in the form look like this:
<%= f.fields_for :expression_groups, #complex_assertions.expression groups do |eg| %>
<%= eg.fields_for :expressions, eg.object.expressions do |e| %>
I will try to explain what is going on. The :expressions_groups is telling fields_for what class of object it is going to render fields for, and the second part I added is telling fields_for where to find the object(s) to render fields for. If we are passing in an array, which we are in this case, it will automatically iterate over the array. On each iteration, it puts the current model object we are working with into a variable called object which is stored in the form builder instance returned by fields_for. So we use this to tell the second fields_for where to find the expression model objects it needed. This means eg.object points to an expression_group model object.
I hope this helps and makes sense. Also, I have not tested anything and am only pointing out what looks out of place.

Resources