How to use Nokogiri to find all class elements in a webpage? - ruby-on-rails

I'm using Nokogiri to parse a webpage.
I'm able to succefully get the 1st bookTitle class element, but I'm trying to get all of the titles in an array.
But I'm struggling to get all the other elements. What's the best way to get them all?
doc = Nokogiri::HTML(URI.open("https://www.goodreads.com/search?utf8=%E2%9C%93&q=barack+obama&search_type=books&search%5Bfield%5D=author"))
puts doc.at_css('.bookTitle').content
# returns Dreams from My Father: A Story of Race and Inheritance
puts doc.xpath('//*[#class="bookTitle"]').size;
# returns 20, which is correct

at_css (or just at) returns first matched element (Nokogiri::XML::Element)
You need css method. It returns collection of matched elements (Nokogiri::XML::NodeSet)
doc.css('.bookTitle')
Probably you need to map through such collection
doc.css('.bookTitle').lazy.map(&:text).map(&:strip).to_a

Related

Override gem method in rails

I want to let users add interests -TV shows in this case-, and to make sure they type a correct tv show, I'm going to search imdb first and let them select one of the returning titles.
I found this gem https://github.com/ariejan/imdb which is doing almost what I need. If I search for "The vampire diaries", it will return it and 200 extra matches.
I went through the gem and I found that he does the querying part here https://github.com/ariejan/imdb/blob/master/lib/imdb/search.rb.
def self.query(query)
open("http://akas.imdb.com/find?q=#{CGI.escape(query)};s=tt")
end
That query basically uses this link http://akas.imdb.com/find?q= and returns everything that can find given the input - movies, tv shows, episodes. Now I found a more advanced query which uses type and some other params. So I could actually return only 4 results in that case instead of 250. All I have to do is to replace that query with http://www.imdb.com/search/title?title=The%20Vampire%20Diaries&title_type=tv_series.
How do I override that search method?
You can re-open the class to override the method:
class Imdb::Search
def self.query(query)
# your custom logic here
end
end
Note that you can call super(query) in your version to get the result of the original.
You can use class_eval and put it in a decorators folder
app/decorators/imdb/search_decorator.rb
class Imdb::Search.class_eval do
def self.query(query)
end
end

How to move a record within a collectionproxy in rails

