How do variables work together in a Rails MVC application - ruby-on-rails

I am reading the RailsTutorial and it is like this:
in the routes.rb file we have added:
resources :users
Then in the app/views/users/show.html.erb file we are using #user
Then in app/controllers/users_controller.rb we can still use #user
Then in app/views/users/show.html.erb again we can use #user
So aren't these all in different folders and classes? so #user is public? how do they see and work together? because I don't have a deep knowledge of Rails this all looks like magic to me. If someone could shed some light on internals of what's going on would be great.

The users in your question are not all the same:
in routes.rb, users is not the #users / #user variable, but a symbol named :users. It could be anything else, :something, :anything, etc. It just a parameter to the "resources" method, which creates routes based on the name passed to it. So if you write, "resources :whatever", then rails will generate default routes for a class named WhateverController - if that exist, it will work
show.html is a view. It may use any member variable declared in it's controller / action (which is users_controller). But it is not global, if you have two controller, they can only use their own variables. Even more, two different actions (methods) in one controller can't use each other's variables, since they are not declared at that time.
in users_controller.rb you actually declare the #users variable which is used in the view

A request first touches your routes.rb file which will direct the request to the appropriate controller action.
ex. example.com/users/new will go to the UsersController new action
The new action will define instance variables which you can then access in your corresponding view (the new.html.erb in your user folder).
resource :users is a rails shortcut to create CRUD routing (http://guides.rubyonrails.org/routing.html#resources-on-the-web)
Each controller action is most likely going to have a different assignment for the #user instance variable. Read this example, http://www.tutorialspoint.com/ruby-on-rails/rails-controllers.html
More Good Reading:
http://www.devarticles.com/c/a/Ruby-on-Rails/Rails-Action-Controller/1/

the # signifies an instance variable. The scope of a variable defines where and how it can be accessed. It would be best for you to learn about variables and their various types in general. Check this out: http://ruby.runpaint.org/variables.

Related

Overriding the default route / path helper in rails to use something other than the `id` field

I can view any User' object's show page in my app using the url
/users/:id
# eg. /users/7
Instead of using the id from my database, I wanted to use a unique hash id that I generate for each user. It looks neater and avoids exposing my sequential DB information
/users/kF87sl8H2
However I lose the convenience of using the route path helper I was previously using - user_path(#user). I now have to replace it in multiple places with user_path(id: #user.hash_id)
Is there a way to keep the old route helper and just pass it my #user instance? I assume it involves overriding the path helper somehow?
Also, marginally related - is there a best practice around generating those hash ID's? I was just going to use SecureRandom.hex, but I know Google+ uses numbers only and Youtube uses different case letters
e.g. https://www.youtube.com/watch?v=dQw4w9WgXcQ
Is there a good way to generate those?
Thanks!
I believe you can do the following to override the route helper method for your User class
class User
def to_param
hash_id
end
end
Try this.
resources :users, param: :hash_id
add the param to your routes file with resources as above with your model field and change the find method in your controller as below.
#user = User.find_by(hash_id: params[:hash_id])
Here is the officail documentation for the same.

What is the logic behind Rails' default restful resource code?

I'm Learning Rails. Lots of the conventions make great sense. The convention for code that maps to controller actions is odd:
//code url controller action
tweets_path /tweets/ def index
tweet /tweet/ID def show
new_tweet_path /tweets/new def new
edit_tweet_path(tweet) /tweets/ID/edit def edit
Why aren't the automatically generated method helpers done in the same symmetrical way as the controller actions? eg:
//code
tweet_index
tweet_show(tweet)
tweet_new
tweet_edit(tweet)
I'm so new I'm sure there's a perfectly good reason, I just don't know it yet :)
There are two asymmetries here.
The first is plural vs. singular route helpers. Why are some helpers tweets_* helpers while others are tweet_* helpers?
The answer is that some resource routes are member routes and others are collection routes. Member routes have to do with an instance of a resource and collection routes have to do with all instances of a resource as a group (unless the resource is singular in which case there are no collection routes). The index action is a collection route and the show action is a member route.
You can declare your own member and collection routes like this:
# routes.rb
resources :tweets do
member do
get :duplicate
end
collection do
get :summarize
end
end
This will create two helpers in addition to the standard ones. Note that Rails will create route helpers that are appropriately singular or plural.
a summarize_tweets_path helper that does not take a parameter
a duplicate_tweet_path helper that does
Official docs are here.
The second asymmetry is that the action is left out of the helper for many of the built-in resource actions. I suppose this could have been for brevity, but I don't really know.
Edit
After thinking about it, the action name was dropped because there is path overloading in Rails and REST. The '/tweet/:id' path could be the show, update, or delete action depending on the HTTP verb. Basically, the path tells you what you are operating on but not what action to take.
The helper methods are generated in a way that makes them more readable by making them more like parts of sentences. Saying 'Create a link to a new tweet' sounds better than 'Create a link to a tweet new'. It helps to keep this in mind as well when naming any custom actions, using names that fit sentences makes it easier to comprehend and remember since this is how we learn to speak.
One reason for it not being symmetrical is that the mappings depends on the HTTP action. For example, a GET to /tweets map to the index action, but a POST to /tweets maps to the create action.

Custom Controller variable in application.html

User Story:
Action for Facebook that has open graph object.
For this I need to modify the tag defined in application.html
Problem:
The logic would need to be defined in helpers or the application_controller
From my understanding this is not clean.
Question:
I want to pass variables directly into the application.html view.
Preferably pass those variables from a custom controller into the application.html. This way I can still utilize the rails routing system to only pass those variables when I am on the facebook action.
The common mechanism for passing variables in to the view is to create instance variables in your controller as these are ported over automatically.
This is the standard approach if it is almost certain they will be used. For things that may not be used, create a helper method that will take care of providing them.
This is the difference between doing this:
def show
#facebook_graph = ...
end
And this in a helper:
def facebook_graph
...
end

Passing from using '#user' to using 'user' in 'html.erb' files: how and why?

I am using Ruby on Rails 3 and I was advised (#user is a global variable, you can't 'localize' it. I would suggest passing local variables from your controller in the same way.) to use variables like user instead of #user in view files. Why to do that, exactly?.
So, I am considering pass from using #user to using user. That is, (in html.erb file) from using
#user.name
to using
user.name
At this time, for example, in the show method of my controller I have:
def show
#user = Users.find(1)
...
end
What I have to change in the controller to do that works in views?
This is only something you need to worry about when the same partial is called in the views from more than one controller.
Having a partial that is using #user in it (likely set in a users_controller), means that the moment you call that partial in a view from some other controller (for example; accounts_controller) that does not set #users you will get an error. If you reference only local variables in your partial you can set them as needed from any controller with the :locals hash that was described.
That's non sense, only instance_variables are sent from the controller to the view.
Nikita Rybak was not wrong in his answer, he just passed the instance variable contained in his view (#current_user) to a partial where it has a different name (user):
:locals => { :user => #current_user }
he concluded very well:
Local variables are local, so you don't need # to refer them.
Indeed you have two choices when working with a partial:
assume it has access to the instance variable (which is not advised)
pass the instance variable to the partial with a local name which is the Rails' way
Take a look at this Rails Best Practice. Using local variables is preferable way when rendering partials (those view files which start with a _). That's because you'll need review your controller's code to know about instance variable.

Create a routes to a controller without model with a /id

I have product with a foreign collection_id key
I want to pass an id to a controller.
To do so i have the following routes for my controller :
controller :magasin do
get "magasin" => "magasin#index"
end
The only view in my controller is magasin/index.html.erb
The link to magasin is link_to collection.nom, magasin_path(collection)
This kind of syntax usually works in controllers with models. Here my link is : http://localhost:3000/magasin.2 instead of http://localhost:3000/magasin/2
Later on i will need to call the same view with product_kind_id instead of collection_id and i will add sort by name/price ....
How can i have the ID as a normal argument (/:id)instead of a type(.id)?
A popular URL schema to follow is RESTful routing. Rails has this built-in and will set it up for you if you initialize your resource via rails generate scaffold Magasin nom:string.
If you put resources :magasins in your routing file, it will route /magasins to MagasinsController#index and /magasins/1 to MagasinsController#show with params[:id] set to "1". It will also set up a few other routes that will be useful in the future but for now will just raise an action not found exception.
You don't want to use the dot as an argument delimiter, since Rails places what comes after the dot in the request.format method or just in params[:format] (ordinarily accessed through the respond_to method that comes with the generated scaffolds). Save that dot for later when you are working on delivering alternative display formats like XML and JSON.
I realize I've said a lot in a small space, so feel free to ask any follow up questions once you've consulted the Rails Guide on the issue, and I'll be very glad to help!

Resources