Rails Modify Array in Loop - ruby-on-rails

I thought this would be a simple task, but I'm finding it difficult to make Rails do what I want.
I've got an array of dates.
So I thought that something like this would work:
def index
#datetimes = Books.all.map(&:checkouts).flatten.map(&:out_date)
#datetimes.each do |c|
c.to_date
end
end
Then I can just call this in my view:
%ul
-#datetimes.each do |c|
%li=c
How do I modify each key in the array? What am I missing here?
Thanks, so much for being nice to new, novice, and ignorant hobbyists like myself.

.each doesn't modify the caller. It simply loops through. You could change the controller action to just this:
#datetimes = Books.all.map(&:checkouts).flatten.map{|e| e.out_date.to_date}
You might also want to explore including :checkouts in your Books query to avoid N+1 queries. Or perhaps doing something like this maybe.
Checkout.where("book_id is not null").map{|e| e.out_date.to_date}

Related

Rails .where any field contains specific text

Is there a short-hand way of querying a Rails database for any record that has a field containing a specific piece of text? I know I could code every field with a .where("field_name LIKE ?", "my text"), but I have several fields and am wondering if there is a shorter way of doing this.
Thanks in advance.
I do not know of a framework-way to do so. You could code something using
my_attributes = YourModel.attributes
# delete attributes you do not need, like `id` etc.
# or just create an array with your desired attributes,
# whichever way is faster
queries = my_attributes.map { |attr| "#{attr} LIKE %insert_your_text_here%" }
# Do not use this if the text your looking for is provided by user input.
built_query = queries.join(" OR ")
YourModel.where(built_query)
This could bring you closer to your goal. Let me know if this makes sense to you.
edit: The answer https://stackoverflow.com/a/49458059/299781 mentions Ransack. That's a nice gem and takes the load off of you. Makes it easier, nicer and performs better :D
Glad you like this, but pay attention that you make your app open for sql injection, if you take user-input as the text you are looking for. (with this solution) Ransack would alleviate that.
class MyModel
scope :search_like, -> (field_name, search_string) {where("#{field_name} LIKE ?", "%#{search_string}%")}
end
then you can call it like:
MyModal.search_like('name', 'foobar')
UPDATE based on #holgar answer but beware if not indexed these searches can be slow on large data sets:
class MyModel
def self.multi_like(search_string)
my_attributes = [:first_name, :last_name] # probalby only use string fields here
queries = my_attributes.map { |attr| "#{attr} LIKE '%#{search_string}%'" }
where(queries.join(" OR "))
end
end
If you want full fledge text search based on params then you can use ransack gem

How do I add an `.order` invocation to a ActiveRecord collection that I retrieved?

I am new in this world of Rails. And I cannot get my head around this problem. How to get #microposts ordered by the date the micropost was created.
This is the line
#microposts = current_user.followeds.map(&:microposts).flatten
I only managed to order by date the 'followeds', but that is not what I am looking for. All the attempts I have made gave me errors, so I guess I am not aware of some syntax.
Any help is welcome!
Normally, you would add an order clause, as in:
Micropost.where(:written_by => current_user.followeds).order(:created_at)
The way you currently have this line structured doesn't permit that, however, since order is only available on ActiveRecord::Relations, and once you do map you no longer have a Relation available to chain on.
If that's the case, you'll want something like:
current_user.followeds.map(&:microposts).flatten.sort_by { |m| m.created_at }
I think you should try to approach this from another angle.
How about something like this:
#microposts = Micropost.where(author_id: current_user.followed_ids).order(:created_at).all
You might of course have to exchange author_id for whatever foreign key you have to identify what user a Micropost was written by.
If you want to reverse the posts (newest first), you just write order("created_at desc") instead.

Is there an idiomatic ruby/rails way of returning the first truthy mapped value?

I have an array of objects, some of which respond to :description, and I want to get the description from the first one with a truthy description. I could do this:
objects.detect{|o| o.try(:description)}.description
or this:
objects.map{|o| o.try(:description)}.detect{|o| o}
but the first isn't DRY (description is in there twice) and the second iterates through the whole array before finding the value. Is there anything in the ruby standard library, or in Rails' extensions to it, which would let me do something like this:
objects.detect_and_return{|o| o.try(:description)}
I know I could write it easily enough, but the standard libraries are big enough that I might not need to. Is there a function which works like my detect_and_return?
I haven't seen such a method, and the closest I found was a method capture_first which I found in the gem merb-cache. Seems they stumbled on the same problem and implemented this:
module Enumerable
def capture_first
each do |o|
return yield(o) || next
end
nil
end
end
You could also take a look at the Array and Enumerable methods in the Ruby facets library and see if you find something similar. Facets contains quite a lot of goodies, so you might get lucky.

Confused, with activerecord, how to I get all items?

I am trying this, but it doesn't seem to work:
users = User.find()
This page doesn't seem to tell me how to do this?
I'm sure I am missing something here!
Use User.find(:all) or its alias User.all().
If you just want to do something for each user, use find_each:
User.find_each do |user|
# Do something with "user"
end
This will be much more efficient than fetching all of the objects, since it will fetch in batches from the DB instead of grabbing everything at once and stuffing it all in memory somewhere.
Users.find(:all)
that should do the trick.

rails/activerecord search eager loaded associations

I have a simple find statement as such:
m = MyModel.find(1, :include => :my_children)
With m.mychildren being an Array; is there anyway to find a particular record from within the array without having to iterate over the entire thing. If I do mychildren.find(1), a new DB query is issues, which doesn't make sense, since they are all loaded already
It looks like there's a little Rails magic going on here. Where Enumerable#find is being overridden by ActiveRecord::Base#find on methods created for associations.
On the upside Enumerable#find is aliased to Enumerable#detect.
Unfortunately Enumerable#find/Enumerable#detect have significantly different syntax from ActiveRecord::Base#find.
So you can't just do mychildren.find(1), instead you've got to do mychildren.detect{|c| c.id == 1} if you want to avoid hitting the database again. You may also want to consider extending Array for a more DRY way of doing this.
class Array
def id_find id
self.detect{|element| element.id == id}
end
end
I'm not quite sure what your asking, but have you tried select:
m.mychildren.select{ |child| child == <<some_statement>> }
This won't hit the database assuming you've used the :include option as you stated in your question.
Alternatively, if you know the number of the child you want, you should be able to just use
m.mychildren[1]

Resources