My (rails 3) application use collection and product as models. collection has_many products and product belongs_to collection.
I managed to have interactions between products and models. I created a menu displaying the different collection. I want to display a view showing only the product belonging to a specific collection.
1) Is it more elegant to create a new view/controller, or do i create a new view in the product views
2)It seems that i must do something with the routes.rb, but how and what?
3)What link_to arguments must i use to pass the value of my collection?
4)I read a whole book (pragmatic ROR) and depspite that and doing a lot of online research i keep ending here asking for not so much complicated Rails question. What am i doing wrong?
I would go with creating new action in collection controllers. Url will look like this:
/collections/1/products
where 1 is collection id.
I assume you have
resources collections
so you need to add 'products' action for collection member:
resources collections do
member do
get :products
end
end
You can run rake routes from console to see how your application routes look now.
Link code should look like this
link_to "Collection products", products_collection_path(#collection)
In my opinion reading is ok, but while you read you should do lots of examples, write them yourself, becouse otherwise you forget stuff very quickly. I'm 100% sure that stuff I wrote above was in the book you've read.
Related
I have a project going on right now that is really big data model wise. I am trying to figure out the best way to handle inter-model relationships.
For the sake of brevity:
Car
has_many :passengers
has_many :items
or
Team
has_one :head_coach
has_many :coaches
has_many :players
belongs_to :owner
So from the show page I would see who is in the car and what items are in the car. I have some co-workers who think we should have a controller action called manage where they would click a link on the show page to manage the relationship between the other models
So the Team controller would have this
class TeamController < ApplicationController
# ... magic ...
def manage_players
#signed_players = Player.signed_players
#free_agents = Player.free_agents
end
end
The manage_players view would just have links to the actual RESTful actions on the appropriate controller to remove relationships etc...
Anyone have thoughts on how this should be accomplished?
That's an overly complicated approach, and the good news is, it's way simpler than you think.
Save yourself some trouble. The quick answer to your question is to use nested resources: you can have a single form that handles the Car and all the associated passengers/items, or the Team and its coach, players, etc.
The action/view you're describing would just be the edit action on the Car/Team. The manage action name is a nice idea and all, but the action you're really taking is an edit (nothing special, by what you're describing), so why confuse what's going on when the default is to call it edit?
If you want a live example of something that takes advantage of nested routes, check out rpglogger.com (it's my site). When you play around with it, notice the routes/URLs in the address bar.
It's also open source. Specifically relevant to your question is:
see the routes.rb file, and note how I define resources on sections twice - this actually gives me two different versions of the routes - one that's scoped to the LogBook, and one that's scoped to the objects in a section
see the world_object_form.haml (haml also rocks, FYI), which is both my new and edit form - yet it's short, rather uncomplicated, and pretty easy to read/undestand given what it does.
Being new to Rails, I am having a difficult time finding a website or reference that gives a run down summary of Ruby on Rails. I understand MVC, ActiveRecord, and that sort of stuff on a basic level, but I am having a hard time understanding some of the relationships and fundamentals. For instance:
What are all naming conventions I need to be aware of?
How should controller actions be structured and named?
What are the best ways to render information in a view (via :content_for or render a partial) and what are ways I shouldn't use?
What should go into a helper and what shouldn't?
What are common pitfalls or something I need to do correctly from the very beginning?
How can you modularize code? Is that what the lib folder is for?
I have read a number of responses on StackOverflow in regards to this question, but all of them just point to a 300+ page book I need to read, whereas I just want a concise summary of what's important.
Some resources I am already aware of, but do not offer a concise summary of fundamental concepts for new users:
http://railscasts.com/ (good, but fragmented)
http://guides.rubyonrails.org/ (assumes you already understand relationships between everything)
http://ruby.railstutorial.org/ (paint by colors, no good summary)
Rails AntiPatterns (great, but you have to read the whole thing before you understand anything)
The Rails 3 Way (great, but again, you have to read the whole thing before you understand anything)
Thank you for any help, references, or guidance you can provide!
P.S. I would like this wiki to become a living document, so please add to it, edit it, etc. as you feel necessary.
1. What are all naming conventions I need to be aware of?
db table is plural, model is singular, controller is plural. so you have the User model that is backed by the users table, and visible through the UsersController.
files should be named as the wide_cased version of the class name. so the FooBar class needs to be in a file called foo_bar.rb. If you are namespacing with modules, the namespaces need to be represented by folders. so if we are talking about Foo::Bar class, it should be in foo/bar.rb.
2. How should controller actions be structured and named?
controller actions should be RESTful. That means that you should think of your controllers as exposing a resource, not as just enabling RPCs. Rails has a concept of member actions vs collection actions for resources. A member action is something that operates on a specific instance, for example /users/1/edit would be an edit member action for users. A collection action is something that operates on all the resources. So /users/search?name=foo would be a collection action.
The tutorials above describe how to actually implement these ideas in your routes file.
3. What are the best ways to render information in a view (via :content_for or render a partial) and what are ways I shouldn't use?
content_for should be used when you want to be able to append html from an inner template to an outer template -- for example, being able to append something from your view template into your layout template. A good example would be to add a page specific javascript.
# app/views/layout/application.rb
<html>
<head>
<%= yield :head %>
...
# app/views/foobars/index.html.erb
<% content_for :head do %>
<script type='text/javascript'>
alert('zomg content_for!');
</script>
<% end %>
partials are either for breaking up large files, or for rendering the same bit of information multiple times. For example
<table>
<%= render :partial => 'foo_row', :collection => #foobars %>
</table>
# _foo_row.html.erb
<tr>
<td>
<%= foobar.name %>
</td>
</tr>
4.What should go into a helper and what shouldn't?
your templates should only have basic branching logic in them. If you need to do anything more intense, it should be in a helper. local variables in views are an abomination against all that is good and right in the world, so that is a great sign that you should make a helper.
Another reason is just pure code reuse. If you are doing the same thing with only slight variation over and over again, pull it into a helper (if it is the exact same thing, it should be in a partial).
5. What are common pitfalls or something I need to do correctly from the very beginning?
partials should never refer directly to instance (#) variables, since it will prevent re-use down the line. always pass data in via the :locals => { :var_name => value } param to the render function.
Keep logic out of your views that is not directly related to rendering your views. If you have the option to do something in the view, and do it somewhere else, 9 times out of 10 doing it somewhere else is the better option.
We have a mantra in rails that is "fat models, skinny controllers". One reason is that models are object oriented, controllers are inherantly procedural. Another is that models can cross controllers, but controllers cant cross models. A third is that models are more testable. Its just a good idea.
6. How can you modularize code? Is that what the lib folder is for?
the lib folder is for code that crosses the concerns of models (i.e. something that isn't a model, but will be used by multiple models). When you need to put something in there, you will know, because you wont be able to figure out what model to put it in. Until that happens, you can just ignore lib.
Something to keep in mind is that as of rails 3, lib is not on the autoload path, meaning that you need to require anything you put in there (or add it back in)
A way to automatically require all modules in the lib directory:
#config/initializers/requires.rb
Dir[File.join(Rails.root, 'lib', '*.rb')].each do |f|
require f
end
I wrote some naming conventions some time ago:
Rails conventions
General
All filenames are in snake_case following the same conventions
- Model: singular (e.g. Restaurant)
- Controller: plural (e.g. RestaurantsController)
- Table in DB: plural (e.g. restaurants)
- URL's: all in plural (e.g. /restaurants, /restaurants/:id, /restaurants/new)
rails generate Commands
Create model: singular (because the name of the model is singular). e.g. rails g model Restaurant name:string rating:integer
Create migration: plural (because we use the name of the table). e.g. rails g migration AddDescriptionToRestaurants description:text
Create controller: plural e.g. rails g controller Restaurants index show
Model (singular)
ActiveRecord methods
All in singular, because all ActiveRecord's methods are linked to the model.
Examples:
- Restaurant.all
- Restaurant.create(name: "Burger King", rating: 2)
- Restaurant.find(params[:id])
Associations
Singular in the belongs_to. Because it belongs to one element.
Plural in the has_many. Because it has many elements.
e.g.
class Restaurant < ActiveRecord::Base
has_many :reviews
end
class Review < ActiveRecord::Base
belongs_to :restaurant
end
Routes
Resources
Plural when defining a route for a resource:
resources :restaurants
Route helpers
index: plural (because we are showing a list of elements). e.g. restaurants_path. Can also be used used for create.
show: singular (we are showing just one element, it needs the element inside parenthesis). e.g. restaurant_path(#restaurant). Can also be used for update & delete.
new: singular. e.g. new_restaurant_path
In Ryan Bates's first episode on complex forms, he adds the following to a model:
# models/project.rb
has_many :tasks
def task_attributes=(task_attributes)
task_attributes.each do |attributes|
tasks.build(attributes)
end
end
I've never thought about this before, but how does the Project model know what "tasks" of which Project instance? Does that come from the has_many association? Is it like, when the project is running and I'm viewing a Project, that's the "active" object so project.rb knows which Project object we're referring to, so it knows that tasks is really some_current_project.tasks? (I'm obviously grasping at straws here.)
Also, if someone would point me to some reference that explains other questions like this one, I'd really appreciate it.
I hope my question is clear. Please ask for more clarification in comments if needed.
Please note: I know that Active Record handles CRUD actions and that objects correspond to rows in tables, etc. Those are just descriptions of what Active Record is. I'm looking for how it works when the project is running. I also now the constructs MVC, but I can't seem to find a detailed explanation of what information is sent where with respect to Rails.
(Not sure I fully understood your question, feel free to let me know if that's the case.)
A rails model is basically a ruby class that is persisted to a database. So it acts like a normal ruby object for the most part, with some database magic mixed in.
You tell rails which project instance to load (e.g. by providing an id), and it loads the data from the database.
Then, when you call project.tasks is when the magic happens: the Project model has no tasks method, so it will trigger ruby's method_missing method. This will then load the associated records into model instances and provide access to them via a rails object.
Since a project has many tasks, rails knows it should look into the tasks database and load the rows where project_id is equal to the project model's id attribute.
In short, ruby meta-programming and monkey patching possibilities make much of rails' magic possible.
(Edit for question on routing.)
When you want to edit project number 13, you go to a URL that looks something like www.mysite.com/projects/13/edit. If you look at routes.rb in your config directory, you'll see (in Rails3) resources :projects what Rails does is set up all sorts of paths for you. Behind the magic, the edit path looks like
get '/projects/:id/edit' => 'projects#edit'
This basically says "when a user wants to see www.mysite.com/projects/13/edit, send him to the edit action in the projects controller and set the id parameter to the value that's in that place.
Then in your controller, you'll load the appropriate project with
#project = Project.find(params[:id])
In a similar way, you could do this (this is an dumb example):
In routes.rb, put
get '/projects/:id/edit_name/:name' => 'projects#edit'
And then in you controller
#project = Project.find(params[:id])
#project.name = params[:name]
So rails basically uses magic to assign values in the URL to params you can work with in your controller. You can read more about routing here: http://guides.rubyonrails.org/routing.html
Ok, so I've got a weird pattern here that I can't figure out.
I have an STI set with CallList as the base model, and City & State inherited. A city belongs to a state (and a state has many cities).
A campaign has many call lists, so I want to display them all. I loop over campaign.call_lists and sometimes get Cities, sometimes States. When I want to link to them I do
link_to call_list.name, call_list
which works fine if I have these routes:
resources :cities, :states
When I nest cities inside states, however, the link_to helper can't figure out the appropriate route. Is there a simple way to do this, or am I going to have to do some manual path helper construction?
you need to check for kind of call_list and then use proper route..it can not be directly as route is nested..
link_to call_list.name, call_list.is_a?(City) ? call_list : state_city_path(call_list)
My web app, up until this point, has been fairly straight forward. I have Users, Contacts, Appointments and a few other things to manage. All of these are easy - it's just one model per section so I just did a scaffold for each, then modified the scaffolded code to fit my need. Pretty easy...
Unfortunately I am having a problem on this next section because I want the 'Financials' section of my app to be more in depth than the other sections which I simply scaffolded. For example, when the user clicks the 'Contacts' link on the navigation bar, it just shows a list of contacts, pretty straight forward and is in line with the scaffold. However, when the user clicks the 'Financials' link on the navigation bar, I want to show the bank accounts on the left of the page and a few of the transactions on the right.
So the financials tab will basically work with data from two models: transactions and bank_accounts. I think I should make the models (transactions & bank_accounts) and then make a controller called Financials, then I can query the models from the Financials controller and display the pages in app/views/financials/
Am I correct in this app layout? I have never worked with more than the basics of scaffolding so I want to ensure I get this right!
Thank you!
If you are comfortable with scaffolding then i would suggest that you generate a scaffold for both
transactions: script/generate scaffold transaction financial_id:integer ...
bank_accounts: script/generate scaffold bank_account financial_id:integer ...
and financials script/generate scaffold financials ...
In your transactions model, add this:
class Transaction < ActiveRecord::Base
belongs_to :financial
end
In your bank_account model, add this:
class Bank_account < ActiveRecord::Base
belongs_to :financial
end
In your financial model, add this:
class Financial < ActiveRecord::Base
has_many :transactions
has_many :bank_accounts
end
Now from your financials controller you can use something like this:
def index
#financial = Financial.find(params[:id])
#This fetches all bank_accounts related to financial
#bank_accounts = #financial.bank_accounts
#This fetches all transactions related to financial
#transactions = #financial.transactions
end
In your views you can view all the bank accounts belonging to a particular financial by just doing this:
<% #bank_accounts.each do |bank_account| -%>
<%=h bank_account.something_here %> <!-- something_here is the column name corresponding to your bank_account table. -->
<%=h bank_account.something_here %> <!-- something_here is the column name corresponding to your bank_account table. -->
<%=h bank_account.something_here %> <!-- something_here is the column name corresponding to your bank_account table. -->
.
.
.
<% end -%>
In your views you can view all the transactions belonging to a particular financial by adding something similar:
<% #transactions.each do |transaction| -%>
<%=h transaction.something_here %> <!-- something_here is the column name corresponding to your bank_account table. -->
<%=h transaction.something_here %> <!-- something_here is the column name corresponding to your bank_account table. -->
<%=h transaction.something_here %> <!-- something_here is the column name corresponding to your bank_account table. -->
.
.
.
<% end -%>
Remember, while creating a new transaction/bankaccount use the id belonging to the particular financial. Hope this helps. Cheers! :)
It sounds to me as if you want two views:
However, when the user clicks the 'Financials' link on the navigation bar, I want to show the bank accounts on the left of the page and a few of the transactions on the right.the transactions on the right.
Keep in mind that one screen doesn't always mean one view in MVC. Conceptually, a view simply renders model data so the user can do something meaningful with it through controller actions. Rails muddies this up a bit by having you create a file in a views/ directory for each screen, so it's easy to think that one screen means one view. However, you can also call layouts within views in Rails, and these layouts are themselves views.
Putting transactions and accounts in separate views means each would have a controller to handle the user's interaction with each segment of data. The account controller, for example, would handle the event where the user selects an account to view additional information about an account. This would result in fetching transactions to show the user. The user could then interact with a transaction in an attempt to reconcile, void, or contest it. This would invoke logic in the transaction controller.
While this does a good job of removing the coupling from your controllers, this may seem like it couples the two views. However, the coupling is one-way: the account list includes logic specific to the transaction view (i.e. updating it when you select an account). The transaction view remains reusable in other areas of the application. For the most part, the account listing remains reusable as well, especially if you isolate it into a separate layout. The only difference you'd need for this specific screen is the handling of when a user clicks on an account.
If you merge everything into a financials view, it's still possible to keep things loosely-coupled, but it becomes more difficult to do so. The additional layer takes extra effort and may subconsciously guide you to merge everything up together.
Of course, either way works, and you could easily argue that the final design is an exercise that is best left to the developer's preference. I simply offer my interpretation of how one would most-accurately stick to the patters Rails attempts to guide developers to using.
I don't have a huge amount of experience with Ruby on Rails, or MVC work in general, but I think you're going about it the right way. You generally want a model object (and by extension, database table) for data-oriented resources... That is, your models should correspond to your "nouns". Your controller's actions are your "verbs", and to me it makes perfect sense to categorize them how you like.
In this case, I would say that putting a bunch of "verbs" under financials, which interact with two model object types as needed, makes sense IF AND ONLY IF it makes sense to you for you to organize it like this. (Bear in mind that you can also evaluate how well organized and intuitive the corresponding URLs feel to you, though I'm sure most MVC purists would add that you should never just rely on examining the URLs!)
So in summary, I think you're doing everything right here, as long as it makes sense to you. Yes, you'll have to write your views and controller actions yourself, though you can start using ruby script/generate controller financials action1 action2 [...]; that at least gives you skeletons of the view files and empty controller actions.