Rails Find with a Block

Rails find with a block

It's a shortcut for .to_a.find { ... }. Here's the method's source code:

def find(*args)
if block_given?
to_a.find(*args) { |*block_args| yield(*block_args) }
else
find_with_ids(*args)
end
end

If you pass a block, it calls .to_a (loading all records) and invokes Enumerable#find on the array.

In other words, it allows you to use Enumerable#find on a ActiveRecord::Relation. This can be useful if your condition can't be expressed or evaluated in SQL, e.g. querying serialized attributes:

Consumer.find { |c| c.preferences[:foo] == :bar }

To avoid confusion, I'd prefer the more explicit version, though:

Consumer.all.to_a.find { |c| c.preferences[:foo] == :bar }

Rails, how to use a block in where statement

:where does not accept block condition.

You can use plain SQL, like Customer.where('id = main') if it works for you.

Or, you can go with more complex way... using arel tables.

consumer = Consumer.arel_table
Consumer.where(consumer[:id].eq(consumer[:main])).to_sql
=> "SELECT \"consumers\".* FROM \"consumers\" WHERE \"consumers\".\"id\" = \"consumers\".\"id\""

Rails find_or_create_by where block runs in the find case?

As far as I understand block will be executed if nothing found. Usecase of it looks like this:

User.find_or_create_by_name("Pedro") do |u|
u.money = 0
u.country = "Mexico"
puts "User is created"
end

If user is not found the it will initialized new User with name "Pedro" and all this stuff inside block and will return new created user. If user exists it will just return this user without executing the block.

Also you can use "block style" other methods like:

User.create do |u|
u.name = "Pedro"
u.money = 1000
end

It will do the same as User.create( :name => "Pedro", :money => 1000 ) but looks little nicer

and

User.find(19) do |u|
..
end

etc

Rails Find a Record Within ActiveRecord::Relation Object without querying database again

Once the relation has been loaded, you can use regular array methods. find is actually a very interesting method here - if block is specified, it will be delegated to the relation target:

@blogs.find {|b| b.id == 1}

Ruby - Array.find, but return the value the block

[1, 2, 3].detect { |i| i += 1; break i if i == 2 } 
# => 2

[1, 2, 3].detect { |i| i += 1; break i if i == 10 }
# => nil

Why is the first value of a Rails find_or_create_by block always nil in database?

The commas that mu is too short spotted are what's actually causing the problem here.

This is interpreted by Ruby as trying to assign an array to start_date_time with a side effect of setting all the other attributes along the way. As this isn't a valid value for the field it ends up remaining as nil.

It looks like this stems from a slight misunderstanding:

Rails should create a new TrainingEvent with all of the attributes passed in the block

The block isn't for passing a list of attributes. The block is code which in the case of create is passed the new model object so that you can do anything you'd like with it prior to it being saved. Remove the commas and you'll be fine!


If you're curious, here's a simpler example that demonstrates how your code is being interpreted:

> x = 1, y = 2

This results in x having the array value [1, 2] and y having the value 2

implementing find_or_create_by when find involves single attribute but create involves multiple attributes

find_or_create_by takes a block that will be applied only on create. It'll look for a record by the attribute you provide and if none is found, will create one with the block attributes you specify.

In your case this would look like:

user = User.find_or_create_by(email: recipient) do |user|
user.password: Devise.friendly_token.first(6),
user.confirmed_at: DateTime.now
end

Find and replace in a block of text

Global substitution of image: xyz on a separate line with <img src='xyz'/>:

text.gsub!(/^image:(.+)$/) { "<img src='#{$1}'/>" }

Why did my Rails find query select all records?

Passing a block to find changes the behaviour of the method. What ends up happening is it will load all models, then return only those for which the supplied block returns true, not unlike how Enumerable#find works.

If you did something like:

MyUserModel.find { |m| [1,2,3].include?(m.id) }

Then you'd have an equivalent version, though this one is way slower since all records must be loaded rather than filtering at the database level.

Generally you'd want to write this as:

MyUserModel.where(id: [ 1, 2, 3 ]).each do |model|
# ...
end


Related Topics



Leave a reply



Submit