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
Related
I know in Rails we follow conventions. And we should name controllers in plural form.
Recently I hired a freelancer to help me with one part of my application in Rails as I'm really new to this framework and ruby.
I had a PortfolioController - this is just feels right because portfolio is a container for entries (who says "I have portfolios"?).
The freelancer said it's not right and I will have troubles not following the convention and renamed it to PortfoliosController. I asked several times what are the exact problems I will have if I name my controller PortfolioController rather than PortfoliosController and I didn't get any explanation other than "You will have problems".
So, can anybody tell me what those problems are?
Well, the simplest reason would be that anyone else who works on that project will probably refer to it in the plural while working on the code, then have to realize "oh, they decided to not follow convention in that one controller" after an unspecified time period of "WTF?" while they try to figure out what they are doing wrong. Also semantically, your controller is a controller of ALL the Portfolios in the Portfolio table.
Code-wise you will have issues with routes. You will have to craft a bunch of non-standard routes because http://my_app/portfolios goes to the index action of the controller by default. You then show a particular portfolio with http://my_app/portfolios/1 which will show you the portfolio with the id of 1. So be prepared to create and maintain a host of custom routes in your config/routes.rb file. You see similar problems with things that have names that are the same whether plural or singular like equipment where you can have one piece of equipment or many pieces of equipment. See this: rails link path and routing error when model singular and plural name are the same (e.g. equipment, species). Not only is it making your routes wonky, it is causing conflicts in methods like portfolio_path or portfolio_url.
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.
I'm pretty much a Rails beginner, at the moment developing a rather complex webapp in Rails 2.2.
So, in this webapp, there are "factories". Not the design pattern, mind you, actual factories (it's a game).
For each factory in a player's team, there are a bunch of options he can take.
Level of maintenance for the machinery
Rewards for workers
etc.
I have two problems with this:
These are all checkboxes, and the checkbox needs to be defaulted with the answer already stored in the db. What I mean is the check must be already in the box if the player already checked it (sorry if this passage is confusing).
This information is stored in multiple tables. The level of maintenance in a factory is saved in one table, while the performance bonus given to workers is in another one.
Can anyone help me with this? This is all way over my head.
Thanks all.
To understand question 1, keep it simple and pretend the attributes you want to access are in the factories table. The design pattern then is pretty simple:
Database schema:
create_table :factories do |t|
t.boolean :now_operating, :default => true, :null => false
...
Controller:
def edit
#factory = Factory.find(params[:id])
if request.post?
#factory.update_attributes(params[:factory])
end
end
View:
<% form_for :factory, #factory, :url => { :action => "edit" } do |f| %>
<%= f.checkbox :now_operating %>
...
You can do this with less code, in fact with none at all if you use RESTful resources and follow naming conventions, but this is a little too much magic to start off, because it conceals what rails is doing for you.
Namely: when the :get action loads an object matching the object name passed to form_for, rails will populate the form fields with that object's attribute values from the database. Then, when the client submits the form, rails encodes those fields into the request as params for that object, which you can then update in the :post action. See the official Rails Guide for a more detailed treatment.
I apologize if that was a little too basic, but it's important to get the basic pattern because the general approach to solving problem 2 is the same:
Load the objects in your :get action.
Render a form with the objects enclosed.
Save the params for each object.
There are several ways of actually doing this for forms with multiple objects. I suggest you see the various tutorials listed under Item 8, Building Complex Forms, in the Rails Guide.
Two last points to consider:
Do you really need those extra tables? If each maintenance row
contains attributes for a single
factory, it may make more sense to
just put the attributes in the
factory table and simplify your forms
and actions.
Attributes such as :maintenance_level might be more
appropriately represented as a
:string type with a radio_button in
the form to set the level.
if the child entities (level of maintenance, performace, etc) are linked to the Factory, then you can use nested model forms.
You can check some info about nested forms here, or just google "rails nested forms" and you'll get plenty of blog posts and tutorials.
--EDIT
Seeing that you added that you're using rails 2.2, you could try to use simple_forms or formtastic to create the nested forms (this is just a suggestion as I have no idea if it works). And I can only thing of setting the values manually to update the models.
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
I'm looking for guidelines on when to know when a RESTful approach to a model and it's associations is proper, and when it's not. Perhaps it's the nature of my current application, but I'm finding that simple models with no associations work well with REST, but complex models with many has_many assocations really seem to complicate the view and the setup required in the controller. form_for calls start to become especially complicated.
Or perhaps it's my neophyte understanding. I've been doing Rails for over three years now, but REST and form helpers together seem to mystify me.
Make a resource of each top-level model in your system. By top-level, I mean models that are independent and have meaning outside of the associated model. Generally, that's most models. In the following example Position and Candidate are top-level. You could consider Candidate to be composed of PastEmployment and positions to which she has applied. Applications to positions and prior work history can be accessed through the Candidate resource, since they don't exist on their own.
Models
class Position
has_many :candidate_positions
has_many :candidates, :through => :candidate_positions
end
class Candidate
has_many :candidate_positions
has_many :positions, :through => :candidate_positions
has_many :past_employments
accepts_nested_attributes_for :past_employments
accepts_nested_attributes_for :candidate_positions
end
class PastEmployment
belongs_to :candidate
end
class CandidatePosition
belongs_to :candidate
belongs_to :position
end
Routes
map.resources :positions
map.resources :candidates
Use a non-resourceful controller for interactions with the user that span models. For example, if you wanted to have a HomeController that shows available positions as well as recent candidates, that would be a new, plain controller. If you want to edit any of the information on this controller, cool! You already have controllers available to handle the form posts, that will automatically be wired with <% form_for #candidate %>. You can render your collection of positions with <%= render #positions %>, and because you've made them a resource, Rails will know to look in views/positions/_position.html.erb for the appropriate partial.
The end result should be that you're never writing logic to handle the persistence of an object in more than one place. That's when controllers get complex and forms get out of hand. It also means that Rails and external systems know where to retrieve and store objects. Same URL, same controller, just a different format.
You might check out this series of Railscasts, which talks about has-many relationships and forms in the context of REST:
Complex Forms Part 1
Complex Forms Part 2
Complex Forms Part 3
I don't know RoR, so I will make generate statements on REST.
REST and ROI treats the URLs as series of nouns, and it uses HTTP methods like GET, POST, PUT, and DELETE as the verbs. This model works for CRUD, and even for models with multiple associations. Since URLs represent resources, associated objects can be expressed as list of URLs.
However if your system requires more fine-grain verbs like validate, move, copy, honk, read, write etc. it may not suit REST.
disclaimer: I know rails, but I still am pretty much a newbie.
Short Answer: REST and form helpers are completely different areas.
Long answer:
As I understand it,Representational State Transfer is only loosely related to the actual rendering of forms and views.
REST really has to do with controllers, and to a certain extend models. The idea is that instead of trying to think about an entire conversation with a client, you write a webapp to respond in specific, predictable ways to individual client messages.
i.e., if a client GETs a model, you just retrieve it, format it for them, send it to them, and forget about it.
if a client POSTS an update of some sort, you change the webapps state to reflect that, send back any response, and then forget about it. Any future GET or POST will look at the new state, but not the message that created it.
So, really, whether or not an application is RESTful depends not really on how complicated the model is, but on how users interact with it. An app meant to be at least somewhat client-agnostic, that is data-centric, is a good candidate for REST. Something that relies heavily on sessions, and interacting with a specific user, and is process-centric, might not be such a good candidate.
On the other hand, you have Rails form helpers. These are great for scaffolding, but sometimes can be frustrating when you try to use them in more complicated ways.
So, what is your main question? Do you have a specific question about rails form helpers? about rails controllers? or something specific to REST?