I’m upgrading from Rails 4.2 to Rails 6. I have this scope in a model …
scope :valid_type, -> { where{ (model_type == nil) | (modeltype << [36, 38]) } }
Previously, I could run this
MyModel.valid_type.include?(model_instance)
But now I get
NoMethodError:
undefined method `include?' for #<ActiveRecord::QueryMethods::WhereChain:0x00007fb9fc58b3e0>
What’s the right way in Rails 6 to check for inclusion in a WhereChain?
You are currently passing a block to where. where does not accept a block neither in 4.2 or 6.0
The block parameter is being ignored. Since you're not passing an argument to where, it's being treated as a WhereChain that only has one method on it: not. For example as a part of where.not(my_column: nil)
You'll need to fix your syntax. Use the .or method to join your queries and use proper ActiveRecord syntax.
scope :valid_type, -> { where(model_type: nil).or(where(modeltype: [36, 38])) }
In your current case though, Rails will handle the nil for you so there is no need for the or
scope :valid_type, -> { where(modeltype: [nil, 36, 38]) }
Related
I am attempting to define a lambda in a FactoryBot like so...
contact_methods = %w[Phone Email Snail\ Mail Carrier\ Pigeon]
factory :my_object do
transient do
remove_used_value = -> (x,y) { x.reject { |a| a == y }.sample }
end
first_contact_option { contact_methods.sample }
second_contact_option { remove_used_value.call(contact_methods,first_contact_option) }
end
However, when I run FactoryBot.create(:my_object) I get the error undefined method remove_used_value for #<FactoryBot::SyntaxRunner:0x00007fb2ac238960>
I define this as a lambda because there are several other fields that need this same functionality in the Bot. The strange thing is that this was working just fine yesterday, except the logic in the lambda wasn't returning the expected result. After reworking the logic and testing the lambda in the CLI I figured out logic that works, but all of the sudden it's not recognizing the lambda is there.
Is there something wrong with my definition? Also, is there a more preferred way of doing something like this either way?
Most probably replacing this assignment
remove_used_value = -> (x,y) { x.reject { |a| a == y }.sample }
with the proper dynamic attribute defined via a block
remove_used_value { -> (x,y) { x.reject { |a| a == y }.sample } }
will fix the issue.
(To be honest, I would avoid such kind of randomization of the object properties that I don't control in full - this often leads to either flaky specs that fail all of a sudden or, even worse, subtle bugs in the app logic that were not caught by the specs; but I understand that it might be a good solution for the particular use case)
This question already has answers here:
What is the -> (stab) operator in Ruby? [duplicate]
(1 answer)
What does -> mean in Ruby [duplicate]
(2 answers)
Closed 9 years ago.
I just ran across the following line of code in a Rails app:
scope :for_uid, ->(external_id) { where(external_id: external_id) }
What does the -> operator mean? It's kind of hard to Google.
This is syntactic sugar.
->(external_id) { where(external_id: external_id) }
is equal to:
lambda { |external_id| where(external_id: external_id) }
It's new lambda notation. This syntax was introduced in ruby 1.9, and is used to define unnamed functions.
In your example it is scope defined by unnamed function.
The -> operator was introduced in Ruby 1.9 as a shorthand syntax for the old lambda function. It behaves nearly identically to the lambda function but allows you to specify parameters outside the block:
lambda {|param| puts param }
# becomes
-> (param) { puts params }
In a controller I have this finder
User.findByEmail('test#test.com')
And works.
Works even if I write
User.findByEmail(null)
But if i write
User.findByEmail(session.email)
and session.email is not defined (ergo is null) it throw exception
groovy.lang.MissingMethodException: No signature of method: myapp.User.findByEmail() is applicable for argument types: () values: []
Is this behavior right?
If i evaluate "session.email" it give me null so I think it must work as it do when I write
User.findByEmail(null)
Even more strange....
If I run this code in groovy console:
import myapp.User
User.findByEmail(null)
It return a user that has null email but if I run the same code a second time it return
groovy.lang.MissingMethodException: No signature of method: myapp.User.findByEmail() is applicable for argument types: () values: []
You can't use standard findBySomething dynamic finders to search for null values, you need to use the findBySomethingIsNull version instead. Try
def user = (session.email ? User.findByEmail(session.email)
: User.findByEmailIsNull())
Note that even if User.findByEmail(null) worked correctly every time, it would not necessarily give you the correct results on all databases as a findBySomething(null) would translate to
WHERE something = null
in the underlying SQL query, and according to the SQL spec null is not equal to anything else (not even to null). You have to use something is null in SQL to match null values, which is what findBySomethingIsNull() translates to.
You could write a static utility method in the User class to gather this check into one place
public static User byOptEmail(val) {
if(val == null) {
return User.findByEmailIsNull()
}
User.findByEmail(val)
}
and then use User.byOptEmail(session.email) in your controllers.
Jeff Brown from grails nabble forum has identified my problem. It's a GORM bug. see jira
More info on this thread
This jira too
I tried with debugger and it looks it should be working, as you write. Maybe the groovy itself is a little bit confused here, try to help it this way:
User.findByEmail( session['email'] )
I'm using Mongoid, but all I need to do is increment a field which is an integer. Would it be worth it for me to skip using Mongoid's methods, and just run the mongodb ruby drivers method to increment a value?
How would I do that?
Mu is too short is correct, you could either use the built in mongoid #inc method:
Model.inc(:field, integer)
Or you could access the collection directly via the collection attribute:
Model.collection.update(
{ query document },
{ "$inc" => { :field => value } }
)
Why not use Mongoid's inc method?
Model#inc
Performs MongoDB's $inc modifier which increments it's value by the supplied amount or initializes it to that value. If the field is not numeric an error will be raised.
So doing model.inc(:a, 11) should send an { $inc: { a: 11 } } update straight into MongoDB.
I have the following check for nil:
client = !deal['deal']['party']['party'].nil? ? deal['deal']['party']['party']['company_id'] : ""
but still I get:
You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]
How can I prevent this?
I don't know Ruby, but I think it goes wrong before the .nil:
deal['deal']['party']['party']
^ ^ ^
The arrows indicate possible nil indexes. For example, what if ["deal"] is nil or the first ["party"] is nil?
You might want to have a look at the andand game:
http://andand.rubyforge.org/
By checking !deal.nil? and !deal['deal'].nil? and !deal['deal']['party'].nil? and !deal['deal']['party']['party'].nil?
At each step, you can use an appropriate method built in NilClass to escape from nil, if it were array, string, or numeric. Just add to_hash to the inventory of this list and use it.
class NilClass; def to_hash; {} end end
client = deal['deal'].to_hash['party'].to_hash['party'].to_hash['company_id'].to_s
You can also do:
client = deal.fetch('deal', {}).fecth('party', {}).fetch('party', {}).fetch('company_id', '')