Nokogiri, different results of xpath in JRuby - ruby

I am getting different results from the same xpath expression in nokogiri when using ruby and jruby,
In ruby the following xpath expression returns a node while in jruby it returns a nodeset:
parent = node.xpath("./ancestor::node()[name(.) = 'div' or name(.) = 'p'][1]")
Has anybody else noticed similar behaviour?

There seems to be a difference between how libxml2 (used in the MRI Nokogiri) and whatever library the pure-Java version uses in how they handle predicates that match only one node. You should report this to the nokogiri-talk mailing list.

Related

ArgumentError when upgrading from Ruby 2.7 to 3.0 - related to separation of positional and keyword arguments?

I am trying to upgrade gems from ruby 2.7.0 to 3.0.0. I have tried to read and plan ahead for what roadblocks I'd hit, like the Separation of positional and keyword arguments.
I have updated two gems smoothly, but I have started to upgrade a third and, while testing the 3.0 update with RSpec, have run into the following error:
ArgumentError:
wrong number of arguments (given 1, expected 0; required keyword: children)
I am trying it instantiate a Class through a Factory. The stack trace is pointing back to the arguments of this function:
# rubocop:disable Style/KeywordParametersOrder
def initialize(parent_obj: nil, children:, **attributes)
#attributes = attributes
#parent = parent_obj
#children_json = children
end
# rubocop:enable Style/KeywordParametersOrder
Though I don't think it is significant, I have included the rubocop dis/enable lines just in case they are important.
The Factory is calling the class in this fashion:
data = {:some_data=>"foo", :some_name=>"bar", :children=>[]}
Long::Class::Name.new(data)
Now, when I pry into the code between data and Long::Class::Name.new(data) and instantiate my own Long::Class::Name like below:
Long::Class::Name.new(children: children, attributes: data)
it results in a successful creation without the ArgumentError.
I think to myself, "Cool, I'll just update the Factory's Class call to this new format and re-run the tests." After doing this, I am still getting the same ArgumentError as above.
I believe this is an issue with positional/keyword arguments like I linked above, but I am having trouble seeing how I can correct this. Besides the link above, I have also looked into Ruby 3 Keyword Arguments, as well as Hash and Keyword Coercion and Ruby 3 Changes. I believe I am facing the "Unforeseen Consequences" portion of that last link.
I have also looked at this Stack Overflow issue about ArgumentError after updating from Ruby 2.7 to Ruby 3.0 and tried to understand how I could use the first portion of the first answer to help me with my issue (disregarding the Update related to a PR).
Any thoughts on how can I dispel or work around this error? I have many gems I need to update and I am sure this will not be the last time I see this error. Any help would be greatly appreciated. Let me know if more information is needed.
When you do this…
data = {:some_data=>"foo", :some_name=>"bar", :children=>[]}
Long::Class::Name.new(data)
…you are calling the method with one positional argument (a hash containing the keys :some_data, :some_name, and :children) and no keyword argument.
If you instead were to call it like this…
Long::Class::Name.new(**data)
…you would be calling it with no positional argument and three keyword arguments.
This is called the "double splat" and was introduced for exactly your usecase, turning a hash into keyword arguments.

Which method could I use to parse difficult string?

