Better alternative to try(:output).try(:data).try(:name)? - ruby-on-rails

"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.

Related

What is the use of ! in rails

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.

Set Parameter if blank

I need to set the id parameter to a value if it is wasn't submitted with the form.
Is it ok to do something like this in Rails or does this violate any standards or cause possible issues?
if params[:cart][:cart_addresses_attributes]["0"][:id].blank?
params[:cart][:cart_addresses_attributes]["0"][:id] = 1234 #default id
end
My implementation works with this logic, but I am not sure if this is the proper way to handle the issue.
There's a chance [:record_type] is nil which will lead to an undefined method error when you attempt to call [:id] on nil. Additionally, I'd find it a bit weird to directly mutate params, even though you technically can do that. I'd consider using Strong Parameter processing methods like so (added a full action, which isn't in your sample, to give more context on how this would be used):
def create
#record_type = RecordType.new(record_type_params)
if record_type.save
redirect_to #record_type
else
render :new
end
end
def record_type_params
params.require(:record_type).permit(:id).reverse_merge(id: 1234)
end
The reverse_merge call is a way to merge the user-supplied parameters into your defaults. This accomplishes what you're after in what I would consider a more conventional way and doesn't mutate params.
def cart_params
params.require(:cart).permit(:cart_addresses_attributes => [:id]).tap do |p|
p[:cart_addresses_attributes]["0"][:id] ||= 1234
end
end
if params[:record_type][:id].nil? # or replace ".nil?" with "== nil"
params[:record_type][:id] = 1234
end
personally, this is the way I prefer to do it. Some ways are more efficient than others, but if that works for you I'd roll with it.

Rails/Rspec respond_to a method_missing lookup

I know some of you are already doubting my sanity with this. I have a ActiveRecord class that uses method missing to dig inside a JSON attribute it has.
# app/models/request_interactor.rb
...
def method_missing(method_sym, *arguments, &block)
return self.request_params[method_sym.to_s] if self.request_params[method_sym.to_s]
super
end
the test looks like this
before(:each) do
#ri = RequestInteractor.create(result: {magic_school: true, magic_learnt: 'all things magical'}, request_params: {application_id: 34, school_id: 20, school_name: 'Hogwarts', course_name: 'Defence against the Dark Arts.'})
end
it 'should respond to attributes set in the request parameters' do
expect(#ri).to respond_to(:school_name)
expect(#ri.school_name).to eq('Hogwarts')
end
I tried binding inside the test, the #ri.school_name will eq 'Hogwarts', but when it runs the responds_to it will fail saying there is no such a method! The dirty, dirty liar!
I tried doing something like this in the model:
def respond_to?(method, include_private = false)
super || self.respond_to?(method, include_private)
end
But this will return a stack level too deep, because of recursion, because of recursion.. so now the fate of my day is in your hands! Enlighten me O' great ones. how would I test the respond to of the method missing.
Use respond_to_missing. More infos here.
Now, with all this being said. Your pattern will still look hackish if you ask me.
Refactors
Ruby has tons of way to clean this.
Use a delegation pattern
delegate :method_name, :to => :request_params
(check other options in doc). This should solve your problems by having a method in your object so respond_to? will work and you will avoid overriding method_missing.
Generate your access methods when setting request_params (meta-programming your accessors).
Use OpenStruct since these can be initialized with a Hash such as your request_params. If you add delegation on top, you should be cool.
Hope this helps.

How to output a value that is a method in rabl?

I am trying to output the value of a method on my Item model (current_user is defined in application_controller). I currently have as my rabl template:
object #item
attributes :id, :name
code :is_liked do |this_item|
if current_user
this_item.is_liked current_user
else
false
end
end
and in my model:
class Item < ActiveRecord::Base
...
def is_liked user
if user
if user.liked_item_ids.include?(self.id)
return true
else
return false
end
end
end
....
end
but it isn't working. I'm not sure what a proper way of outputting this would be. Any idea how to get this to work correctly?
edit 1
Here's the error that I'm getting:
Failure/Error: Unable to find matching line from backtrace
ActionView::Template::Error:
stack level too deep
Your rabl seems to be fine, however, when your find yourself adding some logic in your views (rabl can be compared to a view) you might want to consider refactor the logic in a presenter.
More information about presenters with rabl here
Regarding your error, like #apneadiving just said, there is a recursion issue in your codebase somewhere. Just by curiosity, have you try to rename the code block into something else than your method's name ? Depending on which version on rabl you are using, this could be the issue.
Finaly, you should consider refactoring your is_liked method:
def is_liked user
return user.liked_item_ids.include?(id) if user
false
end
Try:
node(:is_liked) {|this_item| this_item.is_liked(current_user) }
You already have the method, you can simply invoke here within a node instead of recreating logic.

Is there a better way to write the find_messages_by_slug_or_404 method?

In my messages_controller I have the following private method:
def find_message_or_404(slug)
message = user.messages.find_by_slug(slug)
if message.nil?
raise Error404
end
message
end
I find it not elegant and not very Rubist. Is there a way to improve it?
If all you want to do is shorten the code to be more Ruby-like, how about:
def find_message_or_404(slug)
user.messages.find_by_slug(slug) || raise Error404
end
Non-nil find_by_slug will return the message, otherwise it branches to the raise statement.
Personally I don't think there's anything wrong with what you have (coding style wise), but maybe you like this better:
def find_message_or_404(slug)
user.messages.find_by_slug(slug) or raise Error404
end

Resources