cassandra_client 0.1 → 0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data.tar.gz.sig +0 -0
- data/CHANGELOG +2 -0
- data/Manifest +3 -2
- data/README +9 -12
- data/Rakefile +1 -0
- data/cassandra_client.gemspec +4 -4
- data/conf/storage-conf.xml +12 -15
- data/lib/cassandra_client.rb +3 -2
- data/lib/cassandra_client/cassandra_client.rb +170 -0
- data/lib/cassandra_client/helper.rb +57 -0
- data/lib/cassandra_client/ordered_hash.rb +23 -22
- data/lib/cassandra_client/safe_client.rb +18 -0
- data/test/cassandra_client_test.rb +85 -81
- metadata +10 -6
- metadata.gz.sig +0 -0
- data/lib/cassandra_client/client.rb +0 -65
- data/lib/cassandra_client/table.rb +0 -202
    
        data.tar.gz.sig
    CHANGED
    
    | Binary file | 
    
        data/CHANGELOG
    CHANGED
    
    
    
        data/Manifest
    CHANGED
    
    | @@ -2,10 +2,11 @@ CHANGELOG | |
| 2 2 | 
             
            conf/cassandra.in.sh
         | 
| 3 3 | 
             
            conf/log4j.properties
         | 
| 4 4 | 
             
            conf/storage-conf.xml
         | 
| 5 | 
            -
            lib/cassandra_client/ | 
| 5 | 
            +
            lib/cassandra_client/cassandra_client.rb
         | 
| 6 | 
            +
            lib/cassandra_client/helper.rb
         | 
| 6 7 | 
             
            lib/cassandra_client/ordered_hash.rb
         | 
| 8 | 
            +
            lib/cassandra_client/safe_client.rb
         | 
| 7 9 | 
             
            lib/cassandra_client/serialization.rb
         | 
| 8 | 
            -
            lib/cassandra_client/table.rb
         | 
| 9 10 | 
             
            lib/cassandra_client.rb
         | 
| 10 11 | 
             
            LICENSE
         | 
