Why the object returned is Enumerable? - ruby

I don't understand why do I get an Enumerable instead of an object. When I run this code :
- #posts.each do |post|
= Comment.find(id: post.id).title
I've got this error :
undefined method `title' for #Enumerator: Comment:find({:id=>1})>
If I check in the console I get also Enumerator :
[2] pry(#<Sinatra::Application>)> Comment.find 1
=> #<Enumerator: ...>
I just want to have my object like #<Comment #id=1 #content="great" #post_id=1>
I'm working with Sinatra and Datamapper.

The query you're looking for is:
Comment.first(id: post.id).title
Which is a short version of:
Comment.all(id: post.id).first.title
There is no find in Datamapper (that I know of). What you're actually seeing is the result of Ruby's Enumerable#find: http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-find, which must be a part of Datamapper objects.

Related

Ruby parsing XML: no implicit conversion of String into Integer

I'm working on a Sinatra application that pulls in a list of dates through an XML file and then creates an hash of all the dates.
I'm running into a strange issue that's happening when I'm pulling the id and assigning it to a variable.
The error I'm getting is:
no implicit conversion of String into Integer and it's being thrown on the event_date_id = event_date["date_id"] line. I have almost identical method in my code and it's working just fine. When I puts event_date['date_id'] it gives me the correct numerical date_id.
Just in case it helps, the class of the event_date['date_id'] is REXMLUtiliyNodeString, same as the id field in the other method. If I try to event_date['date_id'].to_i it gives breaks at that point.
def get_dates(event_id)
url = "some_url_to_some_xml"
puts '==================='
puts "Pulling in #{url}"
puts '==================='
date_xml = Crack::XML.parse(open(url))
dates = {}
date_xml['document']['date'].each do | event_date |
event_date_id = event_date['date_id']
single_date = {
'date_id' => event_date_id,
'date_start' => event_date['datestart'],
'date_end' => event_date['dateend'],
'date_live' => event_date['live'],
'time_start' => event_date['timestart'],
'time_end' => event_date['timestart'],
'date_available' => event_date['date_available']
}
dates.merge!( event_date_id => single_date )
end
return dates
end
Here is the xml format:
<document>
<date>
<date_id>881908</date_id>
<live>y</live>
<datestart>2017-08-14</datestart>
<dateend>2017-08-15</dateend>
<timestart>13:00</timestart>
<timeend>0:00</timeend>
<date_available>10000</date_available>
</date>
<document>
I have a feeling this is something really simple but I'm wracking my brains trying to figure it out. If anyone could shed some light on this, I'd definitely appreciate it.
Edit 1: When I run the code in irb, it does indeed work without error. Somewhere, somehow it seems Sinatra, Rack or Shotgun are getting in the way.
I have found the cause of my issue. It only occurs when I'm parsing an xml file with one entry for date/event whatever.
This question explains the exact issue I was having and the answer includes a work around that worked for me.
if(!date_xml['document']['date'].is_a?(Array))
date_xml['document']['date'] = [ date_xml['document']['date'] ]
end
This message occurs when you try to use a string index to look up a value in an array.
2.0.0p353 :001 > results = [""]
=> [""]
2.0.0p353 :002 > results["x"]
TypeError: no implicit conversion of String into Integer
from (irb):2:in `[]'
from (irb):2
from /home/jeff/.rvm/rubies/ruby-2.0.0-p353/bin/irb:12:in `<main>'
Since arrays can only be accessed by integer indexes, Ruby attempts to transform your key name into an integer, and fails because it doesn't know what number should be used to represent arbitrary string data. Hence the message "no implicit conversion of String into Integer".
If you're experiencing this, the answer is to fix your code so that it doesn't try to access an array like a hash. If you're importing from XML or JSON data on the assumption that the key desired is always there and will always be imported, but you're still getting this, your assumption is wrong; the data is not formatted as expected. Either fix the data or fix the code to handle the differing format.
I randomly stumbled upon what may be a better answer to this question, although I am very inexperienced so I think it needs to be verified.
I had an identical issue and saw that much of the trouble seems to be because I was returning a somewhat confusing array of one object.
Once I added ".first" to my query, I was able to retrieve my intended attribute.
response = File.open('ncaa_bb_schedule.xml')
doc = Nokogiri::XML(response)
doc.remove_namespaces!
doc.xpath('//game').each do |game|
h = game.xpath('home').first
p h['id']
end
But my original query, shown here
response = File.open('ncaa_bb_schedule.xml')
doc = Nokogiri::XML(response)
doc.remove_namespaces!
doc.xpath('//game').each do |game|
h = game.xpath('home')
p h['id']
end
end
was giving me the same error: "TypeError: no implicit conversion of String into Integer." Hope that helps somebody as it is much shorter than the aforementioned workaround.

http_request.set_form_data : Getting undefined method map for string for the json parameter

I am trying to pass a json representation to a set_form_data and am running into following error:
undefined method `map' for "{\"first_name\":\"bill\",\"last_name\":\"gates\"}":String
Here is how I create the request:
Net::HTTP::Post.new(uri.request_uri).set_form_data({"first_name" => "steve","last_name" => "jobs"}.to_json)
Anything I am missing ?
The error comes from the line:
/jruby/jruby-1.6.2/lib/ruby/1.9/net/http.rb:1593:in `set_form_data'
I used request.body=form_data instead of request.set_form_data and that worked.
However I dont know for sure why set_form_data did not work.
Marking this as an answer, since that's the one that worked for me so far.
set_form_data wants a hash, not a json, so skip the to_json and it should work better.
ie:
some_data = {:foo => :bar, :meh => :muh}
Net::HTTP::Post.new(uri.request_uri).set_form_data(some_data)

Extract a value from an OpenStruct Ruby object

I get the following Ruby object returned (from a query to the Google Analytics API using the garb gem, comes from the sample call shown on the README.md there, Exits.results(profile, :filters => {:page_path.eql => '/'}))
> data.results
=> [#<OpenStruct page_path="/", exits="3706", pageviews="10440">]
I'd to extract the pageviews value (10440), but cannot figure out how to do it. I see that my object, data.results is class array of length 1, but data.first is class OpenStruct with a return value that looks almost identical:
irb(main):140:0> data.results.class
=> Array
irb(main):141:0> data.results.length
=> 1
irb(main):142:0> data.first
=> #<OpenStruct page_path="/", exits="3706", pageviews="10440">
irb(main):143:0> data.first.class
=> OpenStruct
while data itself seems to be a custom return type called ResultsSet:
irb(main):144:0> data.class
=> Garb::ResultSet
irb(main):145:0> data
=> #<Garb::ResultSet:0x00000002411070 #results=[#<OpenStruct page_path="/", exits="3706", pageviews="10440">], #total_results=1, #sampled=false>
irb(main):146:0>
Lots of data structures, but no idea how to get my desired value out. I gathered OpenStruct was related to a hash, so I thought data.first["pageviews"] would do it,
NoMethodError: undefined method `[]' for #<OpenStruct page_path="/", exits="3706", pageviews="10440">
from (irb):146
from /usr/bin/irb:12:in `<main>'
Meanwhile data.first.keys returns nil. No idea how to get my data out, (short of converting the length-1 array, data.results to a string and parsing with grep, which seems crazy. Any ideas?
Please try this:
data.first.pageviews

Rails 3.2 iterate through an array

I have an array that looks like this:
#shipment_products
[
{"old_qty_shipped"=>"324", "product_id"=>"1", "qty_shipped"=>"12443"}
{"old_qty_shipped"=>"4343423", "product_id"=>"3", "qty_shipped"=>"321344"}
{"old_qty_shipped"=>"23", "product_id"=>"4", "qty_shipped"=>"321"}
]
I want to end up being able to do something like this
#shipment_products.each do |p|
Product.adjust_qtys(p.old_qty_shipped, p.qty_shipped, p.product_id)
end
I'm getting the following error
NoMethodError (undefined method `qty_shipped' for #<ActiveSupport::HashWithIndifferentAccess:0x007f>)
The array is not quite in the right format to do this. I need to find a way to be able to iterate through the key/values and extract the attributes so I can call the method I created in the model. Any ideas?
Check following code.
#shipment_products = [ {"old_qty_shipped"=>"324", "product_id"=>"1", "qty_shipped"=>"12443"}, {"old_qty_shipped"=>"4343423", "product_id"=>"3", "qty_shipped"=>"321344"} , {"old_qty_shipped"=>"23", "product_id"=>"4", "qty_shipped"=>"321"}]
#shipment_products.each do |p|
Product.adjust_qtys(p['old_qty_shipped'], p['qty_shipped'], p['product_id'])
end

