Controllers in Rails do not "care" how requests are executed - ruby-on-rails

I was reading an article about Rails controllers, can you help me understand please what is meant by the following phrase:
"The best controller is Dilbert-esque: It gives orders without knowing (or caring) how it gets done."
Is it true, in your opinion?
If, for example, I am accessing the index page associated with the subjects controllers, I would define the index method in the subjects_controller.rb rigorously, so I am confused as to what they mean in the article, as I would have thought the opposite.
Any pointers, please?
Thank you and sorry if this is too interpretable. This is the original article: http://betterexplained.com/articles/intermediate-rails-understanding-models-views-and-controllers/

This article is talking about MVC architecture. What's important to take away from an article like this is the fact that Rails is best written with Fat Models and Thin Controllers. This means that you want to have the bulk of your methods/functions in your Model and want to have calls to the functions from your controller. Index is a bad example since typically you're not going to have a lot going on in there.
Your controller for index will typically look something like this
def index
#subjects = Subject.all
end
If you want to scope order for displaying your subjects though, you would do that in your model with a block as follows:
default_scope { order("id DESC") }
A less contrived example might look something like this: Say for example you have an app that accepts input, takes that input and tallies several counters based on what the user entered. Your controller might be named subject_tally and look like this:
def subject_tally
#subject = Subject.find(params[:id])
#subject.winnings += 1
#subject.total_matches += 1
#subject.win_percentage = #subject.winnings.to_f/#subject.total_matches
redirect_to subjects_path
end
THIS IS WRONG. This is a very fat controller and easily moved to the Model where it should be.
If written properly it would look something like this:
subjects_controller.rb: (The Controller)
def subject_tally
#subject = Subject.find(params[:id])
#subject.subject_tally
redirect_to subjects_path
end
subject.rb: (The Model)
def subject_tally
self.winnings += 1
self.total_matches += 1
self.win_percentage =winnings.to_f/total_matches
end
So as you can see, you make only one call from the controller and it "doesn't care" what is actually going on in the backend. It's literally there to pass a value (in this case, the ID of the subject in question) and direct you to another page, in this case, the index.
Furthermore, if you'll notice, you don't need to add that pesky #subject everywhere in your model's subject_tally function... you can reference the attributes of the object just by using self.winnings where you're assigning to an attribute. Ruby is smart enough to know the current subject the method applies to (since you called that function ON a subject from the controller) and in fact you don't even need the self. if you're just retrieving the attributes instead of assigning them... which is why we didn't need self before winnings.to_f or the last line's total_matches.
Very convenient, less code, less time, yay.