| 11 12 | 
             
            Manifest
         | 
    
        data/README
    CHANGED
    
    | @@ -19,7 +19,9 @@ The public certificate for this gem is here[http://rubyforge.org/frs/download.ph | |
| 19 19 |  | 
| 20 20 | 
             
            This is an alpha release and does not yet support the full Thrift API. 
         | 
| 21 21 |  | 
| 22 | 
            -
            Cassandra is a rapidly moving target; this library is currently tested  | 
| 22 | 
            +
            Cassandra is a rapidly moving target; this library is currently tested against {Cassandra trunk revision 789419}[http://blog.evanweaver.com/files/cassandra/cassandra-r789419.tar.bz2]. 
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            The Github source repository is {here}[http://github.com/fauna/cassandra_client/]; patches and contributions are very welcome.
         | 
| 23 25 |  | 
| 24 26 | 
             
            == Installation
         | 
| 25 27 |  | 
| @@ -35,32 +37,27 @@ Require the library: | |
| 35 37 |  | 
| 36 38 | 
             
              require 'cassandra_client'
         | 
| 37 39 |  | 
| 38 | 
            -
            Connect to a server:
         | 
| 40 | 
            +
            Connect to a server and keyspace:
         | 
| 39 41 |  | 
| 40 | 
            -
              client = CassandraClient.new("127.0.0.1")
         | 
| 42 | 
            +
              client = CassandraClient.new('Twitter', "127.0.0.1")
         | 
| 41 43 |  | 
| 42 | 
            -
            Get a keyspace:
         | 
| 43 | 
            -
             | 
| 44 | 
            -
              users = client.table('Users')  
         | 
| 45 | 
            -
             | 
| 46 44 | 
             
            Insert into a column family. You can insert a CassandraClient::OrderedHash, or a regular Hash, if order doesn't matter:
         | 
| 47 45 |  | 
| 48 | 
            -
               | 
| 46 | 
            +
              client.insert(:Users, "5", {'screen_name' => "buttonscat"})  
         | 
| 49 47 |  | 
| 50 48 | 
             
            Insert into a super column family:
         | 
| 51 49 |  | 
| 52 | 
            -
               | 
| 50 | 
            +
              client.insert(:UserRelationships, "5", {"user_timeline" => {"1" => ""}})
         | 
| 53 51 |  | 
| 54 52 | 
             
            Query a super column:
         | 
| 55 53 |  | 
| 56 | 
            -
              timeline =  | 
| 54 | 
            +
              timeline = client.get(:UserRelationships, "5", "user_timeline")
         | 
| 57 55 |  | 
| 58 56 | 
             
            The returned result will always be a CassandraClient::OrderedHash.
         | 
| 59 57 |  | 
| 60 | 
            -
            See CassandraClient::Table for more methods.
         | 
| 58 | 
            +
            See CassandraClient and CassandraClient::Table for more methods.
         | 
| 61 59 |  | 
| 62 60 | 
             
            == Reporting problems
         | 
| 63 61 |  | 
| 64 62 | 
             
            The Github issue tracker is {here}[http://github.com/fauna/cassandra_client/issues]. If you have problems with Cassandra itself, please use the {cassandra-user mailing list}[http://mail-archives.apache.org/mod_mbox/incubator-cassandra-user/].
         | 
| 65 63 |  | 
| 66 | 
            -
            Patches and contributions are very welcome. Please note that contributors are required to assign copyright for their additions to Twitter, Inc.
         | 
    
        data/Rakefile
    CHANGED
    
    | @@ -7,6 +7,7 @@ Echoe.new("cassandra_client") do |p| | |
| 7 7 | 
             
              p.rubygems_version = ">= 0.8"
         | 
| 8 8 | 
             
              p.dependencies = ['json']
         | 
| 9 9 | 
             
              p.ignore_pattern = /^(data|vendor\/cassandra|cassandra-r789419|vendor\/thrift)/
         | 
| 10 | 
            +
              p.rdoc_pattern = /^(lib|bin|tasks|ext)|_types.rb|_constants.rb|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
         | 
| 10 11 | 
             
              p.url = "http://blog.evanweaver.com/files/doc/fauna/cassandra_client/"
         | 
| 11 12 | 
             
              p.docs_host = "blog.evanweaver.com:~/www/bax/public/files/doc/"
         | 
| 12 13 | 
             
            end
         | 
    
        data/cassandra_client.gemspec
    CHANGED
    
    | @@ -2,16 +2,16 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            Gem::Specification.new do |s|
         | 
| 4 4 | 
             
              s.name = %q{cassandra_client}
         | 
| 5 | 
            -
              s.version = "0. | 
| 5 | 
            +
              s.version = "0.2"
         | 
| 6 6 |  | 
| 7 7 | 
             
              s.required_rubygems_version = Gem::Requirement.new(">= 0.8") if s.respond_to? :required_rubygems_version=
         | 
| 8 8 | 
             
              s.authors = ["Evan Weaver"]
         | 
| 9 9 | 
             
              s.cert_chain = ["/Users/eweaver/p/configuration/gem_certificates/evan_weaver-original-public_cert.pem"]
         | 
| 10 | 
            -
              s.date = %q{2009-07- | 
| 10 | 
            +
              s.date = %q{2009-07-06}
         | 
| 11 11 | 
             
              s.description = %q{A Ruby client for CassandraDB.}
         | 
| 12 12 | 
             
              s.email = %q{}
         | 
| 13 | 
            -
              s.extra_rdoc_files = ["CHANGELOG", "lib/cassandra_client/ | 
| 14 | 
            -
              s.files = ["CHANGELOG", "conf/cassandra.in.sh", "conf/log4j.properties", "conf/storage-conf.xml", "lib/cassandra_client/ | 
| 13 | 
            +
              s.extra_rdoc_files = ["CHANGELOG", "lib/cassandra_client/cassandra_client.rb", "lib/cassandra_client/helper.rb", "lib/cassandra_client/ordered_hash.rb", "lib/cassandra_client/safe_client.rb", "lib/cassandra_client/serialization.rb", "lib/cassandra_client.rb", "LICENSE", "README", "vendor/gen-rb/cassandra_constants.rb", "vendor/gen-rb/cassandra_types.rb"]
         | 
| 14 | 
            +
              s.files = ["CHANGELOG", "conf/cassandra.in.sh", "conf/log4j.properties", "conf/storage-conf.xml", "lib/cassandra_client/cassandra_client.rb", "lib/cassandra_client/helper.rb", "lib/cassandra_client/ordered_hash.rb", "lib/cassandra_client/safe_client.rb", "lib/cassandra_client/serialization.rb", "lib/cassandra_client.rb", "LICENSE", "Manifest", "quickstart.sh", "Rakefile", "README", "test/cassandra_client_test.rb", "vendor/gen-rb/cassandra.rb", "vendor/gen-rb/cassandra_constants.rb", "vendor/gen-rb/cassandra_types.rb", "cassandra_client.gemspec"]
         | 
| 15 15 | 
             
              s.homepage = %q{http://blog.evanweaver.com/files/doc/fauna/cassandra_client/}
         | 
| 16 16 | 
             
              s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Cassandra_client", "--main", "README"]
         | 
| 17 17 | 
             
              s.require_paths = ["lib"]
         | 
    
        data/conf/storage-conf.xml
    CHANGED
    
    | @@ -20,7 +20,7 @@ | |
| 20 20 | 
             
                <!--======================================================================-->
         | 
| 21 21 | 
             
                <!-- Basic Configuration                                                  -->
         | 
| 22 22 | 
             
                <!--======================================================================-->
         | 
| 23 | 
            -
                <ClusterName> | 
| 23 | 
            +
                <ClusterName>Test</ClusterName>
         | 
| 24 24 |  | 
| 25 25 | 
             
                <!-- Tables and ColumnFamilies                                            
         | 
| 26 26 | 
             
                     Think of a table as a namespace, not a relational table.
         | 
| @@ -29,22 +29,19 @@ | |
| 29 29 | 
             
                     There is an implicit table named 'system' for Cassandra internals.
         | 
| 30 30 | 
             
                -->
         | 
| 31 31 | 
             
                <Tables>
         | 
| 32 | 
            -
                  <Table Name=" | 
| 33 | 
            -
                      <ColumnFamily ColumnSort="Name" Name=" | 
| 34 | 
            -
                      <ColumnFamily ColumnSort="Name" Name=" | 
| 35 | 
            -
                      <ColumnFamily ColumnType="Super" ColumnSort="Name" Name=" | 
| 36 | 
            -
                      <ColumnFamily ColumnSort="Time" Name=" | 
| 32 | 
            +
                  <Table Name="Twitter">
         | 
| 33 | 
            +
                      <ColumnFamily ColumnSort="Name" Name="Users" />
         | 
| 34 | 
            +
                      <ColumnFamily ColumnSort="Name" Name="UserAudits" />
         | 
| 35 | 
            +
                      <ColumnFamily ColumnType="Super" ColumnSort="Name" Name="UserRelationships" />
         | 
| 36 | 
            +
                      <ColumnFamily ColumnSort="Time" Name="Usernames" />
         | 
| 37 | 
            +
                      <ColumnFamily ColumnSort="Time" Name="Statuses" />
         | 
| 38 | 
            +
                      <ColumnFamily ColumnSort="Name" Name="StatusAudits" />
         | 
| 39 | 
            +
                      <ColumnFamily ColumnType="Super" ColumnSort="Name" Name="StatusRelationships" />  
         | 
| 37 40 | 
             
                  </Table>
         | 
| 38 41 |  | 
| 39 | 
            -
                  <Table Name=" | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
                      <ColumnFamily ColumnType="Super" ColumnSort="Name" Name="relationships" />  
         | 
| 43 | 
            -
                  </Table>
         | 
| 44 | 
            -
             | 
| 45 | 
            -
                  <Table Name="Blogs">      
         | 
| 46 | 
            -
                    <ColumnFamily ColumnSort="Time" Name="posts"/>
         | 
| 47 | 
            -
                    <ColumnFamily ColumnSort="Time" Name="comments"/>
         | 
| 42 | 
            +
                  <Table Name="Multiblog">      
         | 
| 43 | 
            +
                    <ColumnFamily ColumnSort="Time" Name="Blogs"/>
         | 
| 44 | 
            +
                    <ColumnFamily ColumnSort="Time" Name="Comments"/>
         | 
| 48 45 | 
             
                  </Table>
         | 
| 49 46 | 
             
                </Tables>
         | 
| 50 47 |  | 
    
        data/lib/cassandra_client.rb
    CHANGED
    
    | @@ -5,10 +5,11 @@ require 'thrift' | |
| 5 5 |  | 
| 6 6 | 
             
            HERE = File.expand_path(File.dirname(__FILE__))
         | 
| 7 7 |  | 
| 8 | 
            -
            require "#{HERE}/cassandra_client/ | 
| 9 | 
            -
            require "#{HERE}/cassandra_client/ | 
| 8 | 
            +
            require "#{HERE}/cassandra_client/helper"
         | 
| 9 | 
            +
            require "#{HERE}/cassandra_client/safe_client"
         | 
| 10 10 | 
             
            require "#{HERE}/cassandra_client/serialization"
         | 
| 11 11 | 
             
            require "#{HERE}/cassandra_client/ordered_hash"
         | 
| 12 | 
            +
            require "#{HERE}/cassandra_client/cassandra_client"
         | 
| 12 13 |  | 
| 13 14 | 
             
            $LOAD_PATH << "#{HERE}/../vendor/gen-rb"
         | 
| 14 15 | 
             
            require 'cassandra'
         | 
| @@ -0,0 +1,170 @@ | |
| 1 | 
            +
            class CassandraClient
         | 
| 2 | 
            +
              include Helper  
         | 
| 3 | 
            +
              class AccessError < StandardError; end
         | 
| 4 | 
            +
              
         | 
| 5 | 
            +
              MAX_INT = 2**31 - 1  
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              attr_reader :keyspace, :host, :port, :quorum, :serialization, :transport, :client, :schema
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              # Instantiate a new CassandraClient and open the connection.
         | 
| 10 | 
            +
              def initialize(keyspace, host = '127.0.0.1', port = 9160, quorum = 1, serialization = CassandraClient::Serialization::JSON)
         | 
| 11 | 
            +
                @keyspace = keyspace
         | 
| 12 | 
            +
                @host = host
         | 
| 13 | 
            +
                @port = port
         | 
| 14 | 
            +
                @quorum = quorum
         | 
| 15 | 
            +
                @serialization = serialization
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                extend(@serialization)
         | 
| 18 | 
            +
                
         | 
| 19 | 
            +
                @transport = Thrift::BufferedTransport.new(Thrift::Socket.new(@host, @port))
         | 
| 20 | 
            +
                @transport.open    
         | 
| 21 | 
            +
                @client = Cassandra::SafeClient.new(
         | 
| 22 | 
            +
                  Cassandra::Client.new(Thrift::BinaryProtocol.new(@transport)), 
         | 
| 23 | 
            +
                  @transport)
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                keyspaces = @client.getStringListProperty("tables")
         | 
| 26 | 
            +
                unless keyspaces.include?(@keyspace)
         | 
| 27 | 
            +
                  raise AccessError, "Keyspace #{@keyspace.inspect} not found. Available: #{keyspaces.inspect}"
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
                    
         | 
| 30 | 
            +
                @schema = @client.describeTable(@keyspace)
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
                
         | 
| 33 | 
            +
              def inspect
         | 
| 34 | 
            +
                "#<CassandraClient:#{object_id}, @keyspace=#{keyspace.inspect}, @schema={#{
         | 
| 35 | 
            +
                  schema.map {|name, hash| ":#{name} => #{hash['type'].inspect}"}.join(', ')
         | 
| 36 | 
            +
                }}, @host=#{host.inspect}, @port=#{port}, @quorum=#{quorum}, @serialization=#{serialization.name}>"
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              ## Write
         | 
| 40 | 
            +
              
         | 
| 41 | 
            +
              # Insert a row for a key. Pass a flat hash for a regular column family, and 
         | 
| 42 | 
            +
              # a nested hash for a super column family.
         | 
| 43 | 
            +
              def insert(column_family, key, hash, timestamp = now)
         | 
| 44 | 
            +
                column_family = column_family.to_s    
         | 
| 45 | 
            +
                insert = is_super(column_family) ? :insert_super : :insert_standard
         | 
| 46 | 
            +
                send(insert, column_family, key, hash, timestamp)
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
              
         | 
| 49 | 
            +
              private
         | 
| 50 | 
            +
             | 
| 51 | 
            +
              def insert_standard(column_family, key, hash, timestamp = now)
         | 
| 52 | 
            +
                mutation = Batch_mutation_t.new(
         | 
| 53 | 
            +
                  :table => @keyspace, 
         | 
| 54 | 
            +
                  :key => key, 
         | 
| 55 | 
            +
                  :cfmap => {column_family => hash_to_columns(hash, timestamp)})
         | 
| 56 | 
            +
                @client.batch_insert(mutation, @quorum)
         | 
| 57 | 
            +
              end 
         | 
| 58 | 
            +
             | 
| 59 | 
            +
              def insert_super(column_family, key, hash, timestamp = now)
         | 
| 60 | 
            +
                mutation = Batch_mutation_super_t.new(
         | 
| 61 | 
            +
                  :table => @keyspace, 
         | 
| 62 | 
            +
                  :key => key, 
         | 
| 63 | 
            +
                  :cfmap => {column_family => hash_to_super_columns(hash, timestamp)})
         | 
| 64 | 
            +
                @client.batch_insert_superColumn(mutation, @quorum)
         | 
| 65 | 
            +
              end 
         | 
| 66 | 
            +
              
         | 
| 67 | 
            +
              public
         | 
| 68 | 
            +
              
         | 
| 69 | 
            +
              ## Delete
         | 
| 70 | 
            +
              
         | 
| 71 | 
            +
              # Remove the element at the column_family:key:super_column:column 
         | 
| 72 | 
            +
              # path you request.
         | 
| 73 | 
            +
              def remove(column_family, key, super_column = nil, column = nil, timestamp = now)
         | 
| 74 | 
            +
                column_family = column_family.to_s
         | 
| 75 | 
            +
                column_family += ":#{super_column}" if super_column
         | 
| 76 | 
            +
                column_family += ":#{column}" if column
         | 
| 77 | 
            +
                @client.remove(@keyspace, key, column_family, timestamp, @quorum)
         | 
| 78 | 
            +
              end
         | 
| 79 | 
            +
              
         | 
| 80 | 
            +
              # Remove all rows in the column family you request.
         | 
| 81 | 
            +
              def clear_column_family!(column_family)
         | 
| 82 | 
            +
                get_key_range(column_family).each do |key| 
         | 
| 83 | 
            +
                  remove(column_family, key)
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              # Remove all rows in the keyspace
         | 
| 88 | 
            +
              def clear_keyspace!
         | 
| 89 | 
            +
                @schema.keys.each do |column_family|
         | 
| 90 | 
            +
                  clear_column_family!(column_family)
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
              end
         | 
| 93 | 
            +
              
         | 
| 94 | 
            +
              ## Read
         | 
| 95 | 
            +
             | 
| 96 | 
            +
              # Count the elements at the column_family:key:super_column path you 
         | 
| 97 | 
            +
              # request.
         | 
| 98 | 
            +
              def count_columns(column_family, key, super_column = nil)
         | 
| 99 | 
            +
                column_family = column_family.to_s
         | 
| 100 | 
            +
                column_family += ":#{super_column}" if super_column
         | 
| 101 | 
            +
                @client.get_column_count(@keyspace, key, column_family)
         | 
| 102 | 
            +
              end
         | 
| 103 | 
            +
              
         | 
| 104 | 
            +
              # Return a list of single values for the elements at the
         | 
| 105 | 
            +
              # column_family:key:super_column:column path you request.
         | 
| 106 | 
            +
              def get_columns(column_family, key, super_columns, columns = nil)
         | 
| 107 | 
            +
                column_family = column_family.to_s
         | 
| 108 | 
            +
                get_slice_by_names = (is_super(column_family) && !columns) ? :get_slice_super_by_names : :get_slice_by_names
         | 
| 109 | 
            +
                if super_columns and columns
         | 
| 110 | 
            +
                  column_family += ":#{super_columns}" 
         | 
| 111 | 
            +
                  columns = Array(columns)
         | 
| 112 | 
            +
                else
         | 
| 113 | 
            +
                  columns = Array(super_columns)
         | 
| 114 | 
            +
                end
         | 
| 115 | 
            +
                    
         | 
| 116 | 
            +
                hash = columns_to_hash(@client.send(get_slice_by_names, @keyspace, key, column_family, columns))
         | 
| 117 | 
            +
                columns.map { |column| hash[column] }
         | 
| 118 | 
            +
              end
         | 
| 119 | 
            +
                    
         | 
| 120 | 
            +
              # Return a hash (actually, a CassandraClient::OrderedHash) or a single value 
         | 
| 121 | 
            +
              # representing the element at the column_family:key:super_column:column 
         | 
| 122 | 
            +
              # path you request.
         | 
| 123 | 
            +
              def get(column_family, key, super_column = nil, column = nil, offset = -1, limit = 100)
         | 
| 124 | 
            +
                column_family = column_family.to_s
         | 
| 125 | 
            +
                column_family += ":#{super_column}" if super_column
         | 
| 126 | 
            +
                column_family += ":#{column}" if column          
         | 
| 127 | 
            +
                
         | 
| 128 | 
            +
                # You have got to be kidding
         | 
| 129 | 
            +
                if is_super(column_family)
         | 
| 130 | 
            +
                  if column
         | 
| 131 | 
            +
                    load(@client.get_column(@keyspace, key, column_family).value)
         | 
| 132 | 
            +
                  elsif super_column
         | 
| 133 | 
            +
                    columns_to_hash(@client.get_superColumn(@keyspace, key, column_family).columns)
         | 
| 134 | 
            +
                  else
         | 
| 135 | 
            +
                    columns_to_hash(@client.get_slice_super(@keyspace, key, "#{column_family}:", offset, limit))
         | 
| 136 | 
            +
                  end
         | 
| 137 | 
            +
                else
         | 
| 138 | 
            +
                  if super_column
         | 
| 139 | 
            +
                    load(@client.get_column(@keyspace, key, column_family).value)
         | 
| 140 | 
            +
                  elsif is_sorted_by_time(column_family)
         | 
| 141 | 
            +
                    result = columns_to_hash(@client.get_columns_since(@keyspace, key, column_family, 0))
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                    # FIXME Hack until get_slice on a time-sorted column family works again
         | 
| 144 | 
            +
                    result = OrderedHash[*flatten_once(result.to_a[offset, limit])] if offset > -1
         | 
| 145 | 
            +
                    result
         | 
| 146 | 
            +
                  else
         | 
| 147 | 
            +
                    columns_to_hash(@client.get_slice(@keyspace, key, "#{column_family}:", offset, limit))
         | 
| 148 | 
            +
                  end 
         | 
| 149 | 
            +
                end
         | 
| 150 | 
            +
              rescue NotFoundException
         | 
| 151 | 
            +
                is_super(column_family) && !column ? {} : nil
         | 
| 152 | 
            +
              end  
         | 
| 153 | 
            +
              
         | 
| 154 | 
            +
              # FIXME
         | 
| 155 | 
            +
              # def get_recent(column_family, key, super_column = nil, column = nil, timestamp = 0)
         | 
| 156 | 
            +
              # end
         | 
| 157 | 
            +
             | 
| 158 | 
            +
              # Return a list of keys in the column_family you request. Requires the
         | 
| 159 | 
            +
              # table to be partitioned with OrderPreservingHash.
         | 
| 160 | 
            +
              def get_key_range(column_family, key_range = ''..'', limit = 100)      
         | 
| 161 | 
            +
                column_families = Array(column_family).map {|c| c.to_s}
         | 
| 162 | 
            +
                @client.get_key_range(@keyspace, column_families, key_range.begin, key_range.end, limit)
         | 
| 163 | 
            +
              end
         | 
| 164 | 
            +
              
         | 
| 165 | 
            +
              # Count all rows in the column_family you request. Requires the table 
         | 
| 166 | 
            +
              # to be partitioned with OrderPreservingHash.
         | 
| 167 | 
            +
              def count(column_family, key_range = ''..'', limit = MAX_INT)
         | 
| 168 | 
            +
                get_key_range(column_family, key_range, limit).size
         | 
| 169 | 
            +
              end      
         | 
| 170 | 
            +
            end
         | 
| @@ -0,0 +1,57 @@ | |
| 1 | 
            +
            class CassandraClient
         | 
| 2 | 
            +
              module Helper
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                private
         | 
| 5 | 
            +
                
         | 
| 6 | 
            +
                def is_super(column_family)
         | 
| 7 | 
            +
                  column_family_property(column_family, 'type') == 'Super'
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def is_sorted_by_time(column_family)
         | 
| 11 | 
            +
                  column_family_property(column_family, 'sort') == 'Time'
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
                
         | 
| 14 | 
            +
                def column_family_property(column_family_or_path, key)
         | 
| 15 | 
            +
                  column_family = column_family_or_path.to_s.split(':').first    
         | 
| 16 | 
            +
                  @schema[column_family][key]
         | 
| 17 | 
            +
                rescue NoMethodError
         | 
| 18 | 
            +
                  raise AccessError, "Invalid column family \":#{column_family}\""    
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
                
         | 
| 21 | 
            +
                def columns_to_hash(columns)
         | 
| 22 | 
            +
                  hash = ::CassandraClient::OrderedHash.new
         | 
| 23 | 
            +
                  Array(columns).each do |c| 
         | 
| 24 | 
            +
                    if c.is_a?(SuperColumn_t)
         | 
| 25 | 
            +
                      hash[c.name] = columns_to_hash(c.columns)
         | 
| 26 | 
            +
                    else
         | 
| 27 | 
            +
                      hash[c.columnName] = load(c.value)
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
                  hash
         | 
| 31 | 
            +
                end  
         | 
| 32 | 
            +
                
         | 
| 33 | 
            +
                def hash_to_columns(hash, timestamp)
         | 
| 34 | 
            +
                  hash.map do |column, value|
         | 
| 35 | 
            +
                    Column_t.new(:columnName => column, :value => dump(value), :timestamp => timestamp)
         | 
| 36 | 
            +
                  end    
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
                
         | 
| 39 | 
            +
                def hash_to_super_columns(hash, timestamp)
         | 
| 40 | 
            +
                  hash.map do |super_column, columns|
         | 
| 41 | 
            +
                    SuperColumn_t.new(:name => super_column, :columns => hash_to_columns(columns, timestamp))
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
                
         | 
| 45 | 
            +
                def time_in_microseconds
         | 
| 46 | 
            +
                  time = Time.now
         | 
| 47 | 
            +
                  time.to_i * 1_000_000 + time.usec
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
                alias :now :time_in_microseconds
         | 
| 50 | 
            +
                
         | 
| 51 | 
            +
                def flatten_once(array)
         | 
| 52 | 
            +
                  result = []
         | 
| 53 | 
            +
                  array.each { |el| result.concat(el) }
         | 
| 54 | 
            +
                  result
         | 
| 55 | 
            +
                end    
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
            end
         | 
| @@ -1,30 +1,31 @@ | |
| 1 | 
            -
            #  Copyright (c) 2004-2009 David Heinemeier Hansson
         | 
| 2 | 
            -
            #  
         | 
| 3 | 
            -
            #  Permission is hereby granted, free of charge, to any person obtaining
         | 
| 4 | 
            -
            #  a copy of this software and associated documentation files (the
         | 
| 5 | 
            -
            #  "Software"), to deal in the Software without restriction, including
         | 
| 6 | 
            -
            #  without limitation the rights to use, copy, modify, merge, publish,
         | 
| 7 | 
            -
            #  distribute, sublicense, and/or sell copies of the Software, and to
         | 
| 8 | 
            -
            #  permit persons to whom the Software is furnished to do so, subject to
         | 
| 9 | 
            -
            #  the following conditions:
         | 
| 10 | 
            -
            #  
         | 
| 11 | 
            -
            #  The above copyright notice and this permission notice shall be
         | 
| 12 | 
            -
            #  included in all copies or substantial portions of the Software.
         | 
| 13 | 
            -
            #  
         | 
| 14 | 
            -
            #  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         | 
| 15 | 
            -
            #  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         | 
| 16 | 
            -
            #  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         | 
| 17 | 
            -
            #  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         | 
| 18 | 
            -
            #  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         | 
| 19 | 
            -
            #  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         | 
| 20 | 
            -
            #  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
| 21 1 |  | 
| 22 2 | 
             
            class CassandraClient
         | 
| 23 3 | 
             
              # Hash is ordered in Ruby 1.9!
         | 
| 24 4 | 
             
              if RUBY_VERSION >= '1.9'
         | 
| 25 5 | 
             
                OrderedHash = ::Hash
         | 
| 26 | 
            -
              else
         | 
| 27 | 
            -
                 | 
| 6 | 
            +
              else  
         | 
| 7 | 
            +
                #  Copyright (c) 2004-2009 David Heinemeier Hansson
         | 
| 8 | 
            +
                #  
         | 
| 9 | 
            +
                #  Permission is hereby granted, free of charge, to any person obtaining
         | 
| 10 | 
            +
                #  a copy of this software and associated documentation files (the
         | 
| 11 | 
            +
                #  "Software"), to deal in the Software without restriction, including
         | 
| 12 | 
            +
                #  without limitation the rights to use, copy, modify, merge, publish,
         | 
| 13 | 
            +
                #  distribute, sublicense, and/or sell copies of the Software, and to
         | 
| 14 | 
            +
                #  permit persons to whom the Software is furnished to do so, subject to
         | 
| 15 | 
            +
                #  the following conditions:
         | 
| 16 | 
            +
                #  
         | 
| 17 | 
            +
                #  The above copyright notice and this permission notice shall be
         | 
| 18 | 
            +
                #  included in all copies or substantial portions of the Software.
         | 
| 19 | 
            +
                #  
         | 
| 20 | 
            +
                #  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         | 
| 21 | 
            +
                #  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         | 
| 22 | 
            +
                #  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         | 
| 23 | 
            +
                #  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         | 
| 24 | 
            +
                #  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         | 
| 25 | 
            +
                #  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         | 
| 26 | 
            +
                #  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
| 27 | 
            +
              
         | 
| 28 | 
            +
                class OrderedHash < Hash
         | 
| 28 29 | 
             
                  require 'enumerator'    
         | 
| 29 30 |  | 
| 30 31 | 
             
                  def self.[](*array)
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            module Cassandra
         | 
| 3 | 
            +
              class SafeClient  
         | 
| 4 | 
            +
                def initialize(client, transport)
         | 
| 5 | 
            +
                  @client = client 
         | 
| 6 | 
            +
                  @transport = transport
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
                
         | 
| 9 | 
            +
                def method_missing(*args)
         | 
| 10 | 
            +
                  @client.send(*args)
         | 
| 11 | 
            +
                rescue IOError
         | 
| 12 | 
            +
                  @transport.open
         | 
| 13 | 
            +
                  raise if defined?(once)
         | 
| 14 | 
            +
                  once = true
         | 
| 15 | 
            +
                  retry
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
            end
         | 
| @@ -6,194 +6,198 @@ begin; require 'ruby-debug'; rescue LoadError; end | |
| 6 6 |  | 
| 7 7 | 
             
            class CassandraClientTest < Test::Unit::TestCase
         | 
| 8 8 | 
             
              def setup
         | 
| 9 | 
            -
                @ | 
| 10 | 
            -
                @ | 
| 11 | 
            -
                @ | 
| 12 | 
            -
                @ | 
| 9 | 
            +
                @twitter = CassandraClient.new('Twitter', '127.0.0.1')
         | 
| 10 | 
            +
                @twitter.clear_keyspace!
         | 
| 11 | 
            +
                @blogs = CassandraClient.new('Multiblog', '127.0.0.1')
         | 
| 12 | 
            +
                @blogs.clear_keyspace!
         | 
| 13 13 | 
             
              end
         | 
| 14 14 |  | 
| 15 15 | 
             
              def test_inspect
         | 
| 16 16 | 
             
                assert_nothing_raised do
         | 
| 17 | 
            -
                  @ | 
| 18 | 
            -
                  @ | 
| 17 | 
            +
                  @blogs.inspect
         | 
| 18 | 
            +
                  @twitter.inspect
         | 
| 19 19 | 
             
                end
         | 
| 20 20 | 
             
              end
         | 
| 21 21 |  | 
| 22 22 | 
             
              def test_connection_reopens
         | 
| 23 23 | 
             
                assert_raises(NoMethodError) do
         | 
| 24 | 
            -
                  @ | 
| 24 | 
            +
                  @twitter.insert(:Statuses, 1, {'body' => 'v'})
         | 
| 25 25 | 
             
                end
         | 
| 26 26 | 
             
                assert_nothing_raised do
         | 
| 27 | 
            -
                  @ | 
| 27 | 
            +
                  @twitter.insert(:Statuses, key, {'body' => 'v'})
         | 
| 28 28 | 
             
                end
         | 
| 29 29 | 
             
              end  
         | 
| 30 30 |  | 
| 31 31 | 
             
              def test_get_key_name_sorted
         | 
| 32 | 
            -
                @ | 
| 33 | 
            -
                assert_equal({'body' => 'v', 'user' => 'v'}, @ | 
| 34 | 
            -
                assert_equal({}, @ | 
| 32 | 
            +
                @twitter.insert(:Users, key, {'body' => 'v', 'user' => 'v'})
         | 
| 33 | 
            +
                assert_equal({'body' => 'v', 'user' => 'v'}, @twitter.get(:Users, key))
         | 
| 34 | 
            +
                assert_equal({}, @twitter.get(:Users, 'bogus'))
         | 
| 35 35 | 
             
              end
         | 
| 36 36 |  | 
| 37 37 | 
             
              def test_get_key_name_sorted_preserving_order
         | 
| 38 38 | 
             
                # In-order hash is preserved
         | 
| 39 39 | 
             
                hash = CassandraClient::OrderedHash['a', '', 'b', '', 'c', '', 'd', '',]    
         | 
| 40 | 
            -
                @ | 
| 41 | 
            -
                assert_equal(hash.keys, @ | 
| 40 | 
            +
                @twitter.insert(:Users, key, hash)
         | 
| 41 | 
            +
                assert_equal(hash.keys, @twitter.get(:Users, key).keys)
         | 
| 42 42 |  | 
| 43 | 
            -
                @ | 
| 43 | 
            +
                @twitter.remove(:Users, key)
         | 
| 44 44 |  | 
| 45 45 | 
             
                # Out-of-order hash is returned sorted
         | 
| 46 46 | 
             
                hash = CassandraClient::OrderedHash['b', '', 'c', '', 'd', '', 'a', '']    
         | 
| 47 | 
            -
                @ | 
| 48 | 
            -
                assert_equal(hash.keys.sort, @ | 
| 49 | 
            -
                assert_not_equal(hash.keys, @ | 
| 47 | 
            +
                @twitter.insert(:Users, key, hash)
         | 
| 48 | 
            +
                assert_equal(hash.keys.sort, @twitter.get(:Users, key).keys)
         | 
| 49 | 
            +
                assert_not_equal(hash.keys, @twitter.get(:Users, key).keys)
         | 
| 50 50 | 
             
              end  
         | 
| 51 51 |  | 
| 52 52 | 
             
              def test_get_key_time_sorted
         | 
| 53 | 
            -
                @ | 
| 54 | 
            -
                assert_equal({'body' => 'v', 'user' => 'v'}, @ | 
| 55 | 
            -
                assert_equal({}, @ | 
| 53 | 
            +
                @twitter.insert(:Statuses, key, {'body' => 'v', 'user' => 'v'})
         | 
| 54 | 
            +
                assert_equal({'body' => 'v', 'user' => 'v'}, @twitter.get(:Statuses, key))
         | 
| 55 | 
            +
                assert_equal({}, @twitter.get(:Statuses, 'bogus'))
         | 
| 56 56 | 
             
              end
         | 
| 57 57 |  | 
| 58 58 | 
             
              def test_get_key_time_sorted_with_limit
         | 
| 59 | 
            -
                @ | 
| 60 | 
            -
                @ | 
| 61 | 
            -
                assert_equal({'second' => 'v'}, @ | 
| 59 | 
            +
                @twitter.insert(:Statuses, key, {'first' => 'v'})
         | 
| 60 | 
            +
                @twitter.insert(:Statuses, key, {'second' => 'v'})
         | 
| 61 | 
            +
                assert_equal({'second' => 'v'}, @twitter.get(:Statuses, key, nil, nil, 0, 1))
         | 
| 62 62 | 
             
              end    
         | 
| 63 63 |  | 
| 64 64 | 
             
              def test_get_value
         | 
| 65 | 
            -
                @ | 
| 66 | 
            -
                assert_equal 'v', @ | 
| 67 | 
            -
                assert_nil @ | 
| 65 | 
            +
                @twitter.insert(:Statuses, key, {'body' => 'v'})
         | 
| 66 | 
            +
                assert_equal 'v', @twitter.get(:Statuses, key, 'body')
         | 
| 67 | 
            +
                assert_nil @twitter.get(:Statuses, 'bogus', 'body')
         | 
| 68 68 | 
             
              end
         | 
| 69 69 |  | 
| 70 70 | 
             
              def test_get_super_key
         | 
| 71 | 
            -
                @ | 
| 72 | 
            -
                assert_equal({'user_timelines' => {'4' => 'v', '5' => 'v'}}, @ | 
| 73 | 
            -
                assert_equal({}, @ | 
| 71 | 
            +
                @twitter.insert(:StatusRelationships, key, {'user_timelines' => {'4' => 'v', '5' => 'v'}})
         | 
| 72 | 
            +
                assert_equal({'user_timelines' => {'4' => 'v', '5' => 'v'}}, @twitter.get(:StatusRelationships, key))
         | 
| 73 | 
            +
                assert_equal({}, @twitter.get(:StatusRelationships, 'bogus'))
         | 
| 74 74 | 
             
              end
         | 
| 75 75 |  | 
| 76 76 | 
             
              def test_get_super_key_multi
         | 
| 77 | 
            -
                @ | 
| 77 | 
            +
                @twitter.insert(:StatusRelationships, key, {
         | 
| 78 78 | 
             
                  'user_timelines' => {'1' => 'v1'}, 
         | 
| 79 79 | 
             
                  'mentions_timelines' => {'2' => 'v2'}})
         | 
| 80 80 | 
             
                assert_equal({
         | 
| 81 81 | 
             
                  'user_timelines' => {'1' => 'v1'}, 
         | 
| 82 | 
            -
                  'mentions_timelines' => {'2' => 'v2'}}, @ | 
| 83 | 
            -
                assert_equal({}, @ | 
| 82 | 
            +
                  'mentions_timelines' => {'2' => 'v2'}}, @twitter.get(:StatusRelationships, key))
         | 
| 83 | 
            +
                assert_equal({}, @twitter.get(:StatusRelationships, 'bogus'))
         | 
| 84 84 | 
             
              end
         | 
| 85 85 |  | 
| 86 86 | 
             
              def test_get_super_sub_key
         | 
| 87 | 
            -
                @ | 
| 88 | 
            -
                assert_equal({'4' => 'v', '5' => 'v'}, @ | 
| 89 | 
            -
                assert_equal({}, @ | 
| 87 | 
            +
                @twitter.insert(:StatusRelationships, key, {'user_timelines' => {'4' => 'v', '5' => 'v'}})
         | 
| 88 | 
            +
                assert_equal({'4' => 'v', '5' => 'v'}, @twitter.get(:StatusRelationships, key, 'user_timelines'))
         | 
| 89 | 
            +
                assert_equal({}, @twitter.get(:StatusRelationships, 'bogus', 'user_timelines'))
         | 
| 90 90 | 
             
              end
         | 
| 91 91 |  | 
| 92 92 | 
             
              def test_get_super_value
         | 
| 93 | 
            -
                @ | 
| 94 | 
            -
                assert_equal('v', @ | 
| 95 | 
            -
                assert_nil @ | 
| 93 | 
            +
                @twitter.insert(:StatusRelationships, key, {'user_timelines' => {'1' => 'v'}})
         | 
| 94 | 
            +
                assert_equal('v', @twitter.get(:StatusRelationships, key, 'user_timelines', '1'))
         | 
| 95 | 
            +
                assert_nil @twitter.get(:StatusRelationships, 'bogus', 'user_timelines', '1')
         | 
| 96 96 | 
             
              end  
         | 
| 97 97 |  | 
| 98 98 | 
             
              def test_get_key_range
         | 
| 99 | 
            -
                @ | 
| 100 | 
            -
                @ | 
| 101 | 
            -
                @ | 
| 102 | 
            -
                 | 
| 99 | 
            +
                @twitter.insert(:Statuses, '2', {'body' => '1'})
         | 
| 100 | 
            +
                @twitter.insert(:Statuses, '3', {'body' => '1'})
         | 
| 101 | 
            +
                @twitter.insert(:Statuses, '4', {'body' => '1'})
         | 
| 102 | 
            +
                @twitter.insert(:Statuses, '5', {'body' => '1'})
         | 
| 103 | 
            +
                @twitter.insert(:Statuses, '6', {'body' => '1'})
         | 
| 104 | 
            +
                assert_equal(['3', '4', '5'], @twitter.get_key_range(:Statuses, '3'..'5'))
         | 
| 103 105 | 
             
              end
         | 
| 104 106 |  | 
| 105 107 | 
             
              # Not supported
         | 
| 106 108 | 
             
              #  def test_get_key_range_super
         | 
| 107 | 
            -
              #    @ | 
| 108 | 
            -
              #    @ | 
| 109 | 
            -
              #    @ | 
| 110 | 
            -
              #     | 
| 109 | 
            +
              #    @twitter.insert(:StatusRelationships, '2', {'user_timelines' => {'1' => 'v'}})
         | 
| 110 | 
            +
              #    @twitter.insert(:StatusRelationships, '3', {'user_timelines' => {'1' => 'v'}})
         | 
| 111 | 
            +
              #    @twitter.insert(:StatusRelationships, '4', {'user_timelines' => {'1' => 'v'}})
         | 
| 112 | 
            +
              #    @twitter.insert(:StatusRelationships, '5', {'user_timelines' => {'1' => 'v'}})
         | 
| 113 | 
            +
              #    @twitter.insert(:StatusRelationships, '6', {'user_timelines' => {'1' => 'v'}})
         | 
| 114 | 
            +
              #    assert_equal(['3', '4', '5'], @twitter.get_key_range(:StatusRelationships, '3'..'5', 'user_timelines'))
         | 
| 111 115 | 
             
              #  end
         | 
| 112 116 |  | 
| 113 117 | 
             
              def test_remove_key
         | 
| 114 | 
            -
                @ | 
| 115 | 
            -
                @ | 
| 116 | 
            -
                assert_equal({}, @ | 
| 118 | 
            +
                @twitter.insert(:Statuses, key, {'body' => 'v'})
         | 
| 119 | 
            +
                @twitter.remove(:Statuses, key)
         | 
| 120 | 
            +
                assert_equal({}, @twitter.get(:Statuses, key))
         | 
| 117 121 | 
             
              end
         | 
| 118 122 |  | 
| 119 123 | 
             
              def test_remove_value
         | 
| 120 | 
            -
                @ | 
| 121 | 
            -
                @ | 
| 122 | 
            -
                assert_nil @ | 
| 124 | 
            +
                @twitter.insert(:Statuses, key, {'body' => 'v'})
         | 
| 125 | 
            +
                @twitter.remove(:Statuses, key, 'body')
         | 
| 126 | 
            +
                assert_nil @twitter.get(:Statuses, key, 'body')    
         | 
| 123 127 | 
             
              end
         | 
| 124 128 |  | 
| 125 129 | 
             
              def test_remove_super_key
         | 
| 126 | 
            -
                @ | 
| 127 | 
            -
                @ | 
| 128 | 
            -
                assert_equal({}, @ | 
| 130 | 
            +
                @twitter.insert(:StatusRelationships, key, {'user_timelines' => {'1' => 'v'}})
         | 
| 131 | 
            +
                @twitter.remove(:StatusRelationships, key)
         | 
| 132 | 
            +
                assert_equal({}, @twitter.get(:StatusRelationships, key))
         | 
| 129 133 | 
             
              end
         | 
| 130 134 |  | 
| 131 135 | 
             
              def test_remove_super_sub_key
         | 
| 132 | 
            -
                @ | 
| 133 | 
            -
                @ | 
| 134 | 
            -
                assert_equal({}, @ | 
| 136 | 
            +
                @twitter.insert(:StatusRelationships, key, {'user_timelines' => {'1' => 'v'}})
         | 
| 137 | 
            +
                @twitter.remove(:StatusRelationships, key, 'user_timelines')
         | 
| 138 | 
            +
                assert_equal({}, @twitter.get(:StatusRelationships, key, 'user_timelines'))
         | 
| 135 139 | 
             
              end
         | 
| 136 140 |  | 
| 137 141 | 
             
              def test_remove_super_value
         | 
| 138 | 
            -
                @ | 
| 139 | 
            -
                @ | 
| 140 | 
            -
                assert_nil @ | 
| 142 | 
            +
                @twitter.insert(:StatusRelationships, key, {'user_timelines' => {'1' => 'v'}})
         | 
| 143 | 
            +
                @twitter.remove(:StatusRelationships, key, 'user_timelines', '1')
         | 
| 144 | 
            +
                assert_nil @twitter.get(:StatusRelationships, key, 'user_timelines', '1')    
         | 
| 141 145 | 
             
              end
         | 
| 142 146 |  | 
| 143 147 | 
             
              def test_insert_key
         | 
| 144 | 
            -
                @ | 
| 145 | 
            -
                assert_equal({'body' => 'v', 'user' => 'v'}, @ | 
| 148 | 
            +
                @twitter.insert(:Statuses, key, {'body' => 'v', 'user' => 'v'})
         | 
| 149 | 
            +
                assert_equal({'body' => 'v', 'user' => 'v'}, @twitter.get(:Statuses, key))  
         | 
| 146 150 | 
             
              end
         | 
| 147 151 |  | 
| 148 152 | 
             
              def test_insert_super_key
         | 
| 149 | 
            -
                @ | 
| 150 | 
            -
                assert_equal({'1' => 'v' , key => 'v'}, @ | 
| 153 | 
            +
                @twitter.insert(:StatusRelationships, key, {'user_timelines' => {'1' => 'v', key => 'v'}})
         | 
| 154 | 
            +
                assert_equal({'1' => 'v' , key => 'v'}, @twitter.get(:StatusRelationships, key, 'user_timelines'))  
         | 
| 151 155 | 
             
              end
         | 
| 152 156 |  | 
| 153 157 | 
             
              def test_get_column_values
         | 
| 154 | 
            -
                @ | 
| 155 | 
            -
                assert_equal(['v1' , 'v2'], @ | 
| 158 | 
            +
                @twitter.insert(:Statuses, key, {'body' => 'v1', 'user' => 'v2'})
         | 
| 159 | 
            +
                assert_equal(['v1' , 'v2'], @twitter.get_columns(:Statuses, key,['body', 'user']))
         | 
| 156 160 | 
             
              end  
         | 
| 157 161 |  | 
| 158 162 | 
             
              def test_get_column_values_super
         | 
| 159 | 
            -
                @ | 
| 163 | 
            +
                @twitter.insert(:StatusRelationships, key, {
         | 
| 160 164 | 
             
                  'user_timelines' => {'1' => 'v1'}, 
         | 
| 161 165 | 
             
                  'mentions_timelines' => {'2' => 'v2'}})
         | 
| 162 166 | 
             
                assert_equal [{'1' => 'v1'}, {'2' => 'v2'}], 
         | 
| 163 | 
            -
                  @ | 
| 167 | 
            +
                  @twitter.get_columns(:StatusRelationships, key, ['user_timelines', 'mentions_timelines'])
         | 
| 164 168 | 
             
              end  
         | 
| 165 169 |  | 
| 166 170 | 
             
              # Not supported
         | 
| 167 171 | 
             
              #  def test_get_columns_super_sub
         | 
| 168 | 
            -
              #    @ | 
| 172 | 
            +
              #    @twitter.insert(:StatusRelationships, key, {
         | 
| 169 173 | 
             
              #      'user_timelines' => {'1' => 'v1'}, 
         | 
| 170 174 | 
             
              #      'mentions_timelines' => {'2' => 'v2'}})
         | 
| 171 175 | 
             
              #    assert_equal ['v1', 'v2'], 
         | 
| 172 | 
            -
              #      @ | 
| 176 | 
            +
              #      @twitter.get_columns(:StatusRelationships, key, 'user_timelines', ['1', key])
         | 
| 173 177 | 
             
              #  end    
         | 
| 174 178 |  | 
| 175 179 | 
             
              def test_count_keys
         | 
| 176 | 
            -
                @ | 
| 177 | 
            -
                @ | 
| 178 | 
            -
                @ | 
| 179 | 
            -
                assert_equal 3, @ | 
| 180 | 
            +
                @twitter.insert(:Statuses, key + "1", {'body' => '1'})
         | 
| 181 | 
            +
                @twitter.insert(:Statuses, key + "2", {'body' => '2'})
         | 
| 182 | 
            +
                @twitter.insert(:Statuses, key + "3", {'body' => '3'})
         | 
| 183 | 
            +
                assert_equal 3, @twitter.count(:Statuses)  
         | 
| 180 184 | 
             
              end
         | 
| 181 185 |  | 
| 182 186 | 
             
              def test_count_columns
         | 
| 183 | 
            -
                @ | 
| 184 | 
            -
                assert_equal 2, @ | 
| 187 | 
            +
                @twitter.insert(:Statuses, key, {'body' => 'v1', 'user' => 'v2'})
         | 
| 188 | 
            +
                assert_equal 2, @twitter.count_columns(:Statuses, key)
         | 
| 185 189 | 
             
              end 
         | 
| 186 190 |  | 
| 187 191 | 
             
              def test_count_super_columns
         | 
| 188 | 
            -
                @ | 
| 192 | 
            +
                @twitter.insert(:StatusRelationships, key, {
         | 
| 189 193 | 
             
                  'user_timelines' => {'1' => 'v1'}, 
         | 
| 190 194 | 
             
                  'mentions_timelines' => {'2' => 'v2'}})
         | 
| 191 | 
            -
                assert_equal 2, @ | 
| 195 | 
            +
                assert_equal 2, @twitter.count_columns(:StatusRelationships, key)
         | 
| 192 196 | 
             
              end 
         | 
| 193 197 |  | 
| 194 198 | 
             
              def test_count_super_sub_columns
         | 
| 195 | 
            -
                @ | 
| 196 | 
            -
                assert_equal 2, @ | 
| 199 | 
            +
                @twitter.insert(:StatusRelationships, key, {'user_timelines' => {'1' => 'v1', key => 'v2'}})
         | 
| 200 | 
            +
                assert_equal 2, @twitter.count_columns(:StatusRelationships, key, 'user_timelines')
         | 
| 197 201 | 
             
              end
         | 
| 198 202 |  | 
| 199 203 | 
             
              private
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification 
         | 
| 2 2 | 
             
            name: cassandra_client
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            -
              version: "0. | 
| 4 | 
            +
              version: "0.2"
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors: 
         | 
| 7 7 | 
             
            - Evan Weaver
         | 
| @@ -30,7 +30,7 @@ cert_chain: | |
| 30 30 | 
             
              yZ0=
         | 
| 31 31 | 
             
              -----END CERTIFICATE-----
         | 
| 32 32 |  | 
| 33 | 
            -
            date: 2009-07- | 
| 33 | 
            +
            date: 2009-07-06 00:00:00 -07:00
         | 
| 34 34 | 
             
            default_executable: 
         | 
| 35 35 | 
             
            dependencies: 
         | 
| 36 36 | 
             
            - !ruby/object:Gem::Dependency 
         | 
| @@ -51,22 +51,26 @@ extensions: [] | |
| 51 51 |  | 
| 52 52 | 
             
            extra_rdoc_files: 
         | 
| 53 53 | 
             
            - CHANGELOG
         | 
| 54 | 
            -
            - lib/cassandra_client/ | 
| 54 | 
            +
            - lib/cassandra_client/cassandra_client.rb
         | 
| 55 | 
            +
            - lib/cassandra_client/helper.rb
         | 
| 55 56 | 
             
            - lib/cassandra_client/ordered_hash.rb
         | 
| 57 | 
            +
            - lib/cassandra_client/safe_client.rb
         | 
| 56 58 | 
             
            - lib/cassandra_client/serialization.rb
         | 
| 57 | 
            -
            - lib/cassandra_client/table.rb
         | 
| 58 59 | 
             
            - lib/cassandra_client.rb
         | 
| 59 60 | 
             
            - LICENSE
         | 
| 60 61 | 
             
            - README
         | 
| 62 | 
            +
            - vendor/gen-rb/cassandra_constants.rb
         | 
| 63 | 
            +
            - vendor/gen-rb/cassandra_types.rb
         | 
| 61 64 | 
             
            files: 
         | 
| 62 65 | 
             
            - CHANGELOG
         | 
| 63 66 | 
             
            - conf/cassandra.in.sh
         | 
| 64 67 | 
             
            - conf/log4j.properties
         | 
| 65 68 | 
             
            - conf/storage-conf.xml
         | 
| 66 | 
            -
            - lib/cassandra_client/ | 
| 69 | 
            +
            - lib/cassandra_client/cassandra_client.rb
         | 
| 70 | 
            +
            - lib/cassandra_client/helper.rb
         | 
| 67 71 | 
             
            - lib/cassandra_client/ordered_hash.rb
         | 
| 72 | 
            +
            - lib/cassandra_client/safe_client.rb
         | 
| 68 73 | 
             
            - lib/cassandra_client/serialization.rb
         | 
| 69 | 
            -
            - lib/cassandra_client/table.rb
         | 
| 70 74 | 
             
            - lib/cassandra_client.rb
         | 
| 71 75 | 
             
            - LICENSE
         | 
| 72 76 | 
             
            - Manifest
         | 
    
        metadata.gz.sig
    CHANGED
    
    | Binary file | 
| @@ -1,65 +0,0 @@ | |
| 1 | 
            -
            class CassandraClient
         | 
| 2 | 
            -
              attr_reader :client, :transport, :tables, :host, :port, :block_for, :serialization
         | 
| 3 | 
            -
             | 
| 4 | 
            -
              class AccessError < StandardError; end
         | 
| 5 | 
            -
             | 
| 6 | 
            -
              # Instantiate a new CassandraClient and open the connection.
         | 
| 7 | 
            -
              def initialize(host = '127.0.0.1', port = 9160, block_for = 1, serialization = CassandraClient::Serialization::JSON)
         | 
| 8 | 
            -
                @host = host
         | 
| 9 | 
            -
                @port = port
         | 
| 10 | 
            -
                @serialization = serialization
         | 
| 11 | 
            -
                @block_for = block_for
         | 
| 12 | 
            -
                
         | 
| 13 | 
            -
                @transport = Thrift::BufferedTransport.new(Thrift::Socket.new(@host, @port))
         | 
| 14 | 
            -
                @transport.open
         | 
| 15 | 
            -
             | 
| 16 | 
            -
                @client = SafeClient.new(
         | 
| 17 | 
            -
                  Cassandra::Client.new(Thrift::BinaryProtocol.new(@transport)), 
         | 
| 18 | 
            -
                  @transport)
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                @tables = @client.getStringListProperty("tables").map do |table_name|
         | 
| 21 | 
            -
                  ::CassandraClient::Table.new(table_name, self)
         | 
| 22 | 
            -
                end
         | 
| 23 | 
            -
              end
         | 
| 24 | 
            -
             | 
| 25 | 
            -
              def inspect(full = true)
         | 
| 26 | 
            -
                string = "#<CassandraClient:#{object_id}, @host=#{host.inspect}, @port=#{@port.inspect}"
         | 
| 27 | 
            -
                string += ", @block_for=#{block_for.inspect}, @tables=[#{tables.map {|t| t.inspect(false) }.join(', ')}]" if full
         | 
| 28 | 
            -
                string + ">"
         | 
| 29 | 
            -
              end
         | 
| 30 | 
            -
             | 
| 31 | 
            -
              # Return the CassandraClient::Table instance for the table_name you
         | 
| 32 | 
            -
              # request. You can get an array of all available tables with the #tables
         | 
| 33 | 
            -
              # method.
         | 
| 34 | 
            -
              def table(table_name)
         | 
| 35 | 
            -
                table = @tables.detect {|table| table.name == table_name }
         | 
| 36 | 
            -
                raise AccessError, "No such table #{table_name.inspect}" unless table
         | 
| 37 | 
            -
                table
         | 
| 38 | 
            -
              end
         | 
| 39 | 
            -
             | 
| 40 | 
            -
              # Remove all rows in all column families in all tables.
         | 
| 41 | 
            -
              def remove_all
         | 
| 42 | 
            -
                tables.each do |table|
         | 
| 43 | 
            -
                  table.schema.keys.each do |column_family|
         | 
| 44 | 
            -
                    table.remove_all(column_family)
         | 
| 45 | 
            -
                  end
         | 
| 46 | 
            -
                end
         | 
| 47 | 
            -
              end
         | 
| 48 | 
            -
              
         | 
| 49 | 
            -
              class SafeClient  
         | 
| 50 | 
            -
                def initialize(client, transport)
         | 
| 51 | 
            -
                  @client = client 
         | 
| 52 | 
            -
                  @transport = transport
         | 
| 53 | 
            -
                end
         | 
| 54 | 
            -
                
         | 
| 55 | 
            -
                def method_missing(*args)
         | 
| 56 | 
            -
                  @client.send(*args)
         | 
| 57 | 
            -
                rescue IOError
         | 
| 58 | 
            -
                  @transport.open
         | 
| 59 | 
            -
                  raise if defined?(once)
         | 
| 60 | 
            -
                  once = true
         | 
| 61 | 
            -
                  retry
         | 
| 62 | 
            -
                end
         | 
| 63 | 
            -
              end
         | 
| 64 | 
            -
              
         | 
| 65 | 
            -
            end
         | 
| @@ -1,202 +0,0 @@ | |
| 1 | 
            -
            class CassandraClient
         | 
| 2 | 
            -
              class Table
         | 
| 3 | 
            -
                attr_reader :name, :schema, :parent
         | 
| 4 | 
            -
                
         | 
| 5 | 
            -
                MAX_INT = 2**31 - 1
         | 
| 6 | 
            -
              
         | 
| 7 | 
            -
                def initialize(name, parent)
         | 
| 8 | 
            -
                  @parent = parent
         | 
| 9 | 
            -
                  @client = parent.client
         | 
| 10 | 
            -
                  @block_for = parent.block_for
         | 
| 11 | 
            -
             | 
| 12 | 
            -
                  @name = name
         | 
| 13 | 
            -
                  @schema = @client.describeTable(@name)
         | 
| 14 | 
            -
                  extend(parent.serialization)
         | 
| 15 | 
            -
                end
         | 
| 16 | 
            -
                
         | 
| 17 | 
            -
                def inspect(full = true)
         | 
| 18 | 
            -
                  string = "#<CassandraClient::Table:#{object_id}, @name=#{name.inspect}"
         | 
| 19 | 
            -
                  string += ", @schema={#{schema.map {|name, hash| ":#{name} => #{hash['type'].inspect}"}.join(', ')}}, @parent=#{parent.inspect(false)}" if full
         | 
| 20 | 
            -
                  string + ">"
         | 
| 21 | 
            -
                end
         | 
| 22 | 
            -
              
         | 
| 23 | 
            -
                ## Write
         | 
| 24 | 
            -
                
         | 
| 25 | 
            -
                # Insert a row for a key. Pass a flat hash for a regular column family, and 
         | 
| 26 | 
            -
                # a nested hash for a super column family.
         | 
| 27 | 
            -
                def insert(key, column_family, hash, timestamp = now)
         | 
| 28 | 
            -
                  column_family = column_family.to_s    
         | 
| 29 | 
            -
                  insert = is_super(column_family) ? :insert_super : :insert_standard
         | 
| 30 | 
            -
                  send(insert, key, column_family, hash, timestamp)
         | 
| 31 | 
            -
                end
         | 
| 32 | 
            -
                
         | 
| 33 | 
            -
                private
         | 
| 34 | 
            -
              
         | 
| 35 | 
            -
                def insert_standard(key, column_family, hash, timestamp = now)
         | 
| 36 | 
            -
                  mutation = Batch_mutation_t.new(
         | 
| 37 | 
            -
                    :table => @name, 
         | 
| 38 | 
            -
                    :key => key, 
         | 
| 39 | 
            -
                    :cfmap => {column_family => hash_to_columns(hash, timestamp)})
         | 
| 40 | 
            -
                  @client.batch_insert(mutation, @block_for)
         | 
| 41 | 
            -
                end 
         | 
| 42 | 
            -
              
         | 
| 43 | 
            -
                def insert_super(key, column_family, hash, timestamp = now)
         | 
| 44 | 
            -
                  mutation = Batch_mutation_super_t.new(
         | 
| 45 | 
            -
                    :table => @name, 
         | 
| 46 | 
            -
                    :key => key, 
         | 
| 47 | 
            -
                    :cfmap => {column_family => hash_to_super_columns(hash, timestamp)})
         | 
| 48 | 
            -
                  @client.batch_insert_superColumn(mutation, @block_for)
         | 
| 49 | 
            -
                end 
         | 
| 50 | 
            -
                
         | 
| 51 | 
            -
                public
         | 
| 52 | 
            -
                
         | 
| 53 | 
            -
                ## Delete
         | 
| 54 | 
            -
                
         | 
| 55 | 
            -
                # Remove the element at the column_family:key:super_column:column 
         | 
| 56 | 
            -
                # path you request.
         | 
| 57 | 
            -
                def remove(key, column_family, super_column = nil, column = nil, timestamp = now)
         | 
| 58 | 
            -
                  column_family = column_family.to_s
         | 
| 59 | 
            -
                  column_family += ":#{super_column}" if super_column
         | 
| 60 | 
            -
                  column_family += ":#{column}" if column
         | 
| 61 | 
            -
                  @client.remove(@name, key, column_family, timestamp, @block_for )
         | 
| 62 | 
            -
                end
         | 
| 63 | 
            -
                
         | 
| 64 | 
            -
                # Remove all rows in the column family you request.
         | 
| 65 | 
            -
                def remove_all(column_family)
         | 
| 66 | 
            -
                  get_key_range(column_family).each do |key| 
         | 
| 67 | 
            -
                    remove(key, column_family)
         | 
| 68 | 
            -
                  end
         | 
| 69 | 
            -
                end
         | 
| 70 | 
            -
                
         | 
| 71 | 
            -
                ## Read
         | 
| 72 | 
            -
              
         | 
| 73 | 
            -
                # Count the elements at the column_family:key:super_column path you 
         | 
| 74 | 
            -
                # request.
         | 
| 75 | 
            -
                def count_columns(key, column_family, super_column = nil)
         | 
| 76 | 
            -
                  column_family = column_family.to_s
         | 
| 77 | 
            -
                  column_family += ":#{super_column}" if super_column
         | 
| 78 | 
            -
                  @client.get_column_count(@name, key, column_family)
         | 
| 79 | 
            -
                end
         | 
| 80 | 
            -
                
         | 
| 81 | 
            -
                # Return a list of single values for the elements at the
         | 
| 82 | 
            -
                # column_family:key:super_column:column path you request.
         | 
| 83 | 
            -
                def get_columns(key, column_family, super_columns, columns = nil)
         | 
| 84 | 
            -
                  column_family = column_family.to_s
         | 
| 85 | 
            -
                  get_slice_by_names = (is_super(column_family) && !columns) ? :get_slice_super_by_names : :get_slice_by_names
         | 
| 86 | 
            -
                  if super_columns and columns
         | 
| 87 | 
            -
                    column_family += ":#{super_columns}" 
         | 
| 88 | 
            -
                    columns = Array(columns)
         | 
| 89 | 
            -
                  else
         | 
| 90 | 
            -
                    columns = Array(super_columns)
         | 
| 91 | 
            -
                  end
         | 
| 92 | 
            -
                      
         | 
| 93 | 
            -
                  hash = columns_to_hash(@client.send(get_slice_by_names, @name, key, column_family, columns))
         | 
| 94 | 
            -
                  columns.map { |column| hash[column] }
         | 
| 95 | 
            -
                end
         | 
| 96 | 
            -
                      
         | 
| 97 | 
            -
                # Return a hash (actually, a CassandraClient::OrderedHash) or a single value 
         | 
| 98 | 
            -
                # representing the element at the column_family:key:super_column:column 
         | 
| 99 | 
            -
                # path you request.
         | 
| 100 | 
            -
                def get(key, column_family, super_column = nil, column = nil, offset = -1, limit = 100)
         | 
| 101 | 
            -
                  column_family = column_family.to_s
         | 
| 102 | 
            -
                  column_family += ":#{super_column}" if super_column
         | 
| 103 | 
            -
                  column_family += ":#{column}" if column          
         | 
| 104 | 
            -
                  
         | 
| 105 | 
            -
                  # You have got to be kidding
         | 
| 106 | 
            -
                  if is_super(column_family)
         | 
| 107 | 
            -
                    if column
         | 
| 108 | 
            -
                      load(@client.get_column(@name, key, column_family).value)
         | 
| 109 | 
            -
                    elsif super_column
         | 
| 110 | 
            -
                      columns_to_hash(@client.get_superColumn(@name, key, column_family).columns)
         | 
| 111 | 
            -
                    else
         | 
| 112 | 
            -
                      columns_to_hash(@client.get_slice_super(@name, key, "#{column_family}:", offset, limit))
         | 
| 113 | 
            -
                    end
         | 
| 114 | 
            -
                  else
         | 
| 115 | 
            -
                    if super_column
         | 
| 116 | 
            -
                      load(@client.get_column(@name, key, column_family).value)
         | 
| 117 | 
            -
                    elsif is_sorted_by_time(column_family)
         | 
| 118 | 
            -
                      result = columns_to_hash(@client.get_columns_since(@name, key, column_family, 0))
         | 
| 119 | 
            -
             | 
| 120 | 
            -
                      # FIXME Hack until get_slice on a time-sorted column family works again
         | 
| 121 | 
            -
                      result = OrderedHash[*flatten_once(result.to_a[offset, limit])] if offset > -1
         | 
| 122 | 
            -
                      result
         | 
| 123 | 
            -
                    else
         | 
| 124 | 
            -
                      columns_to_hash(@client.get_slice(@name, key, "#{column_family}:", offset, limit))
         | 
| 125 | 
            -
                    end 
         | 
| 126 | 
            -
                  end
         | 
| 127 | 
            -
                rescue NotFoundException
         | 
| 128 | 
            -
                  is_super(column_family) && !column ? {} : nil
         | 
| 129 | 
            -
                end  
         | 
| 130 | 
            -
                
         | 
| 131 | 
            -
                # FIXME
         | 
| 132 | 
            -
                # def get_recent(key, column_family, super_column = nil, column = nil, timestamp = 0)
         | 
| 133 | 
            -
                # end
         | 
| 134 | 
            -
              
         | 
| 135 | 
            -
                # Return a list of keys in the column_family you request. Requires the
         | 
| 136 | 
            -
                # table to be partitioned with OrderPreservingHash.
         | 
| 137 | 
            -
                def get_key_range(key_range, column_family = nil, limit = 100)      
         | 
| 138 | 
            -
                  column_family, key_range = key_range, ''..'' unless column_family
         | 
| 139 | 
            -
                  column_families = Array(column_family).map {|c| c.to_s}
         | 
| 140 | 
            -
                  @client.get_key_range(@name, column_families, key_range.begin, key_range.end, limit)
         | 
| 141 | 
            -
                end
         | 
| 142 | 
            -
                
         | 
| 143 | 
            -
                # Count all rows in the column_family you request. Requires the table 
         | 
| 144 | 
            -
                # to be partitioned with OrderPreservingHash.
         | 
| 145 | 
            -
                def count(key_range, column_family = nil, limit = MAX_INT)
         | 
| 146 | 
            -
                  get_key_range(key_range, column_family, limit).size
         | 
| 147 | 
            -
                end
         | 
| 148 | 
            -
                  
         | 
| 149 | 
            -
                private
         | 
| 150 | 
            -
                
         | 
| 151 | 
            -
                def is_super(column_family)
         | 
| 152 | 
            -
                  column_family_property(column_family, 'type') == 'Super'
         | 
| 153 | 
            -
                end
         | 
| 154 | 
            -
             | 
| 155 | 
            -
                def is_sorted_by_time(column_family)
         | 
| 156 | 
            -
                  column_family_property(column_family, 'sort') == 'Time'
         | 
| 157 | 
            -
                end
         | 
| 158 | 
            -
                
         | 
| 159 | 
            -
                def column_family_property(column_family_or_path, key)
         | 
| 160 | 
            -
                  column_family = column_family_or_path.to_s.split(':').first    
         | 
| 161 | 
            -
                  @schema[column_family][key]
         | 
| 162 | 
            -
                rescue NoMethodError
         | 
| 163 | 
            -
                  raise AccessError, "Invalid column family \":#{column_family}\""    
         | 
| 164 | 
            -
                end
         | 
| 165 | 
            -
                
         | 
| 166 | 
            -
                def columns_to_hash(columns)
         | 
| 167 | 
            -
                  hash = ::CassandraClient::OrderedHash.new
         | 
| 168 | 
            -
                  Array(columns).each do |c| 
         | 
| 169 | 
            -
                    if c.is_a?(SuperColumn_t)
         | 
| 170 | 
            -
                      hash[c.name] = columns_to_hash(c.columns)
         | 
| 171 | 
            -
                    else
         | 
| 172 | 
            -
                      hash[c.columnName] = load(c.value)
         | 
| 173 | 
            -
                    end
         | 
| 174 | 
            -
                  end
         | 
| 175 | 
            -
                  hash
         | 
| 176 | 
            -
                end  
         | 
| 177 | 
            -
                
         | 
| 178 | 
            -
                def hash_to_columns(hash, timestamp)
         | 
| 179 | 
            -
                  hash.map do |column, value|
         | 
| 180 | 
            -
                    Column_t.new(:columnName => column, :value => dump(value), :timestamp => timestamp)
         | 
| 181 | 
            -
                  end    
         | 
| 182 | 
            -
                end
         | 
| 183 | 
            -
                
         | 
| 184 | 
            -
                def hash_to_super_columns(hash, timestamp)
         | 
| 185 | 
            -
                  hash.map do |super_column, columns|
         | 
| 186 | 
            -
                    SuperColumn_t.new(:name => super_column, :columns => hash_to_columns(columns, timestamp))
         | 
| 187 | 
            -
                  end
         | 
| 188 | 
            -
                end
         | 
| 189 | 
            -
                
         | 
| 190 | 
            -
                def time_in_microseconds
         | 
| 191 | 
            -
                  time = Time.now
         | 
| 192 | 
            -
                  time.to_i * 1_000_000 + time.usec
         | 
| 193 | 
            -
                end
         | 
| 194 | 
            -
                alias :now :time_in_microseconds
         | 
| 195 | 
            -
                
         | 
| 196 | 
            -
                def flatten_once(array)
         | 
| 197 | 
            -
                  result = []
         | 
| 198 | 
            -
                  array.each { |el| result.concat(el) }
         | 
| 199 | 
            -
                  result
         | 
| 200 | 
            -
                end    
         | 
| 201 | 
            -
              end
         | 
| 202 | 
            -
            end
         |