How to get to_json work on BSON::Code object as well in mongo-ruby-driver

I am trying 'mongo-ruby-driver' for some project. It's working fine except when I call to_json on mongo object. It gives well formed json but it's not converting BSON::Code into readable value i resulting JSON.
Instead of showing code text, it show something like
#<BSON::Code:0x00000100af6fa8>
Did anyone tried it. Any help id highly appreciated.
UPDATE
here is some code snippet:
#records is variable that contains Array of MongoDB documents in hash.
#records.to_json
When I call to_json on it it gives everything as expected. except for the key that contain BSON::Code (means javascript code). for example consider following doc:
{
"_id" : "contains",
"value" : function( obj, target ) { return obj.indexOf(target) != -1; };
}
Querying same doc from ruby gives output like:
{
"_id"=>"contains",
"value"=><BSON::Code:2160165280 #data="function( obj, target ) { return obj.indexOf(target) != -1; };" #scope="{}">
}
and calling to_json on this gives following:
{"_id":"contains","value":"#<BSON::Code:0x00000100b54658>"}
this is what the problem is. Instead of getting actual code for 'value' key I am getting ruby object as string.
Calling code method on BSON::Code we can get it converted into code. But for that I need to loop mongo docs, check values for each key, calling code on it if it is an object of BSON::Code and then assigning it back to key. And at last we can call to_json on it. But I dont want this much overhead. I need to_json itself should take care of it.
The serializer that is iterating over the attributes of the objects in the array is probably calling the to_s method which would cause the output you are seeing.
You can either monkey patch the BSON::Code class to include a to_s method which calls inspect (the method that produces the output you want) or modify the serializer to detect when it encounters a BSON::Code instance and call inspect on it rather than to_s.
The code to mokey patch the BSON::Code class would look like this:
module BSON
class Code
def to_s
inspect
end
end
end
This would have the same behavior as inspect. If you just wanted the code you could monkey patch this in:
module BSON
class Code
def to_s
#code
end
end
end
I might make this the default behavior for to_s in the driver but for now just include that in your code and it should work like a champ.
Which Mongo object, do you mean the module? If you could, please post the code and what you are trying to do.
The only to_json I see in the driver is BSON::ObjectId#to_json which (from the docs) is described as a method that does the following:
Convert to MongoDB extended JSON format. Since JSON includes type information, but lacks an ObjectId type, this JSON format encodes the type using an $oid key.
I get the same results when I use it:
1.9.3-p0 :001 > require 'mongo'
=> true
1.9.3-p0 :002 > BSON::ObjectId
=> BSON::ObjectId
1.9.3-p0 :003 > BSON::ObjectId.new()
=> BSON::ObjectId('4f17350eadd361e91d000001')
1.9.3-p0 :004 > BSON::ObjectId.new().to_json
=> "{\"$oid\": \"4f173512add361e91d000002\"}"
BSON ( http://bsonspec.org/ ) is a binary representation of JSON ( http://www.json.org/ ) . JSON is not meant to be used to describe functions, as it is meant to be portable. Functions in this manner are not portable to other systems. So there is no way to serialize it. There is some hacks defined here that may get you what you need, but ultimately, BSON/JSON may be a weird tool for the job if your trying to serialize functions.

Resources