I do a mysql query in ruby, and it gives me a string like that:
O:8:"stdClass":4:{s:7:"updates";a:1:{i:0;O:8:"stdClass":10:{s:8:"response";s:6:"latest";s:8:"download";s:65:"https://downloads.wordpress.org/release/fr_FR/wordpress-4.2.4.zip";s:6:"locale";s:5:"fr_FR";s:8:"packages";O:8:"stdClass":5:{s:4:"full";s:65:"https://downloads.wordpress.org/release/fr_FR/wordpress-4.2.4.zip";s:10:"no_content";b:0;s:11:"new_bundled";b:0;s:7:"partial";b:0;s:8:"rollback";b:0;}s:7:"current";s:5:"4.2.4";s:7:"version";s:5:"4.2.4";s:11:"php_version";s:5:"5.2.4";s:13:"mysql_version";s:3:"5.0";s:11:"new_bundled";s:3:"4.1";s:15:"partial_version";s:0:"";}}s:12:"last_checked";i:1439805713;s:15:"version_checked";s:5:"4.2.4";s:12:"translations";a:0:{}}
I need to parse this part: s:6:"latest" to know if it's the latest version or an upgrade is available.
Which method could I use to do it in ruby? I just started this language and it's my first object oriented language, I only do C habitually.
This is string serialized with PHP. There is no simple, and stable way to parse that in Ruby. Regular expressions will get you only so far and will be fragile, since the serialized string is not regular.
There is, however, a gem php-serialize that allows unserialize and serialize in the PHP-way from Ruby. If you look into the gem, you'll see how complex the parser really is.
With that gem you could do:
require 'php-serialize'
string = 'O:8:"stdClass":4:{s:7:"updates";a:1:{i:0;O:8:"stdClass":10:{s:8:"response";s:6:"latest";s:8:"download";s:65:"https://downloads.wordpress.org/release/fr_FR/wordpress-4.2.4.zip";s:6:"locale";s:5:"fr_FR";s:8:"packages";O:8:"stdClass":5:{s:4:"full";s:65:"https://downloads.wordpress.org/release/fr_FR/wordpress-4.2.4.zip";s:10:"no_content";b:0;s:11:"new_bundled";b:0;s:7:"partial";b:0;s:8:"rollback";b:0;}s:7:"current";s:5:"4.2.4";s:7:"version";s:5:"4.2.4";s:11:"php_version";s:5:"5.2.4";s:13:"mysql_version";s:3:"5.0";s:11:"new_bundled";s:3:"4.1";s:15:"partial_version";s:0:"";}}s:12:"last_checked";i:1439805713;s:15:"version_checked";s:5:"4.2.4";s:12:"translations";a:0:{}}'
wp_settings = PHP.unserialize(string)
puts wp_settings.updates.inspect

Is ruby's rand method supposed to accept Range objects?

I've only seen something like rand(1..5) work in MRI ruby 1.9.3 (haven't tried 1.9.2). Jruby doesn't support it, even in 1.9 mode - it raises a TypeError.
Even ruby-doc doesn't mention that Ranges are supported. What's the official behavior?
UPDATE
Well as the answers and comments point out, only 1.9.3 supports it. Jruby is only at 1.9.2.
The docs do say that it is supported
If max is Range, returns a pseudorandom number where range.member(number) == true.
It's also in the 1.9.3 changelog
Verbatim Copy-paste from docs
If max is Range, returns a pseudorandom number where range.member(number) == true.
Or else converts max to an integer using max1 = max.to_i.abs.
so, yes. it is supported from ruby 1.9.3

Rubinius: how to generate enumerator as the official way?

I have this simple code to generate a lazy array:
lazy_arr = Enumerator.new { |y|
i = 1
loop {
y << i
i+=1
}
}
p lazy_arr.take(5)
In official Ruby 1.9.3, the output is [1,2,3,4,5], which is what I want.
But in Rubinius, it gives error and tells me cannot find Enumerator constant.
So I looked it up, and find Enumerator defined in Enumerable module instead of kernel, and when it is generated, it needs a few arguments in the brackets:
http://rubydoc.info/github/evanphx/rubinius/master/Enumerable/Enumerator
I tried to change Enumerator.new to Enumerable::Enumerator.new, or include Enumerable, neither works because it needs more arguments.
How can I do the example above in Rubinius? Is there any way around to make the code work in both official and Rubinius?
You're using Rubinius in 1.8 mode, which doesn't have Enumerator in the global namespace. Please use Rubinius in 1.9 mode and the example works fine then. You can use 1.9 by passing -X19 when starting Rubinius, or setting RBXOPT=-X19 for example.
It's also possible to make 1.9 mode the default with configure during compile time.
Sounds like a bug/missing class in Rubinius. Open up an issue on github and it will get added. Or dig in and send a pull request!

Summing the values of an array of hashes in Ruby

I am having trouble figuring out the elegant way to add an array of hashes
[{:a=>1,:b=>2,:c=>3},{:a=>1,:b=>2,:c=>3},{:a=>1,:b=>2,:c=>3}]
should return
[{:a=>3,:b=>6,:c=>9}]
I know it would probably involve mapping/reducing, but I can't figure out the right syntax, doesn't help that ruby-doc dot org doesn't match my version
I am using 1.8.7
array.inject{|x,y| x.merge(y){|_,a,b| a + b}}
(verified on Ruby 1.8.7)

Resources