rails nested resource and routes for initialized resource - ruby-on-rails-3

I have a problem with the normal way rails operates when using nested forms / resources and routing.
I have two tables, Words and Definitions...
Words have many definitions, but I do not create a Word until it has at least one definition.
Everything on the model and controller end works but I cannot figure out how to handle the form helpers.
<%= semantic_form_for [#word, #definition] do |f| %>
This works perfectly but only if #word actually exists and is not a new UNSAVED record. IE in the controller I am doing a find_or_initialize_by call for Word then building a definition off of that.
<%= semantic_form_for [:word, #definition] do |f| %>
This words but only if the word doesn't exist. IE if I try to edit using this construction I get an odd url (which doesn't work). words/12345/definition/12345
I tried using the url_for helper but had similar results as above...
Any other ideas?

Mongoid doesn't initialize embedded documents by default. You need to build them yourself most likely with a callback in your Word model:
after_initialize :build_definition
def build_definition
self.definitions.build unless self.definitions.any?
end

If you wanna stay CRUD and allow definitions to be created before words, you must duplicate routes for definitions, one inside words and one outside, so you can do:
<%= semantic_form_for [#definition] do |f| %>

Related

Middleman sitemap.where doesn't exist

I'm trying to automatically generate a list of links to pages that have certain frontmatter in them, but every time I try to use sitemap.where(), I get a NoMethodError. For example, the following line:
<%= sitemap.where(:title=>"about") %>
produces this output:
NoMethodError at /
undefined method `where' for #<Middleman::Sitemap::Store:0x007f9b95c7d890>
Ruby layouts/layout.erb: in block in singleton class, line 20
Web GET localhost/
I was wondering if I accidentally messed something up in my project, so I generated a new Middleman project, but I had the same problem when I tried to use sitemap.where. Is there a solution to this or another way that I can query all of the pages?
The where method is part of ActiveRecord and might not work in Middleman.
To get only those pages in the sitemap which have a particular property, you can use Ruby's select:
<% sitemap.resources.select{|p| p.data.title == 'about'}.each do |page| %>
<%= page.url %>
<% end %>
This code will print a (very basic) list of the URLs of pages that match your criteria.

How do you spec an overridden Rails 3 FormHelper?

I have overridden the standard Rails FormHelper to get it to spit form elements out in our (pretty much Bootstrap-based) format (after http://www.likeawritingdesk.com/posts/very-custom-form-builders-in-rails).
It works fine, but it is difficult to write a spec for. I am using a spec like this:
rendered_form = helper.my_custom_form_for(#account, :url => '/accounts') do |f|
f.inputs do
# This isn't rendered
f.text_field 'name', :size => 30
# This is rendered
f.select 'locale', options_for_select([%w(Australia en-AU), %w(UK en-GB)])
end
end
What is rendered includes the select tag, but not the text field tag. I suspect this is because of the way ActionView handles blocks now, appending the return value of the block rather than the return value of each statement in the block. Obviously, when used in context in an app, the helper method gets passed some ERB and evaluates in that context.
Am I right that it's not possible to test rendering helper methods in this way, with multiple statements in a block?
If I am right, what would be the least hacky way of making a spec that does the same thing as the helper does in the context of a full app? Make an ERB string and somehow pass it to the helper to render?
Did you solve this? I am curious if you are just getting the last statment in the block returned. Have you tried changing the f.text_field/f.select order to see what gets returned? If so, then you just need to add them:
rendered_form = helper.my_custom_form_for(#account, :url => '/accounts') do |f|
f.inputs do
f.text_field('name', :size => 30) +
f.select('locale', options_for_select([%w(Australia en-AU), %w(UK en-GB)]))
end
end

ActiveRecord "select" results of model method

I have a Rails app that pulls in music from Soundcloud. This data contains a title, which I save as mix.sc_title but it's not always properly formatted. I have added an additional attribute on my Mix model which I call mix.override_title
For display on my site, I want to use the override title if available, and the sc_title in all other cases.
I have a Mix model method to do this for me
def display_title
override_title.blank? sc_title : override_title
end
Mixes#index grabs #mixes = Mix.where(:active => true) and mixes/index.html.erb looks like this:
<ul>
<% #mixes.each do |mix| %>
<li><%= link_to mix.display_title, mix %></li>
<% end %>
</ul>
As you can see, I'm not directly using any mix attributes, and so I take a huge hit when I go to the DB, and I don't actually benefit from it.
Is there a leaner way to get just the information I need? (mix.display_title)
I have tried Mix.select("display_title").where(:active => true) but it fails because display_title is not a real DB column
You can do Mix.select("sc_title, override_title").where(:active => true) and it will work, since those are the actual fields that the method uses. I don't really think getting the additional attributes gives you that much of a DB hit but sometimes selecting only what you need can be beneficial.
As you start chaining on more Arel commands, consider putting the select into a model method:
def select_active_titles
select("sc_title, override_title").where(:active => true)
end
Edit: Your link_to helper also secretly calls mix.id to link to the right mix, so make sure it's working and if not add id to the list of selected attributes.

Correct way to share a view in the index page

I'm a Ruby-on-Rails newbie, just starting out.
I have an MVC called "account_types", generated via scaffold to produce:
controllers/account_types_controller.rb
helpers/account_types_helper.rb
models/account_type.rb
views/account_types/_form, edit, index etc...
Going to localhost:3000/account_types gives me the index view.
What I'd like to do is display the same data as selected from the account_types index method in the application index page as a list.
I wrote a new view called account_types/_list.html_erb as follows:
<ul>
<% #account_types.each do |account| %>
<li><% account.label %></li>
<% end %>
</ul>
I then edited home/index.html.erb (This is based on examples given in other questions on SO):
<%= render :partial => 'account_types/list', :module_types => #module_types %>
However I get
undefined method `each' for nil:NilClass
and the error displays the code from account_types/_list.html.erb where I've written
<% #account_types.each do |account| %>
The scaffolded views work fine, why aren't mine?
Are partials the right thing to use here?
Thanks in advance.
What is the correct way to define an application-wide partial and its variables in rails says to use a before_filter on ApplicationController.
You pass :module_types to partial, but use account_types. As I can see you just need to change your index.html.erb to:
<%= render :partial => 'account_types/list', :account_types => #module_types %>
You can use partials for this if you want, though it would be unnecessary in this case as far as I can tell (they are for sharing chunks of code between several views). In order to get this code to work you'll need to define #account_types in your controller with something like
#account_types = AccountType.all
You can see exact line in your account_types_controller.rb under index action. :module_types => #module_types is not necessary here, since I doubt you defined #module_types either and you don't use module_types in your partial at all.
It's obvious, that you don't understand how Rails works, so I suggest reading through a good tutorial (like this one) before you proceed with whatever you have in mind.

How do I make a settings configuration page for the rails-settings gem?

I just discovered the rails-settings gem and now I need to make an admin page that lets me edit the setting values. How would I make a settings controller with an edit view that can change these dynamic app wide settings?
I haven't used this gem but it seems like it should be fairly straight forward. Since it uses a database backed model, you would simply create a controller as normal:
rails g controller Settings
From here you would define your index action to gather all your individual settings for display in the view:
def index
#settings = Settings.all
end
Then in the view you can setup a loop to display them:
<% #settings.each do |setting| %>
<%= setting.var %> = <%= setting.value %>
<% end %>
As far as editing ... this might be a bit tricky since by default rails would expect you to submit only one setting at a time to edit. You could do it this way but unless you implement the edit with ajax it might be tedious and non-intuitive.
Another way would be to set up your update method to accept all the individual settings at once, loop through and update each one with new values. It might look something like this:
// The /settings route would need to be setup manually since it is without an id (the default)
<%= form_tag("/settings", :method => "put") do %>
<% #settings.each do |setting| %>
<%= label_tag(setting.var, setting.var) %>
<%= text_field_tag(setting.var, :value => setting.value) %>
<% end %>
<%= submit_tag("Save Changes") %>
<% end %>
This should output all of the settings (given they have been assigned to the #settings variable) with the var name as the label and the current value as the text field value. Assuming that the routing is setup, when you submit this form the action that receives it should all the new settings in the params variable. Then you can do something like this in the action:
def update
params.each_pair do |setting, value|
eval("Settings.#{setting} = #{value}")
end
redirect_to settings_path, :notice => 'Settings updated' # Redirect to the settings index
end
This may not be the best way depending on how often you edit the settings and how many settings you have...but this is a possible solution.
I was looking for some suggestions for this and found another answer to this that is very simple and elegant, for anyone looking for this later. It just sets up dynamic accessors in your model, allowing your form to have settings fields just like your normal attributes. An example can be found in the original answer:
How to create a form for the rails-settings plugin