In my rails app I have a collectionproxy that is an array (I think) of records. I want to take a record from the middle of the array and put it at the beginning. I don't know the position of the item, but I can find it using an attribute on user model. It seems like some methods aren't available to use on a collectionproxy.
I tried:
user_images = user.images
user_images.insert(0, user_images.delete(user.images.find_by_id(user.primary_image_id))
but got an error that I gave 2 arguments but it expected 1. I'm guessing because the insert method that is used on arrays isnt the same method that is used on collectionproxies. What's the best way to do this?
Edit: I just need this to display the items in the view, I don't need to change at the database level.
As very few methods are available for collection proxy, first change the collection proxy to array and then manipulate it.
Here is the code to do so,
user_images = user.images.to_a //converted collection to array
user_images.unshift(user_images.detect{ |image| image.id == user.primary_image_id}).uniq //used unshift
puts user_images
The magic we have done here is detect the images that's the primary image of the user and unshift into array
The unshift adds the object in the beginning.
Now remove the duplicated oject which is already there at someplace by using uniq.
That's it your required objects comes first into the array and you can use this in the view as active record collection is used.
It looks like there's currently no way to add to the beginning of a CollectionProxy. The prepend and sort methods were removed. Here's the API for the deprecated prepend method:
http://apidock.com/rails/v4.2.1/ActiveRecord/Associations/CollectionProxy/prepend
You could re-think this slightly, and use the append or << operator along with delete to copy elements to the end of the collection, and delete them from the middle. It's not ideal, but it might be a workaround until you have a better solution.

acts-as-taggable-on tag owner bug

I have code such as:
Parent Class
->acts_as tagger
Child Class
->acts_as_taggable
If I print 'child.tag_list' I get the correct result e.g. 'A'. If I print 'parent.owned_tags' I get:
#<ActsAsTaggableOn::Tag id: 7, name: "A">
Any ideas why the format for the parents owned tags appears this way?
I've only been using acts-as-taggable-on for a week, but here's my two cents...
I think it's because typically when you get and set tags on a taggable object, it's easiest to do it with a string. When we want to know the tags we own, we want the actual objects, through the taggings, in a collection so we can do what we want. In your case we'd map that array with something like parent.owned_tags.map{|tag| tag.name}.join(', ') I think.
Does that make sense? It'd be nice to add a method so we could do parent.owned_tag_list. Maybe that exists already for outputting a string?!

Can I access the collection an instance method was called on in ruby on rails

I'm working on implementing a search form in a ruby on rails application. The general idea is to use form_tag to submit the search fields (via params) to a search function in the model of the class I'm trying to search. The search function will then iterate through each of the params and execute a scoping function if the name of the function appears in params.
The issue is that when I call the search on a collection like so:
#calendar.reservations.search({:search_email => "test"})
I don't know how to refer to the collection of #calendar.reservations from within the search function.
Additionally I'm confused as to why #calendar.reservations.search(...) works, but Reservations.all.search gives me an error saying you can't call an instance method on an array.
I've got the details of the search method over here: https://gist.github.com/783964
Any help would be greatly appreciated!
I don't know how to refer to the
collection of #calendar.reservations
from within the search function.
If you use self (or Reservation, it's the same object) inside the classmethod, you will access the records with the current scope, so in your case you will see only the reservations of a particular calendar.
[edit] I looked at you search function, and I think what you want is:
def self.search(search_fields)
search_fields.inject(self) do |scope, (key, value)|
scope.send(key, value)
end
end
Additionally I'm confused as to why
#calendar.reservations.search(...)
works, but Reservations.all.search
gives me an error saying you can't
call an instance method on an array.
#calendar.reservations does not return a standard array but a (lazy) AssociationCollection, where you can still apply scopes (and classmethods as your filter). On the other hand Reservation.all returns a plain array, so you cannot execute search there (or any scope, for that matter).
You don't really need a search method at all, as far as I can tell.
Simply use where:
#calendar.reservations.where(:search_email => 'test')
I would strongly encourage you to look at the MetaSearch GEM by Ernie Miller. It handles the kind of thing you're working on very elegantly and is quite easy to implement. I suspect that your view code would almost accomplish what the GEM needs already, and this would take care of all your model searching needs very nicely.
Take a look and see if it will solve your problem. Good luck!
Reservation.all.search doesn't work because it returns all the results as an array, while Reservation.where(..) returns an ActiveRecord object (AREL). Reservation.all actually fetches the results instead of just building the query further, which methods like where, limit etc do.

Accessing object values that have reserved keywords as names in Rails

I'm accessing the Amazon AWS API using the ruby-aaws gem, but without going to much into details of the API or the gem, I think my problem is more of a general nature.
When I query the API I will end up with "object array", let's call it item, containing the API response.
I can easily access the data in the array, e.g. puts item.item_attributes.artist.to_s
Now the API returns attributes whose identifier are reserved words in Rails, e.g. format or binding.
So doing this:
puts item.item_attributes.format.to_s will return method not found
while
puts item.item_attributes.binding.to_s will return some object hash like #<Binding:0xb70478e4>.
I can see that there are values under that name when doing
puts item.item_attributes.to_yaml
Snippet from the resulting yaml show artist and binding:
--- !seq:Amazon::AWS::AWSArray
- !ruby/object:Amazon::AWS::AWSObject::ItemAttributes
__val__:
artist: !seq:Amazon::AWS::AWSArray
- !ruby/object:Amazon::AWS::AWSObject::Artist
__val__: Summerbirds in the Cellar
binding: !seq:Amazon::AWS::AWSArray
- !ruby/object:Amazon::AWS::AWSObject::Binding
__val__: Vinyl
This was probably a very detailed explanation with a very simple solution, but I can't seem to find the solution.
edit
Finally found it. I guess it is because it is an array of objects, duh...
puts item.item_attributes[0].binding.to_s
You may be able to access the individual attributes by using [] instead of the method name (which is probably provided using method_missing anyway).
So, item.item_attributes[:artist].to_s may return what you want. If it doesn't it would be worth trying 'artist' as the key instead.
Finally found it. I guess it is because it is an array of objects, duh...
puts item.item_attributes[0].binding.to_s

Resources