Ruby Scratch PadRussell Bateman |
This is a scratch pad for Ruby stuff I'm trying out, want to remember how to do, etc.
# apt-get install ruby1.9.1-full # update-alternatives --config ruby # apt-get install build-essential # gem install rdoc-data # rdoc-data --install
Which is to say that it can happen and I don't yet know how to fix it.
This demonstrates blindly trusting that I could get JSON input working without knowing anything about how to set it up. After Googling for "ruby json input", I found stackoverflow.com: how to use json as input and clicked on the link in the answer. With an uncharacteristic lack of caution, I decided to go for it haphazardly and was richly rewarded.
Also, for converting names into symbols below, see what is the best way to convert a json [...] to ruby hash with symbol as key".
In this trial are the following statements, originally sequential in irb, that demonstrate...
~ $ sudo gem install json Building native extensions. This could take a while... Successfully installed json-1.8.0 1 gem installed Installing ri documentation for json-1.8.0... Installing RDoc documentation for json-1.8.0... ~ $ irb irb(main):001:0> require 'json' => true
irb(main):002:0> our_planet = '{ "earth" : true, "moon" : false }'
=> "{ \"earth\" : true, \"moon\" : false }"
irb(main):003:0> puts our_planet
{ "earth" : true, "moon" : false }
=> nil
irb(main):004:0> chez_nous = JSON.parse( our_planet, :symbolize_names => true ) => {:earth=>true, :moon=>false} irb(main):005:0> puts chez_nous[ :earth ] true => nil irb(main):006:0> puts chez_nous[ :moon ] false => nil
irb(main):007:0> if chez_nous[ :earth ] ; puts "It's true" ; end It's true => nil irb(main):008:0> if not chez_nous[ :earth ] ; puts "It's false" ; end => nil irb(main):009:0> if chez_nous[ :moon ] ; puts "It's false" ; end => nil irb(main):010:0> if not chez_nous[ :moon ] ; puts "It's false" ; end It's false => nil
irb(main):011:0> puts our_planet { "earth" : true, "moon" : false } => nil irb(main):012:0> puts our_planet[ :earth ] TypeError: can't convert Symbol into Integer from (irb):19:in `[]' from (irb):19 from /usr/bin/irb:12:in `' irb(main):013:0> puts our_planet[ "earth" ] earth => nil irb(main):014:0> puts our_planet[ "moon" ] moon => nil
I admit it, I'm not a Ruby guy. It's like when you're a hammer, everything you think you want to do looks like a nail to you no matter what it really is. In my case, all the sample code dealing with classes in Ruby was so simple and so "pre-initialized" as to be useless to me as I try to be more Ruby-esque and less Java-esque.
Turning to a question I posted on stackoverflow.com, here is what I mean. Suppose the following Java code and sample invocation thereof:
class Node { private Map< String, String > normal = new HashMap< String, String >(); public void addToNormal( String key, String value ) { this.normal.put( key, value ); } public String getNormal( String key ) { return this.normal.get( key ); } } ... Node node = new Node(); node.addToNormal( "city", "New Orleans" ); System.out.println( node.getNormal( "city" );
I want a class to imitate Chef's Node, whose definition simply isn't given, but it's the basis for what a node's doing when chef-client is run. Mostly, I want to experiment with it in Ruby so that I can write more correct code in my Chef recipe rather than put crap into the recipe that errors out with mysterious errors.
Here's what my new friend on stackoverflow gave me as a stepping off point:
class Node @normal = {} def initialize @normal = {} end def get_normal( n ) @normal[ :n ] end def add_normal( n, value ) @normal[ :n ] = value end end ... node = Node.new node.add_normal( :city, "New Orleans" ) puts node.get_normal( :city )
The code for both these examples prints "New Orleans" to the console, here's Ruby:
~ $ irb irb(main):001:0> class Node irb(main):002:1> @normal = {} irb(main):003:1> irb(main):004:1* def initialize irb(main):005:2> @normal = {} irb(main):006:2> end irb(main):007:1> irb(main):008:1* def get_normal( n ) irb(main):009:2> @normal[ :n ] irb(main):010:2> end irb(main):011:1> irb(main):012:1* def add_normal( n, value ) irb(main):013:2> @normal[ :n ] = value irb(main):014:2> end irb(main):015:1> end => nil irb(main):016:0> node = Node.new => #<Node:0x00000001edd270 @normal={}> irb(main):017:0> node.add_normal( :city, "New Orleans" ) => "New Orleans" irb(main):018:0> puts node.get_normal( :city ) New Orleans => nil
Still, this is wrong-headed. I really need to replicate how node in Chef works. So, along those lines, here's a pretty thing:
Finally. Ruby is not taking her love to town on me every time. Observe this code. The first line is the definition of node's normal field in the Chef node. No, I don't know what it means yet. The subsequent lines imitate what happens when Chef reads in attributes from attribute or node files:
normal = Hash.new { | h, k | h[ k ] = Hash.new( &h.default_proc ) } normal[ :mongodb ][ :package ] = "mongodb-10gen" normal[ :mongodb ][ :replicaset ][ :name ] = "myreplicas" normal[ :mongodb ][ :replica_1 ][ :hostname ] = "16.86.193.101" normal[ :mongodb ][ :replica_1 ][ :port ] = 37017 normal[ :mongodb ][ :replica_2 ][ :hostname ] = "16.86.193.102" normal[ :mongodb ][ :replica_2 ][ :port ] = 37018 normal[ :mongodb ][ :replica_3 ][ :hostname ] = "16.86.193.103" normal[ :mongodb ][ :replica_3 ][ :port ] = 37019
...and its behavior, answers in bold. This is just the stuff I need to ferret out in my MongoDB recipe that's going to execute commands in the MongoDB shell when it erects a replica set. Ruby's unless keyword is just what I need too:
irb(main):010:0> puts normal[ :mongodb ][ :package ] mongodb-10gen => nil irb(main):011:0> puts normal[ :mongodb ][ :replicaset ][ :name ] myreplicas => nil irb(main):012:0> puts normal[ :mongodb ][ :replica_1 ][ :hostname ] 16.86.193.101 => nil irb(main):013:0> puts normal[ :mongodb ][ :replica_1 ][ :port ] 37017 => nil irb(main):014:0> puts normal[ :mongodb ][ :replica_2 ][ :hostname ] 16.86.193.102 => nil irb(main):015:0> puts normal[ :mongodb ][ :replica_2 ][ :port ] 37018 => nil irb(main):016:0> puts normal[ :mongodb ][ :replica_3 ][ :hostname ] 16.86.193.103 => nil irb(main):017:0> puts normal[ :mongodb ][ :replica_3 ][ :port ] 37019 => nil irb(main):018:0> unless normal[ :mongodb ][ :replica_4 ] irb(main):019:1> puts "I did it." irb(main):020:1> else irb(main):021:1* puts "I didn't do it." irb(main):022:1> end I didn't do it. => nil
Yeah, there is no "replica_4", that's the point. By the way, here's what this looks like in the JSON node file in Chef:
{ "normal": { "mongodb" : { "replicanode" : { "hostname" : 16.86.193.100 }, "replicanode" : { "port" : 37017 } } "replica_1" : { "hostname" : 16.86.193.100, "port" : 37017 }, "replica_2" : { "hostname" : 16.86.193.101, "port" : 37018 }, "replica_3" : { "hostname" : 16.86.193.102, "port" : 37019 }, "arbiter_1" : { "hostname" : 16.86.193.103, "port" : 37016 } }, "name": "db01", "override": { }, "default": { }, "json_class": "Chef::Node", "automatic": { }, "run_list": [ "recipe[apt]", "recipe[mongodb]", "recipe[mongodb::replica]", "recipe[mongodb::post-replicas]", "role[install_database_node]", "role[install_replica_node]", "role[install_post_replica_node]" ], "chef_type": "node" }
I'm writing a Chef recipe getting ready to issue commands to the MongoDB shell. I'm constructing a command to configure a replica set based on recorded information from JSON in a node file.
# --------------------------------------------------------------------------+ # This simulates 'node' as Chef will set it up in chef-client run. # | node = Hash.new { | h, k | h[ k ] = Hash.new( &h.default_proc ) } # | # | # Contributed by attributes/default.rb. # | node[ :mongodb ][ :package ] = "mongodb-10gen" # | # | # Contributed by nodes/db01.json, assuming using it as the "tie-up" # | # node that's going to be run after all others are set up. # | node[ :mongodb ][ :replicanode ][ :hostname ] = "16.86.193.100" # | node[ :mongodb ][ :replicanode ][ :port ] = 37016 # | node[ :mongodb ][ :replicaset ][ :name ] = "myreplicas" # | node[ :replica_1 ] [ :hostname ] = "16.86.193.100" # | node[ :replica_1 ] [ :port ] = 37017 # | node[ :replica_2 ] [ :hostname ] = "16.86.193.101" # | node[ :replica_2 ] [ :port ] = 37018 # | node[ :replica_3 ] [ :hostname ] = "16.86.193.102" # | node[ :replica_3 ] [ :port ] = 37019 # | node[ :arbiter_1 ] [ :hostname ] = "16.86.193.103" # | node[ :arbiter_1 ] [ :port ] = 37016 # | # --------------------------------------------------------------------------+ id = 0 found = false replica_members = "" if !node[ :replica_1 ].empty? found = true hostname = node[ :replica_1 ][ :hostname ] port = node[ :replica_1 ][ :port ] replica_members += "{ _id:%d, host:%s:%s }" % [ id, hostname, port ] id += 1 end if !node[ :replica_2 ].empty? if found replica_members += ", " end found = true hostname = node[ :replica_2 ][ :hostname ] port = node[ :replica_2 ][ :port ] replica_members += "{ _id:%d, host:%s:%s }" % [ id, hostname, port ] id += 1 end if !node[ :replica_3 ].empty? if found replica_members += ", " end found = true hostname = node[ :replica_3 ][ :hostname ] port = node[ :replica_3 ][ :port ] replica_members += "{ _id:%d, host:%s:%s }" % [ id, hostname, port ] id += 1 end if !node[ :replica_4 ].empty? if found replica_members += ", " end hostname = node[ :replica_4 ][ :hostname ] port = node[ :replica_4 ][ :port ] replica_members += "{ _id:%d, host:%s:%s }" % [ id, hostname, port ] id += 1 end replicaset_name = node[ :mongodb ][ :replicaset ][ :name ] replica_id = "id : \"%s\"" % replicaset_name configuration = "config = { " + replica_id + ", members: [" + replica_members + "] }" # Print out what has happened... print "replicaset_name = ", replicaset_name puts print " replica_id = ", replica_id puts print "replica_members = ", replica_members puts puts " configuration = ", configuration
And the console output appears thus, though I've re-formatted it a) to put it on multiple lines and b) to demonstrate its JSON correctness (MongoDB uses JSON as its shell language). I added color and text face too.
replicaset_name = myreplicas replica_id = id : "myreplicas" replica_members = { _id:0, host:16.86.193.100:37017 }, { _id:1, host:16.86.193.101:37018 }, { _id:2, host:16.86.193.102:37019 } configuration = config = { id : "myreplicas", members: [ { _id:0, host:16.86.193.100:37017 }, { _id:1, host:16.86.193.101:37018 }, { _id:2, host:16.86.193.102:37019 } ] }