I am trying to write some validation logic inside of a model for one of my applications. The logic I would like to build in looks like this.
def validation
if this == true or (!that.nil? and those < 1000)
do something
else
do nothing
end
Is it possible to do this within a ruby method?
Sure you can. However, two things to be aware of:
I suspect you mean this == true instead of this = true.
Be very careful when using and and or instead of && and || - they are not equivalent. Read up on operator precedence in ruby, it's subtly different than in other languages such as PHP. You're probably better off sticking with && and || for most logical statements and reserving the use of or and and to control flow, such as redirect and return.
So your concrete example should probably look like this:
if this == true || (!that.nil? && those < 1000)
do something
else
do nothing
end
In this particular case, the parentheses are redundant, since && precedes ||, but they don't hurt, and for anything more complicated, it's good practice to use them to avoid ambiguity and subtle bugs due to a misunderstanding of operator precedence.
Sure, i would only recommend you to create smaller methods like a method compare each of the attributes and on that method call them.
def validation
if this? or others?
#do something
else
#do nothing
end
end
private
def others?
that? and those?
end
def this?
this == true
end
def that?
that != nil
end
def those?
those < 1000
end
Related
We have a library that we use internally that requires you to return a specific type from the method you define. I'm trying to write a cop that enables us to detect that case. Here is the situation I want to detect and flag:
You are inside the Slayer::Command class
You've returned something other than a call to pass or flunk!
It doesn't seem like there's an easy way to check both the call and the parent class, but I know it's doable, because the Rails/HasAndBelongsToMany cop only triggers inside Rails models, not Rails controllers. More specifically, there definitely isn't an easy way to iterate all returns, because the number 1 cause of this bug is people forgetting that the last branch of a method is the return balue.
What I have so far is this:
require 'rubocop'
module Slayer
class CommandReturn < RuboCop::Cop::Base
def_node_search :explicit_returns, 'return'
def_node_matcher :slayer_command?, "(class (const (const nil :Slayer) :Command) _)"
def_node_matcher :is_call_to_pass?, "(send nil :pass ?)"
def_node_matcher :is_call_to_flunk?, "(send nil :flunk! ?)"
def on_def(node)
return unless node.method?(:call)
return unless in_slayer_command?(node)
explicit_returns(node) do |node|
validate_return! node.child_nodes.first, node
end
return # Temporarily does not look at implicit returns
implicit_returns(node) do |node|
validate_return! node
end
end
private
# Continue traversing `node` until you get to the last expression.
# If that expression is a call to `.can_see?`, then add an offense.
def implicit_returns(node)
raise "Not Implemented Yet"
end
def in_slayer_command?(node)
node.ancestors.any? &:slayer_command?
end
def validate_return!(node, return_node = nil)
return if is_call_to_pass? node
return if is_call_to_flunk? node
add_offense(return_node || node, message: "call in Slayer::Command must return the result of `pass` or call `flunk!`")
end
end
end
I need help with two things:
Is this really the best way to check the class' parent? What about inheriting from something else that inherits from Slayer::Command? Something feels off here.
What's the best way to implement the implicit_returns method? That feels like it should be baked in and I'm just missing it.
I'm using Rails 5. I see many posts about getting a boolean from a string, but I would like to go the reverse path. I was wondering if there is a more elegant way than this ...
my_boolean_value ? "true" : "false"
You can use to_s to transform true or false to a string. But you only want that if your value is different than nil. The .nil? method will return true or false (if the variable is nil or not). The exclamation mark negates the assertion. In that case, if it's NOT nil, to_s method will be called.
my_boolean_value.to_s if !my_boolean_value.nil?
You can use my_boolean_value.to_s. Basically it will convert booleans to string.
You can also do "#{my_boolean_value}"
Note: If my_boolean_value can be .nil? or anything other then true/false then your solution in the question is the best and simplest way to do it. You can use following way as well if you don't want to use ternary operator,
(!!my_boolean_value).to_s
But I still think from readability and maintainability point of view, you should use the solution given in the question. Reason would be, you are doing double negation can be confusing if you don't put comments around.
More elegant way? No. More practical way? Maybe.
# in app/helpers/application_helper.rb
module ApplicationHelper
def boolean_label_for(value)
BooleanLabel.to_s(value)
end
end
# in lib/boolean_label.rb
class BooleanLabel
def self.to_s(value)
new(value).to_s
end
def initialize(value)
#value = value
end
def to_s
if #value
"true"
elsif #value.nil?
"i am nil — false"
elsif #value
"false"
end
end
end
It's not overly sexy and you could argue it's unnecessarily complicated, but if you find yourself implementing this check a lot, you should DRY it up.
Try
ActiveRecord::Type::Boolean.new.type_cast_from_user(my_boolean_value).to_s
I want to prevent reports where the values of distributed && checked are both nil || zero from being created.
I also want users to be able to delete existing reports by setting these values to either nil or zero in the form (reports are created and edited in batches and the UX leans towards setting the value to 0 or deleting the values from the form instead of a 'delete' button).
As a hobbyist, something about this feels like a bad idea:
class Report < ApplicationRecord
...
before_validation :prevent_meaningless_reports, if: (distributed.nil? || distributed.zero?) && (checked.nil? || checked.zero?)
...
...
private
def prevent_meaningless_reports
if new_record?
throw :abort
else #persisted?
self.destroy
end
end
end
It feels bad to be destroying a record during a before_* callback.
I'm risking asking a potentially opinion-based question because it seems like I might be violating some software principle that would tell me why this is a bad idea.
If this is acceptable behavior, is it better to do this in two blocks, one for before_create (for new records) and one for before_update (for persistent records)?
The issue you'll run into with this is when you trying to update a meaningless_report that already exists to perform further operations on it.
E.g:
meaningless_report.update(attribute: some_value)
meaningless_report.some_other_value #meaningless_report is already removed from the db and frozen
If you try to update the meaningless_report object again, you'll run into a RuntimeError. So this is definitely not a good idea
A better option is to use ActiveRecord validations, such that even before the reports are created, we validate them to ensure they are not meaningless and also ensure the records don't become meaningless during updates
E.g:
class Report < ApplicationRecord
validate :validate_meaningful_reports
def not_meaningful_report?
(distributed.nil? || distributed.zero?) && (checked.nil? || checked.zero?)
end
private
def validate_meaningful_reports
if not_meaningful_report?
errors.add(:base, "Report violates validations")
end
end
end
With this, we ensure that no meaningless report is created. To handle old meaningless reports, we can use a script or maybe a rake task for this. The script/task can look like this:
meaningless_reports = Report.unscoped.find_each { |r| r.destroy if r.not_meaningful_report?}
This is much safer
A common pattern is to use current_user in many places, but check whether it is set.
if current_user
#your code
end
But instead of injecting an if check just about every time you want to use current_user, how and where can you wrap the current_user method in a different method ONCE, so that the you won't have to deal with your code breaking due to a nil value for devise's default current_user method?
The current_user method is added to ApplicationController, then I think you can override it in ApplicationController doing somethig like:
# in application_controller.rb
alias_method :devise_current_user, :current_user
def current_user
if ...#your validation
devise_current_user # || User.new # <-- or whatever other non-nil result
end
end
Creating a custom classes that have the methods you are trying to use on current_user throughout your application is one way.
http://littlelines.com/blog/2013/06/22/how-to-guard-against-ruby-nil-errors/
If you do not want to account for nil value possibilities while working ruby, you're going to be swimming upstream, and become very frustrated. I know I've had similar feelings. It can be really annoying to write:
if current_user && current_user.attr == 'val'
But I've developed ways to make that less awkward over time...
--edit to show some of the things I do to avoid this--
I often do some of these things. This isn't necessarily a recommendation, or a best practice, but I find that it makes my code less verbose and more readable sometimes
If I expect an array, but the array might be nil:
my_array ||= []
That way I can safely do things that require array-ness, usually when this is needed in various places in the scope.
my_array.size
and not have things choke.
In a similar way I might do
car ||= Car.new
Then I can treat car like any car object. But I probably wouldn't do this with current_user, though.
A colleague and I are working on a RoR project which receives text messages from the Twilio messaging service and then parses them to perform commands. To interface with Twilio, we have a controller action that Twilio POSTs to with a few parameters, like to, from, and message body.
The problem is that we have a lot of possible commands that the user can perform by texting in. Eventually, our code was starting to look like this:
# message_controller.rb
def process_message
words = params[:Body].upcase.split
if words[0] == 'FOO'
do_something()
render 'foo'
elsif words[0] == 'BAR' && params[:From] == 'some number'
.
. # 10 to 15 lines
.
else
render 'command_not_found'
end
end
We decided to refactor using the Interpreter pattern and parse the message body sort of like a language. We ended up with something like this in the controller:
# message_controller.rb
def process_message
interpreter = InitialInterpreter.new(message_params)
while !interpreter.is_terminal? do
interpreter = interpreter.next_interpreter()
end
render interpreter.template
end
def message_params
params.permit(:To, :From, :Body)
end
And we created models like this:
# initial_interpreter.rb, a non-terminal interpreter
attr_accessible :To, :From, :Body
def next_interpreter
if words[0] == 'FOO'
FooInterpreter.new
elsif
.
. # a couple of non-terminal interpreters
.
else
CommandNotFoundInterpreter.new
end
end
# command_not_found_interpreter.rb, a terminal interpreter
def is_terminal?
true
end
def template
'command_not_found'
end
But then we started thinking about it and realized that almost all our commands have no more than two expressions, so we didn't really need the while loop and refactored like this:
# message_controller.rb
def process_message
interpreter = InitialInterpreter.new(message_params)
render interpreter.template
end
# initial_interpreter.rb
def template
if words[0] == 'FOO'
interpreter = FooInterpreter.new
elsif
.
. # a couple of non-terminal interpreters
.
else
interpreter = CommandNotFoundInterpreter.new
end
interpreter.template
end
# command_not_found_interpreter.rb
def template
'command_not_found'
end
But this seems a bit too simple. Now, it's just classes wrapping method. Are we overthinking this? Does this pattern seem good or bad? I feel like we're doing something wrong, but I can't put my finger on it. What would be best way of dealing with this sort of case?
EDIT
I also should have mention that I'm wondering about efficiency too. This solution we're using doesn't seem very efficient, and this action may be receiving a lot of traffic when we go live. I wonder if there's anything rails or ruby specific that might provide a good solution.
In my opinion, your pattern seems fine. I've done something similar in the past and haven't had any bad experiences with it. If you really have a ton of cases, maybe the observer pattern would be okay here. Your InitialInterpreter can accept the message and dispatch it to all of it's observers (which would be your other interpreters). Each interpreter can choose to do something with the message or not based on regular expressions or string matching or whatever you choose.
In this pattern, interpreters contain the logic of when to interpret inside of themselves. The InitialInterpreter and your controller won't care what interpreter picks up the message as long as it's subscribed to the InitialInterpreter's events.
Another benefit to the observer pattern is that it allows you to easily add multiple interpreters for a single command if you need to do so in the future. Ruby has built in support for observers via the Observable module. I believe Rails has something for it as well but I've never personally used it.