Is there any way to define a model's attribute as always html_safe? - ruby-on-rails

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

Related

Rails common method for updating a database field

I am new to rails and I have a task to write a common method that will update a specific database field with a given value. And I should be able to invoke the method from anywhere in the app.(I understand about the security flaw and so on.. But I was asked to do it anyway) In my application controller I tried
def update_my_model_status(model,id,field, value)
#model = model.find(id)
#model.update(field: value)
end
Of course this doesn't work.. How to achieve this? What is the right way to do this? And if it is possible how to pass a model as an argument to a method?
If you're using Rails, why not use Rails?
Compare update_all:
MyModel.where(id: 1).update_all(banned: true)
or maybe update_attribute:
my_model.update_attribute(:banned, true)
to:
update_my_model_status(MyModel, 1, :banned, true)
Notice how, despite being shorter, the first two approaches are significantly more expressive than the last - it is much more obvious what is happening. Not only that, but they are immediately more familiar to any Rails developer off the street, while the custom one has a learning curve. This, combined with the added code from the unnecessary method adds to the maintenance cost of the application. Additionally, the Rails methods are well tested and documented - are you planning to write that, too? Finally, the Rails methods are better thought out - for example, your prototype naively uses attribute validations, but does not check them (which could result in unexpected behavior) and makes more SQL queries than it needs to. It's fine to write custom methods, but let's not write arbitrary wrappers around perfectly fine Rails methods...
Try this:
def update_my_model_status(model,id,field, value)
#model_var = model.capitalize.constantize.find(id)
#model_var.update_attributes(field: value)
end
Instead of just using update you should use update_attributes:
def update_my_model_status(model,id,field, value)
#model_var = model.find(id)
#model.update_attributes(field: value)
end
http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-update

Should we use strong params when we update only one attribute?

I'm working on a Rails app and I have several actions( #delete_later, #ban_later and so on) where I only set one attribute from the request parameter( specifically, a reason field for doing that action).
I was wondering if it is ok to do it like this:
def ban_later
#object.reason = params[:object][:reason]
#object.save
end
Or is it a best practice to use strong params even in this situation?
def ban_later
#object.reason = object_params[:reason]
#object.save
end
private
def object_params
params.require(:object).permit(:permitted_1, :permitted_2, :reason)
end
Which of these solutions is the best one? If none of them is, then what's the best solution to my problem?
Later Edit:
The #ban_later, #delete_later actions can indeed set a flag column status but that can be done without receiving it's value from the params hash. Since you will only set one status per method you can simply set the status "pending_delete" when you are in #delete_later and "pending_ban" when you are in #ban_later.
Later Later Edit
Why use #save and not update_attributes directly? Let's say you need to have a if #object.save statement. On the false branch( object not saved) you might still want to render a view where the contents of that #object are used.
First one saves computation.
Second one checks for existence of :object sub-hash, which I think is good for fault-tolerance.
I initially would pick the 1st, but after some thought I liked the second one more.
The simplest answer is that if you only use one parameter in params, and do not pass it to a multi attribute setter like model#create then you don't have to use strong_parameters to get a secure solution.
However, I expect that it is unlikely that this is the case for the whole controller. Where the ban_later method only needs one parameter, other controller methods will need more. In this case the question becomes: "do you want to handle params differently for ban_later to how you use it for the other controller methods?".
Also can you be sure that the functionality will not change, and that when you change the functionality, that you'll remember to change the way params is handled.
Therefore, I would use strong_parameters because it means:
parameters are handled consistently across all methods in the controller.
changes to methods are less likely to expose vulnerabilities as functionality changes.
If you're updating a single attribute, why don't you use the update_attributes method? (update_attribute doesn't invoke validation)
def ban_later
#object.update_attributes reason: params(:reason)
end
private
def params params
params = %i(:permitted_1, :permitted_2, :permitted_3) unless params
params.require(:object).permit params
end
In light of the comments by ReggieB, you could also use the update option:
def ban_later
#object.update reason: params(:reason)
end
As mentioned, Reggie and the other answers explain the schematics of how this works best (IE with mass-assignment etc). Above is actionable code which you're free to use.
The bottom line here is that if you want to keep your application versatile (IE having ultimate extensibility wherever you need), you'll need to adhere to the strong params setup.
The other answers outline how that setup works, and how its functionality is different dependent on what you need.
I have included a trick to make it so you only accept specific params in your params method. I've not tested it extensively, so we may have to refactor it to get the required result.
After strong parameters check why not just update the object? Its just a standart workflow. (Please tell me if there are any reasons not to do that in your situation)
def ban_later
#object.update(object_params)
# dont forget validation check
end
private
def object_params
params.require(:object).permit(:permitted_1, :permitted_2, :reason)
end
In this case it'd be much easier to add more updateble fields.

How do I override / add to the method_missing while maintaining searchlogic functionality?

I want to add some behavior to my model when method_missing is thrown, but I know searchlogic already uses that for their named search scopes.
What is the proper way to extend method_missing functionality? Is there a way to react to a method_missing trigger? Much like how one would bind functions to events in javascript?
when you say you want something to happen when method missing is thrown, do you mean every time or just when search logic didn't find something? The former is quite simple:
def method_missing(name, *)
if name =~ /foo/i
# call your custom method
# or for example define_method
else
# you do not want to handle the
# method, so fall back to standard behavior
super
end
end
Where you replace name =~ /foo/i with a condition that checks whether you want to handle the missing method or not. The latter is not as easy. I can't answer it, because i do not know search logic, but you'd have to check whether search logic found something or not before calling your custom method.

Should Rails helpers assume an instance variable exists or should they receive them as parameters?

I'm wondering if there's a specific programming principle (Demeter?) that supports the idea that Rails helpers should never use controller instance variables, rather, they should receive such variables as function parameters. For example, assume my ChickensController#squawk action creates an instance variable called #egg. Furthermore, assume the squawk view contains a call to a helper called cockadoodledoo, implemented like so:
def cockadoodledoo
#egg.to_s
end
Would it be better or unnecessarily verbose to pass #egg as a parameter, such that the view calls cockadoodledoo(#egg) and for the helper to resemble:
def cockadoodledoo(egg)
egg.to_s
end
I hope one of you happy hackers is bored enough on a Friday afternoon to assert an answer. Cockadoodledoo!
This question here is similar, but was never accurately answered.
Receive them as a param. Otherwise, as the app grows, it gets very difficult to trace where the instance vars are being set when refactoring, troubleshooting, etc.
Also, I believe there's a general best practice to only use instance vars in views within the initial template...and from there you should pass the var into helpers and other partials.
I'd say you should always pass the variables explicitly to your helper for 2 reasons:
you control exactly what you do
above all, you can test your helper
I don't know if there is any named principle governing this sort of thing but I would pass an argument. Not only will the argument make your helper easier to test and your application's data flow easier to follow but it will also let you use one helper for a single instance as well as a list; if you pass an argument then both:
<%= cockadoodledoo #egg %>
and:
<% #eggs.each do |egg| %>
<%= cockadoodledoo egg %>
<% end %>
will work as expected without introducing a special cockadoodledoo that handles a list in #eggs rather than a single #egg.
Since helper messages are mixed in to all controllers, hence available to all views (including partials and layouts), it's always wise to establish a clear contract - the parameters.
The only exception I could think of is when a instance variable is also available to all views and controllers, like a menu or something similar.

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.

Resources