The best controller is Dilbert-esque: It gives orders without knowing
(or caring) how it gets done.
means that you should put less logic as you can in the controller,
the controller should only know what to call to get what it needs, and should not know how to carry out a certain action.
In the "Sandy Metz rules" for rails developers (http://robots.thoughtbot.com/sandi-metz-rules-for-developers), she says:
Controllers can instantiate only one object. Therefore, views can only
know about one instance variable and views should only send messages
to that object
only one object could seem a bit extreme, but makes the idea about how much business logic (no logic) you should put in the controller.

Related

Independent ActiveRecord query inside ActiveRecord::Relation context

There is some ruby on rails code
class User < ActiveRecord::Base
def self.all_users_count
User.all
end
end
User.all_users_count
returns, for example, 100
User.limit(5).all_users_count
Now it return 5 because of ActiveRecord::Relation context, in despite of i wroute name of class User.all instead simple all
(.to_sql show that query always contains limit or where id or other things in other cases)
So, how can i make context-independent AR queries inside model methods? like User.all and others?
Thank you!
Ps. Or maybe my code has an error or something like this, and in fact User.all inside any methods and context always must returns correct rows count of this model table
This is very weird and unexpected (unfortunately I can't confirm that, because my computer crashed, and have no rails projects at hand).
I would expect
User.all
to create a new scope (or as you call it - context)
Try working around this with
User.unscoped.all
Edit:
I tried it out on my project and on clean rails repo, and the results are consistent.
And after thinking a bit - this is maybe not even an issue - I think your approach could be faulty.
In what scenario would you chain User.limit(2).all_users_count ?? I can't think of any. Because either you need all users count, and you call User.all_usert_count (or just User.count)
... or you need something else and you call User.limit(2).where(...) - there's no point in calling all_users_count in that chain, is it?
And, when you think of it, it makes sense. Imagine you had some different method like count_retired, what would you expect from such call:
User.limit(2).count_retired ?
The number of retired users not bigger than 2, or the number of all retired users in the system? I would expect the former.
So I think one of two possibilities here:
either you implemented it wrong and should do it in a different way (as described above in the edit section)
or you have some more complex issue, but you boiled your examples down to a point where they don't make much sense anymore (please follow up with another question if you please, and please, ping me in the comment with a link if you do, because it sounds interesting)

Cant call method from helper into model

I wish to use this code but I am having difficulty getting it working. It is contained in a model:
def to_pdf
title = #account.name
subtitle = "Account Statement: #{#month_name} #{#year}"
StatementPDF.new(title, subtitle, transactions).render
end
The transactions method is stored in a helper file (dont ask) but I cant seem to call it. Any suggestions.
def transactions(account_id, start, finish)
return #transactions if #transactions
response = get_call('/Accounts/Statements/' + account_id.to_s + '/' + start + '/' + finish)
response = JSON.parse(response.body)
#transactions = response.map {|txn| Transaction.new(txn)}
end
I have had suggestions regarding placement of my transactions method but have to have it there for reasons above my paygrade. The strangest thing about this is that it worked at a point and when i reloaded the environment just to check everything was working as should be it stopped working again. Any suggestions?
(Moved to answer for readability, but it's more an extended comment.)
Helpers are for the view.
Making HTTP requests from the view layer is bad because (a) it puts too much logic into the view layer, and (b) it's counter-intuitive and could be misused, e.g., someone could accidentally (or purposefully) call it multiple times, potentially incurring the cost twice.
(Partially alleviated because of the sort-of caching, depending on access to #transactions.)
Something like this belongs in a utility class, called from the controller. (In general only the controller should generate data passed into the view.) That makes the controller easier to test because you can trivially mock/stub a top-level utility class method instead of having to use a fake web layer, mocking get_call, etc.
Unrelated, but Ruby favors string interpolation over concatenation, e.g.,
response = get_call("/Accounts/Statements/#{account_id.to_s}/#{start}/#{finish}")
I'm also not a fan of raw calls like this and prefer to see them behind some form of API mechanism, particularly if this is a pattern used throughout the app, something like:
def get_transactions_data(account_id, start, finish)
response = get_call("/Accounts/Statements/#{account_id.to_s}/#{start}/#{finish}")
JSON.parse(response.body)
end
(I might even create a URL builder as well, but I like really, really concise, communicative code.)
The step of transforming them into Transaction objects may or may not belong there as well, it depends on how your app is laid out, and how strongly your layers are separated.

Is there any way to define a model's attribute as always html_safe?

I have a model called Feature with a variable called body_string, which contains HTML markup I'd like to render, rather than escape.
Every time I reference body_string in my views, I need to use <%=raw or .html_safe. This seems redundant and not-so-DRY.
Is there any way that I can establish once-and-for-all the body_string variable as html_safe?
I'm assuming this would happen in the app/models/feature.rb file, but I can't figure out what the right syntax would be, exactly. I've thought of this:
def body_string
return self.body_string.html_safe
end
But Rails doesn't like it; it raises a stack level too deep exception.
Naturally I could define a variable/method with a different name:
def safe_body_string
return self.body_string.html_safe
end
And then just change all references in the views from body_string to safe_body_string. But somehow this seems almost as un-DRY as simply using raw or .html_safe in the first place.
Any insights to how best to handle this? I feel like there must be something really elegant that I'm just not seeing.
Just use read_attribute to avoid the recursive call to body_string:
def body_string
read_attribute(:body_string).html_safe
end
read_attribute is complemented by write_attribute for setting attributes from within your model.
A note on style: Don't use explicit returns unless you actually need them. The result of the last statement in a method is implicitly the value returned from the method.
While #meager's answer will definitely work, I don't think this logic belongs in a model. Simply because it adds view-level concerns (HTML safeness) to the model layer, which should just include business logic. Instead, I would recommend using a Presenter for this (see http://nithinbekal.com/posts/rails-presenters/ or find a gem for this -- I personally love Display Case). Your presenter can easily override the body_string method and provide the .html_safe designation when displaying in the view. This way you separate your concerns and can continue to get body_string from other models without mixing in the view concern.
Maybe this gem is useful for you. I also wanted to stop repeating html_safe all the time when the content is completely trustable.
http://rubygems.org/gems/html_safe_attribute
Or you can also use this approach,
def body_string
super && super.html_safe
end

Avoiding nil in Rails views

I'm sure this has been asked already, but I can't find the answer.
I have a Project model, which has a belongs_to relationship with my Client model. A client has a name, but a project doesn't necessarily have a client.
In my view, I've got code like this:
<%=h project.client && project.client.name %>
because if the project doesn't have a client then trying to access project.client.name causes a NoMethodError (nil doesn't have a method called name).
The question is, is it acceptable to have this kind of nil checking in the view, or should I be looking for another way around it?
Just use
project.client.try(:name)
I think its perfectly acceptable - this is view logic, you are more or less deciding whether or not to show portions of your view, based on whether there is data.
I run into this all the time, and yes it's annoying. Even when there is supposed to never be a nil, dirty data that I inherited sometimes triggers it.
Your solution is one way of handling it. You could also add a method to Project called client_name that displays the client name if it exists, but then you are linking the models together more than some people recommend.
def client_name
client && client.name
end
You could also make a helper method to do it, but you can end up writing a lot of them. :)
As mentioned by Skilldrick below, this is also useful to add a default string:
def client_name
client ? client.name : "no client"
end
You can use delegate in your Project class, so this way you will respect the Law of demeter which says that you should "talk only to your immediate friends".
project.rb
class Project
delegate :name, to: :client, prefix: true, allow_nil: true
end
So this way the project object will know where to ask about the client's name:
#You can now call
project.client_name
See more about delegate in the Rails documentation.
my hacky solution is to yield a block and rescue the error. Many would say using rescue as logic is very bad form. Just don't use this where you would actually need to know when something is nil and shouldn't be.
In application_helper.rb:
def none_on_fail
begin
return yield
rescue
return "(none entered)"
end
end
Then in the view:
<%= none_on_fail { project.client.name } %>
Then methods can be chained as deep as needed and it can be used on any method BUT it will cover up other potential problems with models/relationships/methods if they exist. I would equate it to taking out a splinter with a flamethrower. Very effective with painful consequences if used improperly.
I think these checks can usually be eliminated with a bit of thought. This has the benefit of keeping your view code cleaner, and more importantly, keeping logic out of the view layer, which is a best practice. Some templating engines don't allow any logic in the view.
There are at least a couple of scenarios. Let's say you have a show action that depends on an instance variable. I'd say if the record is not found the controller should not render the html, by redirecting or something else. If you have a loop in the view for an array, use #array.each do |a| end so that it doesn't evaluate if the array is empty. If you truly want an application default in the view, try loading it from a config file, e.g. #page_title || #{#APP_CONFIG['page_title']} (see Railscasts #85). Remember you may want to change these strings later, for example translating the UI.
Those are a couple scenarios where presence checks and usage of try can be avoided. I'd try to avoid them if possible. If you can't avoid them, I'd put the conditional checks in a view helper and add a helper unit test for it to verify (and document) both code paths.

Rails: Helpers and Models - where to organize code

More and more I'm putting all of my code in models and helpers concerning MVC.
However, sometimes I'm not sure where to organize code. Should it go into the model or should it go into a helper. What are the benefits of each. Is one faster or are they the same. I've heard something about all models getting cached so it seems then like that would be a better place to put most of my code.
For example here is a scenario that works in a model or in helper:
def status
if self.purchased
"Purchased"
elsif self.confirmed
"Confirmed"
elsif self.reserved
"Reserved"
else
"Pending"
end
end
I don't need to save this status as in the database because there are boolean fields for purchased, and confirmed, and reserved. So why put this in a model or why put it into a helper?
So I'm not sure of the best practice or benefits gained on putting code into a model or into helper if it can be in both.
Your specific example is including a business rule in the sense that if the instance of the model is both purchased and confirmed then the proper status is "purchased" not "confirmed"
So in your example, I'd definitely put the method in the model since it is coding one of your applications business rules.
A different example:
def status_string
case status
when 0: "Purchased"
when 1: "Confirmed"
else
"Pending"
end
end
In this case, the status_string method could be reasonably defined either in a View Helper or Model--it has nothing to do with any business rules, it is changing the representation of the value. I'd put it in the model since I tend to only put html-related sw into the View Helpers. But depending on your internationalization scheme, a similar method might be better placed in the View Helper.
A good example of a View Helper is an application-wide method to transform date time values into the standard representation for your app. Eg
# application_helper.rb
def date_long_s(d)
d.strftime("%A, %b *%d, %Y *%I:%M %p")
end
This is really subjective and I agree, sometimes it is not clear if something belongs in a model or helper.
For example:
# using model
status ? status.nice_name : "Pending"
# using helper
nice_name(status)
The clear advantage here for the helper is that it can handle nil objects gracefully keeping views clean. The disadvantage is that the code is now in a different location away from the model
Performance wise you will not see any significant difference between using helpers and models. It is more likely that DB round trips to pull status objects will be a bottleneck.
I use constant hashes in this kind of situations.
Hash is defined in model file like this
STATUS = {
1 => "Pending",
2 => "Confirmed"
}
I also declare constants for each status like this.
ST_PENDING = 1
Declaring this is useful when writing queries. For example,
MyModel.all(:status=>ST_PENDING)
status field in database table is number.So when printing, I simply use this.
MyModel::STATUS[obj.status]

Resources