I am interested in taking an existing hash and resuming the SHA256 encryption from that point. It seems doable in C++:
This is what I have tried:
irb(main):007:0> sha2 = Digest::SHA2.new => #<Digest::SHA2:256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855>
irb(main):008:0> sha2 << "string1"
=> #<Digest::SHA2:256 93fedde43203e0a76172135221b8636313635d7afff96a490ae9066330505d47>
irb(main):009:0> sha2 << "string2"
=> #<Digest::SHA2:256 bac09aa72e632e76c36e6c1c4e502b73c3da7fca68c475273dc5517815587cc4>
irb(main):010:0>
The above code updates the SHA2 digest object, in session. I want to start a new session...lets call it newshaw2 by assigning the same original hash as created when I passed String1 to sha2, as above.
I want to "make" newshaw2 as the object 93fedde43203e0a76172135221b8636313635d7afff96a490ae9066330505d47.
Setting it equal: newshaw2 = '93fedde43203e0a76172135221b8636313635d7afff96a490ae9066330505d47' just makes this a string object, not a digest.
Once, I have successfully assigned newsha2 with the hash then I would update with string2 to resume the hashing process.
It sounds like you want to be able to create a new Digest::SHA2 object and set its initial value to an existing hash without having to recalculate that hash.
For example, if you use Digest::SHA2 to calculate the hash for a 10 GB file, then is there any way to make a new Digest::SHA2 with that hash value but without having to rehash all 10 GB?
The answer is no, Digest does not have this capability. You can confirm that in one of a few ways. First, look at the public methods available on the object to see that there are no options to do this:
Digest::SHA256.new.public_methods - Object.public_methods
=> [:update, :digest_length, :block_length, :<<, :reset, :digest, :file, :base64digest, :base64digest!, :hexdigest!, :length, :digest!, :size, :hexdigest]
Then take a look at the source for initialize in Digest::SHA2 and you'll see there's no argument to set the initial state when calling new.
There may be something I missed because the source code for Digest is a little hard to follow because Digest is a mix of Ruby and C. For example, if you look at ext/digest/sha2/lib/sha2.rb you'll see that initialize is just calling Digest::SHA256. But there's no sha256.rb file to be found at ext/digest/sha2/lib. That's because it's defined by ext/digest/sha2/sha2init.c, with methods defined in ext/digest/sha2/sha2.c.
One other possible solution would be to marshal the data to a bytestream but that isn't possible with Digest::SHA2 objects:
Marshal.dump(Digest::SHA2.new)
TypeError: no _dump_data is defined for class Digest::SHA256
Someone else asked a similar question at Can I serialize a ruby Digest::SHA1 instance object? that was not answered. A possible solution for SHA1 digests was listed at https://gist.github.com/parameme/2280705, and you may be able to adapt it, but that gist is 9 years old and the Ruby version at the time was 1.9.3, so it's possible that it will not work on Ruby 2.7.
All my research indicates that you will have to recalculate the hash to get the object to the desired state.
Related
I am pretty new to Ruby and currently discovering its differences from Java, consider the following code snippet:
file = File.new('test.json', 'w')
hash = {}
hash['1234'] = 'onetwothreefour_str'
hash[1234] = 'onetwothreefour_num'
puts hash.to_json
file.write(hash.to_json)
file.close
str = File.read('test.json')
puts str
puts JSON.parse(str)
it outputs
{"1234":"onetwothreefour_str","1234":"onetwothreefour_num"}
{"1234":"onetwothreefour_str","1234":"onetwothreefour_num"}
{"1234"=>"onetwothreefour_num"}
so, after deserialization we have one less object in hash.
Now, the question - is it normal behaviour? I think that it is perfectly legal to store in hash keys of different types. If so, then shouldn't JSON.parse write to file keys as '1234' and 1234?
Just to be clear - I understand that it's better to have keys of the same type, I just saw that after restoring my object has them as strings instead of numbers.
Yes, ruby hashes can have keys of whatever type.
JSON spec, on the other hand, dictates that object keys must be strings, no other type allowed.
So that explains the output you observe: upon serializing, integer key is turned into a string, making it a duplicate of another key. When reading it back, duplicate keys are dropped (last one wins, IIRC). I'm pretty sure you would get the same behaviour if you tried to use that json from javascript.
I'm doing some update on other one's code and now I have a hash, it's like:
{"instance_id"=>"74563c459c457b2288568ec0a7779f62", "mem_quota"=>536870912, "disk_quota"=>2147483648, "mem_usage"=>59164.0, "cpu_usage"=>0.1, "disk_usage"=>6336512}
and I want to get the value by symbol as a key, for example: :mem_quota, but failed.
The code is like:
instance[:mem_usage].to_f
but it returns nothing. Is there any reason can cause this problem?
Use instance["mem_usage"] instead since the hash is not using symbols.
The other explanations are correct, but to give a broader background:
You are probably used to working within Rails where a very specific variant of Hash, called HashWithIndifferentAccess, is used for things like params. This particular class works like a standard ruby Hash, except when you access keys you are allowed to use either Symbols or Strings. The standard Ruby Hash, and generally speaking, Hash implementations in other languages, expect that to access an element, the key used for later access should be an object of the same class and value as the key used to store the object. HashWithIndifferentAccess is a Rails convenience class provided via the Active Support libraries. You are free to use them yourself, but they have first be brought in by requiring them.
HashWithIndifferentAccess just does the conversion for you at access time from string to symbol.
So, for your case, instance["mem_usage"].to_f should work.
You need HashWithIndifferentAccess.
require 'active_support/core_ext'
h1 = {"instance_id"=>"74563c459c457b2288568ec0a7779f62", "mem_quota"=>536870912,
"disk_quota"=>2147483648, "mem_usage"=>59164.0, "cpu_usage"=>0.1,
"disk_usage"=>6336512}
h2 = h1.with_indifferent_access
h1[:mem_usage] # => nil
h1["mem_usage"] # => 59164.0
h2[:mem_usage] # => 59164.0
h2["mem_usage"] # => 59164.0
Also, there are the symbolize_keys and stringify_keys options that may be of help. The method names are self-descriptive enough, I believe.
Clearly the keys of your hash are strings because they have double quotes around them. Therefore you will need to access the keys with instance["mem_usage"] or you will need to build a new hash with symbols as the keys first.
If you use Rails with ActiveSupport, then do use HashWithIndifferentAccess for flexibility in accessing hash with either string or symbol.
hash = HashWithIndifferentAccess.new({
"instance_id"=>"74563c459c457b2288568ec0a7779f62",
"mem_quota"=>536870912, "disk_quota"=>2147483648,
"mem_usage"=>59164.0,
"cpu_usage"=>0.1,
"disk_usage"=>6336512
})
hash[:mem_usage] # => 59164.0
hash["mem_usage"] # => 59164.0
I'm learning Ruby right now for the Rhodes mobile application framework and came across this problem: Rhodes' HTTP client parses JSON responses into Ruby data structures, e.g.
puts #params # prints {"body"=>{"results"=>[]}}
Since the key "body" is a string here, my first attempt #params[:body] failed (is nil) and instead it must be #params['body']. I find this most unfortunate.
Can somebody explain the rationale why strings and symbols have different hashes, i.e. :body.hash != 'body'.hash in this case?
Symbols and strings serve two different purposes.
Strings are your good old familiar friends: mutable and garbage-collectable. Every time you use a string literal or #to_s method, a new string is created. You use strings to build HTML markup, output text to screen and whatnot.
Symbols, on the other hand, are different. Each symbol exists only in one instance and it exists always (i.e, it is not garbage-collected). Because of that you should make new symbols very carefully (String#to_sym and :'' literal). These properties make them a good candidate for naming things. For example, it's idiomatic to use symbols in macros like attr_reader :foo.
If you got your hash from an external source (you deserialized a JSON response, for example) and you want to use symbols to access its elements, then you can either use HashWithIndifferentAccess (as others pointed out), or call helper methods from ActiveSupport:
require 'active_support/core_ext'
h = {"body"=>{"results"=>[]}}
h.symbolize_keys # => {:body=>{"results"=>[]}}
h.stringify_keys # => {"body"=>{"results"=>[]}}
Note that it'll only touch top level and will not go into child hashes.
Symbols and Strings are never ==:
:foo == 'foo' # => false
That's a (very reasonable) design decision. After all, they have different classes, methods, one is mutable the other isn't, etc...
Because of that, it is mandatory that they are never eql?:
:foo.eql? 'foo' # => false
Two objects that are not eql? typically don't have the same hash, but even if they did, the Hash lookup uses hash and then eql?. So your question really was "why are symbols and strings not eql?".
Rails uses HashWithIndifferentAccess that accesses indifferently with strings or symbols.
In Rails, the params hash is actually a HashWithIndifferentAccess rather than a standard ruby Hash object. This allows you to use either strings like 'action' or symbols like :action to access the contents.
You will get the same results regardless of what you use, but keep in mind this only works on HashWithIndifferentAccess objects.
Copied from : Params hash keys as symbols vs strings
I want to merge a value into a Thor option hash.
If I just use merge I get an error, HashWithIndifferentAccess
I have read the documentation but I have difficulties to understand how to get it to work. I guess I hope this question will help me to both find an answer on the question how to merge a value into this kind of hash and understand how to read documentation.
p options.inspect
#=> "{\"ruby\"=>\"/Users/work/.rbenv/versions/1.9.2-p290/bin/ruby\"}"
p options.merge!(:a => true)
#=> hash_with_indifferent_access.rb:26:in `[]=': can't modify frozen hash (RuntimeError)
The hash is frozen:
"Prevents further modifications to obj. A RuntimeError will be raised
if modification is attempted. There is no way to unfreeze a frozen
object."
You can copy options to a new hash (will be unfrozen) and modifying that instead.
new_options = options.dup
options = new_options
options.merge!(:a => "this will work now")
Or if you want it to be even briefer:
options=options.dup
options.merge!(:a => "this will work now")
The Thor library returns a frozen hash by default, so another option would be to modify the library to return unfrozen hashes, but I think the first solution is good enough.
Below is a link to the source code for Thor's parse function, you'll notice it freezes the "assigns" return hash prior to actually returning it (go to the bottom of the page, and under (Object) parse(args), click 'View Source'. The freezing is on line 83 of the source snippet.)
http://rubydoc.info/github/wycats/thor/master/Thor/Options
Here is a clever trick to enable hash autovivification in ruby (taken from facets):
# File lib/core/facets/hash/autonew.rb, line 19
def self.autonew(*args)
leet = lambda { |hsh, key| hsh[key] = new( &leet ) }
new(*args,&leet)
end
Although it works (of course), I find it really frustrating that I can't figure out how this two liner does what it does.
leet is put as a default value. So that then just accessing h['new_key'] somehow brings it up and creates 'new_key' => {}
Now, I'd expect h['new_key'] returning default value object as opposed to evaluating it. That is, 'new_key' => {} is not automatically created. So how does leet actually get called? Especially with two parameters?
The standard new method for Hash accepts a block. This block is called in the event of trying to access a key in the Hash which does not exist. The block is passed the Hash itself and the key that was requested (the two parameters) and should return the value that should be returned for the requested key.
You will notice that the leet lambda does 2 things. It returns a new Hash with leet itself as the block for handling defaults. This is the behaviour which allows autonew to work for Hashes of arbitrary depth. It also assigns this new Hash to hsh[key] so that next time you request the same key you will get the existing Hash rather than a new one being created.
It's also worth noting that this code can be made into a one-liner as follows:
def self.autonew(*args)
new(*args){|hsh, key| hsh[key] = Hash.new(&hsh.default_proc) }
end
The call to Hash#default_proc returns the proc that was used to create the parent, so we have a nice recursive setup here.
I talk about a similar case to this on my blog.
Alternatively, you might consider my xkeys gem. It's a module that you can use to extend arrays or hashes to facilitate nested access.
If you look for something that doesn't exist yet, you get a nil value (or another value or an exception if you prefer) without creating anything by looking. It can also append to the end of arrays.
You can opt to autovivify either hashes or arrays for integer keys (but just once for the entire structure).