I simplified my RSpec comparison problem down to this:
expect(DetailObjective.all.limit(0)).to eq(DetailObjective.none)
which returns a failed result:
expected: #<ActiveRecord::Relation []>
got: #<ActiveRecord::Relation []>
(compared using ==)
which seems like they DO match? How do I compare these empty Relations? I do not want to compare something fake like be_empty as that tells nothing about the comparative structures, just content.
DetailObjective.all.limit(0) and DetailObjective.none both return a new instance of ActiveRecord::Relation, but eq returns false when the objects are not identical -- that is to say, they are not the same object. You can see this easily by assigning the output of each method to a local variable and checking its object_id -- they will be different.
I do not want to compare something fake like be_empty ...
Not sure what you mean by 'fake'. Collections are complex types -- comparing them is not like comparing integers. Are they the same when the have the same members? When the members are in the same order? When the members are the same class?
Comparing one empty collection with another empty collection doesn't seem to make much sense ... can you be explicit about what you are trying to accomplish here?
Related
I get the following input hash in my ruby code
my_hash = { include: 'a,b,c' }
(or)
my_hash = { includes: 'a,b,c' }
Now I want the fastest way to get 'a,b,c'
I currently use
def my_includes
my_hash[:include] || my_hash[:includes]
end
But this is very slow because it always checks for :include keyword first then if it fails it'll look for :includes. I call this function several times and the value inside this hash can keep changing. Is there any way I can optimise and speed up this? I won't get any other keywords. I just need support for :include and :includes.
Caveats and Considerations
First, some caveats:
You tagged this Rails 3, so you're probably on a very old Ruby that doesn't support a number of optimizations, newer Hash-related method calls like #fetch_values or #transform_keys!, or pattern matching for structured data.
You can do all sorts of things with your Hash lookups, but none of them are likely to be faster than a Boolean short-circuit when assuming you can be sure of having only one key or the other at all times.
You haven't shown any of the calling code, so without benchmarks it's tough to see how this operation can be considered "slow" in any general sense.
If you're using Rails and not looking for a pure Ruby solution, you might want to consider ActiveModel::Dirty to only take action when an attribute has changed.
Use Memoization
Regardless of the foregoing, what you're probably missing here is some form of memoization so you don't need to constantly re-evaluate the keys and extract the values each time through whatever loop feels slow to you. For example, you could store the results of your Hash evaluation until it needs to be refreshed:
attr_accessor :includes
def extract_includes(hash)
#includes = hash[:include] || hash[:includes]
end
You can then call #includes or #includes= (or use the #includes instance variable directly if you like) from anywhere in scope as often as you like without having to re-evaluate the hashes or keys. For example:
def count_includes
#includes.split(?,).count
end
500.times { count_includes }
The tricky part is basically knowing if and when to update your memoized value. Basically, you should only call #extract_includes when you fetch a new Hash from somewhere like ActiveRecord or a remote API. Until that happens, you can reuse the stored value for as long as it remains valid.
You could work with a modified hash that has both keys :include and :includes with the same values:
my_hash = { include: 'a,b,c' }
my_hash.update(my_hash.key?(:include) ? { includes: my_hash[:include] } :
{ include: my_hash[:includes] })
#=> {:include=>"a,b,c", :includes=>"a,b,c"}
This may be fastest if you were using the same hash my_hash for multiple operations. If, however, a new hash is generated after just a few interrogations, you might see if both the keys :include and :includes can be included when the hash is constructed.
I checked using File.open('file.txt').class and File.open('file.txt').readlines.class and the former one returns File and the latter returns Array.
I understand this difference, but if I do something like:
File.open('file.txt').collect {|l| l.upcase}
=== File.open('file.txt').readlines.collect {|l| l.upcase}
it returns true. So are there any differences between the two objects when each item in the object is being passed to a block as an argument?
And also, I was assuming that the arguments that are passed to the block in both expressions are both a line in the file as a string which makes the comparison return true, is that correct? If so, how do I know what kind of argument will be passed to the block when I write the code? Do I have to check something like the documentation or the source code?
For example, I know how
['a','b','c'].each_with_index { |num, index| puts "#{index + 1}: #{num}" }
works and take this for granted. But how do I know the first argument should be each item in the array and the second the index, instead of the reverse?
Hope that makes sense, thanks!
Get comfortable doing some Ruby introspection in irb.
irb(main):001:0> puts File.ancestors.inspect
[File, IO, File::Constants, Enumerable, Object, Kernel, BasicObject]
This result shows us classes the File class inherits from and that includes the methods of class Enumerable. So what object is returned from File.readlines? An Array I think, let's check.
ri File.readlines
IO.readlines(name, sep=$/ [, open_args]) -> array
IO.readlines(name, limit [, open_args]) -> array
IO.readlines(name, sep, limit [, open_args]) -> array
This may be overkill, but we can verify Enumerable methods exists within an Array.
irb(main):003:0> puts Array.ancestors.inspect
[Array, Enumerable, Object, Kernel, BasicObject]
I'll try to make this response as compact as possible.
The second question - if you operate on objects which come from standard library you may always refer to their documentation in order to be 100% sure what arguments to expect when passing a block. For instance, multiple times you will be using methods like each, select, map (etc ...), on different data structures (Array, Hash, ...). Before you get used to them you may find all information about them in docs for example: http://ruby-doc.org/core-2.2.0/Array.html
If you are operating on non core data structures (for example a class which comes from gem you include, you should always browse it's documentation or sources on Github).
The first question. Result may be the same, when using different methods, but on deeper level there may be some differences. As far as your case is based on files. Reading file content may be processed in two ways. First - read everything into memory and operate on array of string, and the second - read file lines sequentially which may last longer but will not reserve as much memory.
In Enumerable#each_with_index the arguments are in the same order as they are in the method name itself, first the element, then the index. Same thing with each_with_object.
Let's say we have a Ruby method like this:
# Pseudocode
def get(globbed)
a_items = Dir.glob(globbed)
a_items.length == 1 ? a_items.first : a_items
end
The method is meant to return a String containing information about the items in question. If there are many items, it will return an Array. The ternary makes it so that if there is only one item, it just returns that String.
What is the best practice here? Should such a method always return an Array even if there is only one item?
It should always return an array. Returning different things means that whatever method that calls this method would also have to have a condition. That is not good. Whenever you can get rid of a condition, you should. A condition should only be used as a last resort.
As a real example, the jQuery library built on top of JavaScript has the notion of selectors, expressed in the form $(...). This can result in multiple matching dom objects, or a single one. But jQuery always returns an array even if the matched dom object is one. That makes things simple.
It's always about use cases. You have to define what's the responsibility of that method and then decide what makes sense to do.
In this specific case, I would say that, unless there isn't any specific reason to return different types, you should choose the way that is simpler, both to test and to read.
Always returning an array in this case means clearer method interface:
"The method returns an array with the directory content"
instead of the more convoluted
"The method returns an array of directory content if there more than
one object, otherwise return the single object."
So, clarity first of all.
And: testing would result easier. The cyclomatic complexity of the routine is less.
There are cases where the uniformity of return types can't be fulfilled. Just think of the Array method index: it wouldn't be possible to distinguish between "object not found" and "index 0" if the practice here was applied.
Conclusion: here I don't see any reason why to make the method more complex by distinguishing the two cases, so.. KISS.
Hi, ruby provides block, yield and iterator to permit easy array or hash treatment. And it's a good practice to use the same code for one or several numbers of element. Exemple :
a_items.each { |element| file_treatment(element) }
Regards.
I've only been learning the deep parts of Ruby for a few months so apologies if this is a bit of a dumb question. I'm attempting to recursively iterate through an Array that may contain nested Arrays and as such I need to check the type of the current element. I have the following code as a small test:
arr = [ 1..2, [3..4, [5..6]], [7..8, [9..10]] ]
arr.each do |node|
p node.class
p node.instance_of? Array
end
When I run it, I get the following output:
Range
false
Array
false
Array
false
I expected the last two to return True, given I have an Array containing a Range and two nested Arrays.
What's even weirder, is if I write the following:
node.class.name == "Array"
It returns True, as it should.
What's happening here?
Ruby Version: MRI 1.9.3-p194
Note: I eventually realised that this was occurring due to the way I namespace my code using modules to avoid code-collision, like so, but also verify object identity in a naughty way:
module MyProg
class MyClass
attr_reader :my_array
def initialize(size)
#my_array = Array.new(size)
end
end
end
MyProg::MyClass.new
Doing this isolates your code but has the downfall of causing all class lookups to be resolved starting from under your namespace. This means that in the above example, my_array.class would actually resolve to MyProg::Array instead of the global Array class.
If you namespace like this and you still want to use this method, you can remedy it by using the double-colon global identifier before the class to force Ruby to begin lookup from the global namespace:
arr.is_a? ::Array
arr.is_a? ::String
Given Ruby's Duck Typing abilities however (and for better code maintenance later on), you should really be testing the behaviour of the object as-per Peter's suggestion below. As such I'm marking his answer as correct for some excellent help given to a learner!
I wrote another answer, but one major question is - why are you doing this? Why not, instead, just call flatten on the array so you just get the entries? Or, check for the behavior of the objects instead. You might need to give more detail about what you require.
You really mean is_a?, which is a more general test to see if the node is of type Array, rather than a specific instance of the specific Array class that you mention. See here for more details. But if you just use is_a? everything will make sense.
I ran your code and got these results.
Range
false
Array
true
Array
true
I'm running ruby 1.9.3p125
Ok, this might sound like a silly question, but I'm having all sorts of problems with it and I'm sure there has to be an easy way to do this:
How do you check in ruby if a variable has length?
If I use #dvds.length? I get an error undefined method length for ... when it doesn't have a length.
Basically I've got some searches that mostly retrieve an ActiveRecord:Relation, but one or two simply return one entry since they search by id. I have things like:
#total = #dvds.length
To show how many we've found.
When #dvds does not contain any length, I get an error for undefined method. I did something like:
if #dvds.class == 'ActiveRecord:Relation'
to get around one error, but this is horrible and prone to more errors.
There has got to be a Ruby way of checking whether a variable has length or not that is a lot prettier and easier no?
Ideally I'm looking for something like:
#total = #dvds.length if #dvds.length
Or similar.
Thanks.
You can check whether an object responds to specified method using Object#respond_to? method:
obj.respond_to? :length # true or false