What is the use of ! in rails?
Especially in this line: From HArtl tutorial
users = User.order(:created_at).take(6)
50.times do
content = Faker::Lorem.sentence(5)
user.each { |user| user.microposts.create!( content: content )}
end
Basically this is creating tweets/microposts for 6 users.
I am really wondering why need to use !
The important thing to remember is that in Ruby a trailing ! or ? are allowed on method names and become part of the method name, not a modifier added on. x and x! and x? are three completely different methods.
In Ruby the convention is to add ! to methods that make in-place modifications, that is they modify the object in fundamental ways. An example of this is String#gsub which returns a copy, and String#gsub! which modifies the string in-place.
In Rails this has been ported over to mean that as well as situations where the method will raise an exception on failure instead of returning nil. This is best illustrated here:
Record.find_by(id: 10) # => Can return nil if not found
Record.find_by!(id: 10) # => Can raise ActiveRecord::RecordNotFound
Note that this is not always the case, as methods like find will raise exceptions even without the !. It's purely an informational component built into the method name and does not guarantee that it will or won't raise exceptions.
Update:
The reason for using exceptions is to make flow-control easier. If you're constantly testing for nil, you end up with highly paranoid code that looks like this:
def update
if (user.save)
if (purchase.save)
if (email.sent?)
redirect_to(success_path)
else
render(template: 'invalid_email')
end
else
render(template: 'edit')
end
else
render(template: 'edit')
end
end
In other words, you always need to be looking over your shoulder to be sure nothing bad is happening. With exceptions it looks like this:
def update
user.save!
purchase.save!
email.send!
redirect_to(success_path)
rescue ActiveRecord::RecordNotFound
render(template: 'edit')
rescue SomeMailer::EmailNotSent
render(template: 'invalid_email')
end
Where you can see the flow is a lot easier to understand. It describes "exceptional situations" as being less likely to occur so they don't clutter up the main code.
Related
I ran into a problem, when PG fails out of sync (well known problem)(example).
PG fails out of sync, sequence of id stops incrementing and raises ActiveRecord::RecordNotUnique error.
But all solutions proposed here (all I found) propose some manual solutions - either do some operations in console, either run custom rake task.
However, I find this unsatisfying for production: each times it happens, users get 500, while someone administrating server should operatively save the day. (And according to test data for some reason it possible will occur frequently in my case).
So I would like rather to patch ActiveRecord Base class to catch this specific error and rescue it.
I use this logic sometimes in controller:
class ApplicationController < ActionController::Base
rescue_from ActionController::ParameterMissing, ActiveRecord::RecordNotFound do |e|
# some logic here
end
end
However, here I don't need retry. Also, I would like to not to go deep in monkey patching, for example, without overriding Base create method.
So I was thinking of something like this:
module ActiveRecord
class Base
rescue ActiveRecord::RecordNotUnique => e
if e.message.include? '_pkey'
table =e.message.match(//) #regex to define table
ActiveRecord::Base.connection.reset_pk_sequence!(table)
retry
else
raise
end
end
end
But it most likely doesn't work, as I'm not sure if Rails/Ruby will understand what exactly it asked to retry.
Is there any solution?
P.S. Not related solution for overall problem of sequence which will work without manual command line commands and having unserved users are also appreciated.
To answer the question you're asking, no. rescue can only be used from within a begin..end block or method body.
begin
bad_method
rescue SomeException
retry
end
def some_method
bad_method
rescue SomeException
retry
end
rescue_from is just a framework helper method created because of how indirect the execution is in a controller.
To answer the question you're really asking, sure. You can override create_or_update with a rescue/retry.
module NonUniquePkeyRecovery
def create_or_update(*)
super
rescue ActiveRecord::RecordNotUnique => e
raise unless e.message.include? '_pkey'
self.class.connection.reset_pk_sequence!(self.class.table_name)
retry
end
end
ActiveSupport.on_load(:active_record) do
include NonUniquePkeyRecovery
end
I'm just getting started with Rails, so I'm using Brakeman to learn about potential vulnerabilities in my newbie code. It's throwing a high-confidence "Dynamic Render Path" warning about the following code in my show.js.erb file:
$('#media-fragment').html('<%= escape_javascript(render(params[:partial])) %>');
I actually expected this was a problem, so no surprise there. So I changed it to the following:
# controller:
def show
if legal_partial?
#allowed_partial = params[:partial]
else
raise StandardError, "unexpected partial request: #{params[:partial]}"
end
end
private
def legal_partial?
%w(screenshots video updates).include? params[:partial]
end
# ...
# show.js.erb
$('#media-fragment').html('<%= escape_javascript(render(#allowed_partial)) %>');
Although I believe the code is now safe, Brakeman is still unhappy with this. Is there a more idiomatic way to control rendering of a partial based on user input?
Update (2/5/2016):
This has been fixed as of Brakeman 3.0.3.
If the legal_partial? method is inlined like this:
def show
if %w(screenshots video updates).include? params[:partial]
#allowed_partial = params[:partial]
else
raise StandardError, "unexpected partial request: #{params[:partial]}"
end
end
Brakeman will be able to detect the guard condition and will no longer warn about the later render call.
Original answer:
Unfortunately, Brakeman does not know that if legal_partial? is a proper guard. All it knows is that params[:partial] is assigned to #allowed_partial, and that is then passed to render.
You may be able to tell that #allowed_partial will always be a safe value. At that point, you have to consider whether or not it makes sense to add complexity in order to make a tool happy.
Just as an example, you could do this:
def show
render_allowed_partial params[:partial]
end
def render_allowed_partial name
if %w(screenshots video updates).include? name
#allowed_partial = name
else
raise StandardError, "unexpected partial request: #{params[:partial]}"
end
end
It's basically the same thing, except now you are hiding the assignment of #allowed_partial from Brakeman.
(Warning: Not necessarily "best" way of doing this.)
Using brakeman 4.2.0
I had a similar issue trying to render a specific hand-positioned-and-named template. Every product of my app required that specific named template. The template name came from the controller params as params[:a_particular_slug].underscore.
I solved with something like this:
def show
if #products = Product.where(a_slug: params[:a_particular_slug])
render template: lookup_context.find(params[:a_particular_slug].underscore, ["featured_products"])
else
render_404
end
end
Here I'm looking for a template. If you need to use a partial, be aware that lookup_context.find third params set to true allows to search for partials.
You can find more about lookup_context.find here
Hope this helps.
I'm pretty sure I saw on a Rails related site something along the lines of:
def my_function(*opts)
opts.require_keys(:first, :second, :third)
end
And if one of the keys in require_keys weren't specified, or if there were keys that weren't specified, an exception was raised. I've been looking through ActiveSupport and I guess I might be looking for something like the inverse of except.
I like to try and use as much of the framework as possible compared to writing my own code, that's the reason I'm asking when I know how to make the same functionality on my own. :)
At the moment I'm doing it through the normal merge routine and making sure that I have what I need with some IFs.
I think the method you're thinking of is assert_valid_keys (documentation here) but this only raises an exception if any unexpected keys exist in the hash, not if any of the specified keys are missing. i.e. if a hash is being used to pass options to a method it can be used to check for invalid options not for required options.
You can do this yourself relatively easily. As was stated in an earlier answer, half your work is done for you in assert_valid_keys. You can roll your own method to do the rest.
def my_function( *opts )
opts.require_and_assert_keys( :first, :second, :third )
end
create lib/hash_extensions.rb with the following:
class Hash
def require_and_assert_keys( *required_keys )
assert_valid_keys( keys )
missing_keys = required_keys.inject(missing=[]) do |missing, key|
has_key?( key ) ? missing : missing.push( key )
end
raise( ArgumentError, "Missing key(s): #{missing_keys.join( ", ")}" ) unless missing_keys.empty?
end
end
finally, in config/environment.rb, add this to make it work:
require 'hash_extensions'
"output" is a serialized OpenStruct.
def title
try(:output).try(:data).try(:title)
end
What would be better? :)
Or simply this:
def title
output.data.title rescue nil
end
Referring to this blog you might find it better to use the &. operator like below for ruby version > 2.3.0;
output&.data&.title
def try_chain
yield
rescue NoMethodError
nil
end
def title
try_chain { output.data.title }
end
Thoughtbot just talked about this on their blog, using what they call it's a Shallow Nil:
def swallow_nil
yield
rescue NoMethodError
nil
end
So, in their example, they could do something like:
campaign = swallow_nil { supporter.politician.campaign }
Or, in your case,
def title
swallow_nil { output.data.title }
end
However, be aware that any of your bugs will also be swallowed and would be hard to find, specially since it traps every NoMethodErrors, which would be caused from other parts of your code (although if you use testing, this helps a lot).
Another approach would be to use andand, where your code would be then
def title
output.andand.data.andand.title
end
Not as clean as the swallow_nil one, but probably best to not just ignore everything.
Is there a built-in way of specifying asserts in Rails that will throw an exception if an invariant is broken during development and testing?
Edit: Just to be clear, I'm looking for asserts that can be placed in models or controllers as opposed to asserts that you would use for unit tests.
There are many assert functions if you are writing tests. But for assertiona in the main code, there aren't any and you can roll your own easily.
Add something like this to environment.rb:
class AssertFailure < Exception
end
def assert(message = 'assertion failed')
unless block_given? and yield
raise message
end
end
and make it a no-op in your environments/production.rb so there is minimal overhead
def assert(message = 'assertion failed')
end
Then, in your code, you can assert to your heart's content:
assert { value == expected_value }
assert('value was not what was expected') { value == expected_value }
If value does not equal expected_value and you aren't running in production, an exception will be raised.
Beyond these, you mean?
Raise exceptions, and use rescue_from.
As #Gordon said, no there isn't. I ended up using the solid_assert gem by Jorge Manrubia, as mentioned in this question: Is it idiomatic Ruby to add an assert( ) method to Ruby's Kernel class?