massive_record 0.1.0

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.
Files changed (81) hide show
  1. data/.autotest +15 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +38 -0
  6. data/Manifest +24 -0
  7. data/README.md +225 -0
  8. data/Rakefile +16 -0
  9. data/TODO.md +8 -0
  10. data/autotest/discover.rb +1 -0
  11. data/lib/massive_record.rb +18 -0
  12. data/lib/massive_record/exceptions.rb +11 -0
  13. data/lib/massive_record/orm/attribute_methods.rb +61 -0
  14. data/lib/massive_record/orm/attribute_methods/dirty.rb +80 -0
  15. data/lib/massive_record/orm/attribute_methods/read.rb +23 -0
  16. data/lib/massive_record/orm/attribute_methods/write.rb +24 -0
  17. data/lib/massive_record/orm/base.rb +176 -0
  18. data/lib/massive_record/orm/callbacks.rb +52 -0
  19. data/lib/massive_record/orm/column.rb +18 -0
  20. data/lib/massive_record/orm/config.rb +47 -0
  21. data/lib/massive_record/orm/errors.rb +47 -0
  22. data/lib/massive_record/orm/finders.rb +125 -0
  23. data/lib/massive_record/orm/id_factory.rb +133 -0
  24. data/lib/massive_record/orm/persistence.rb +199 -0
  25. data/lib/massive_record/orm/schema.rb +4 -0
  26. data/lib/massive_record/orm/schema/column_families.rb +48 -0
  27. data/lib/massive_record/orm/schema/column_family.rb +102 -0
  28. data/lib/massive_record/orm/schema/column_interface.rb +91 -0
  29. data/lib/massive_record/orm/schema/common_interface.rb +48 -0
  30. data/lib/massive_record/orm/schema/field.rb +128 -0
  31. data/lib/massive_record/orm/schema/fields.rb +37 -0
  32. data/lib/massive_record/orm/schema/table_interface.rb +96 -0
  33. data/lib/massive_record/orm/table.rb +9 -0
  34. data/lib/massive_record/orm/validations.rb +52 -0
  35. data/lib/massive_record/spec/support/simple_database_cleaner.rb +52 -0
  36. data/lib/massive_record/thrift/hbase.rb +2307 -0
  37. data/lib/massive_record/thrift/hbase_constants.rb +14 -0
  38. data/lib/massive_record/thrift/hbase_types.rb +225 -0
  39. data/lib/massive_record/version.rb +3 -0
  40. data/lib/massive_record/wrapper/base.rb +28 -0
  41. data/lib/massive_record/wrapper/cell.rb +45 -0
  42. data/lib/massive_record/wrapper/column_families_collection.rb +19 -0
  43. data/lib/massive_record/wrapper/column_family.rb +22 -0
  44. data/lib/massive_record/wrapper/connection.rb +71 -0
  45. data/lib/massive_record/wrapper/row.rb +170 -0
  46. data/lib/massive_record/wrapper/scanner.rb +50 -0
  47. data/lib/massive_record/wrapper/table.rb +148 -0
  48. data/lib/massive_record/wrapper/tables_collection.rb +13 -0
  49. data/massive_record.gemspec +28 -0
  50. data/spec/config.yml.example +4 -0
  51. data/spec/orm/cases/attribute_methods_spec.rb +47 -0
  52. data/spec/orm/cases/auto_generate_id_spec.rb +54 -0
  53. data/spec/orm/cases/base_spec.rb +176 -0
  54. data/spec/orm/cases/callbacks_spec.rb +309 -0
  55. data/spec/orm/cases/column_spec.rb +49 -0
  56. data/spec/orm/cases/config_spec.rb +103 -0
  57. data/spec/orm/cases/dirty_spec.rb +129 -0
  58. data/spec/orm/cases/encoding_spec.rb +49 -0
  59. data/spec/orm/cases/finders_spec.rb +208 -0
  60. data/spec/orm/cases/hbase/connection_spec.rb +13 -0
  61. data/spec/orm/cases/i18n_spec.rb +32 -0
  62. data/spec/orm/cases/id_factory_spec.rb +75 -0
  63. data/spec/orm/cases/persistence_spec.rb +479 -0
  64. data/spec/orm/cases/table_spec.rb +81 -0
  65. data/spec/orm/cases/validation_spec.rb +92 -0
  66. data/spec/orm/models/address.rb +7 -0
  67. data/spec/orm/models/person.rb +15 -0
  68. data/spec/orm/models/test_class.rb +5 -0
  69. data/spec/orm/schema/column_families_spec.rb +186 -0
  70. data/spec/orm/schema/column_family_spec.rb +131 -0
  71. data/spec/orm/schema/column_interface_spec.rb +115 -0
  72. data/spec/orm/schema/field_spec.rb +196 -0
  73. data/spec/orm/schema/fields_spec.rb +126 -0
  74. data/spec/orm/schema/table_interface_spec.rb +171 -0
  75. data/spec/spec_helper.rb +15 -0
  76. data/spec/support/connection_helpers.rb +76 -0
  77. data/spec/support/mock_massive_record_connection.rb +80 -0
  78. data/spec/thrift/cases/encoding_spec.rb +48 -0
  79. data/spec/wrapper/cases/connection_spec.rb +53 -0
  80. data/spec/wrapper/cases/table_spec.rb +231 -0
  81. metadata +228 -0
@@ -0,0 +1,14 @@
1
+ #
2
+ # Autogenerated by Thrift
3
+ #
4
+ # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5
+ #
6
+
7
+ module Apache
8
+ module Hadoop
9
+ module Hbase
10
+ module Thrift
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,225 @@
1
+ #
2
+ # Autogenerated by Thrift
3
+ #
4
+ # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5
+ #
6
+
7
+ module Apache
8
+ module Hadoop
9
+ module Hbase
10
+ module Thrift
11
+ # TCell - Used to transport a cell value (byte[]) and the timestamp it was
12
+ # stored with together as a result for get and getRow methods. This promotes
13
+ # the timestamp of a cell to a first-class value, making it easy to take
14
+ # note of temporal data. Cell is used all the way from HStore up to HTable.
15
+ class TCell
16
+ include ::Thrift::Struct, ::Thrift::Struct_Union
17
+ VALUE = 1
18
+ TIMESTAMP = 2
19
+
20
+ FIELDS = {
21
+ VALUE => {:type => ::Thrift::Types::STRING, :name => 'value', :binary => true},
22
+ TIMESTAMP => {:type => ::Thrift::Types::I64, :name => 'timestamp'}
23
+ }
24
+
25
+ def struct_fields; FIELDS; end
26
+
27
+ def validate
28
+ end
29
+
30
+ ::Thrift::Struct.generate_accessors self
31
+ end
32
+
33
+ # An HColumnDescriptor contains information about a column family
34
+ # such as the number of versions, compression settings, etc. It is
35
+ # used as input when creating a table or adding a column.
36
+ class ColumnDescriptor
37
+ include ::Thrift::Struct, ::Thrift::Struct_Union
38
+ NAME = 1
39
+ MAXVERSIONS = 2
40
+ COMPRESSION = 3
41
+ INMEMORY = 4
42
+ BLOOMFILTERTYPE = 5
43
+ BLOOMFILTERVECTORSIZE = 6
44
+ BLOOMFILTERNBHASHES = 7
45
+ BLOCKCACHEENABLED = 8
46
+ TIMETOLIVE = 9
47
+
48
+ FIELDS = {
49
+ NAME => {:type => ::Thrift::Types::STRING, :name => 'name', :binary => true},
50
+ MAXVERSIONS => {:type => ::Thrift::Types::I32, :name => 'maxVersions', :default => 3},
51
+ COMPRESSION => {:type => ::Thrift::Types::STRING, :name => 'compression', :default => %q"NONE"},
52
+ INMEMORY => {:type => ::Thrift::Types::BOOL, :name => 'inMemory', :default => false},
53
+ BLOOMFILTERTYPE => {:type => ::Thrift::Types::STRING, :name => 'bloomFilterType', :default => %q"NONE"},
54
+ BLOOMFILTERVECTORSIZE => {:type => ::Thrift::Types::I32, :name => 'bloomFilterVectorSize', :default => 0},
55
+ BLOOMFILTERNBHASHES => {:type => ::Thrift::Types::I32, :name => 'bloomFilterNbHashes', :default => 0},
56
+ BLOCKCACHEENABLED => {:type => ::Thrift::Types::BOOL, :name => 'blockCacheEnabled', :default => false},
57
+ TIMETOLIVE => {:type => ::Thrift::Types::I32, :name => 'timeToLive', :default => -1}
58
+ }
59
+
60
+ def struct_fields; FIELDS; end
61
+
62
+ def validate
63
+ end
64
+
65
+ ::Thrift::Struct.generate_accessors self
66
+ end
67
+
68
+ # A TRegionInfo contains information about an HTable region.
69
+ class TRegionInfo
70
+ include ::Thrift::Struct, ::Thrift::Struct_Union
71
+ STARTKEY = 1
72
+ ENDKEY = 2
73
+ ID = 3
74
+ NAME = 4
75
+ VERSION = 5
76
+
77
+ FIELDS = {
78
+ STARTKEY => {:type => ::Thrift::Types::STRING, :name => 'startKey', :binary => true},
79
+ ENDKEY => {:type => ::Thrift::Types::STRING, :name => 'endKey', :binary => true},
80
+ ID => {:type => ::Thrift::Types::I64, :name => 'id'},
81
+ NAME => {:type => ::Thrift::Types::STRING, :name => 'name', :binary => true},
82
+ VERSION => {:type => ::Thrift::Types::BYTE, :name => 'version'}
83
+ }
84
+
85
+ def struct_fields; FIELDS; end
86
+
87
+ def validate
88
+ end
89
+
90
+ ::Thrift::Struct.generate_accessors self
91
+ end
92
+
93
+ # A Mutation object is used to either update or delete a column-value.
94
+ class Mutation
95
+ include ::Thrift::Struct, ::Thrift::Struct_Union
96
+ ISDELETE = 1
97
+ COLUMN = 2
98
+ VALUE = 3
99
+
100
+ FIELDS = {
101
+ ISDELETE => {:type => ::Thrift::Types::BOOL, :name => 'isDelete', :default => false},
102
+ COLUMN => {:type => ::Thrift::Types::STRING, :name => 'column', :binary => true},
103
+ VALUE => {:type => ::Thrift::Types::STRING, :name => 'value', :binary => true}
104
+ }
105
+
106
+ def struct_fields; FIELDS; end
107
+
108
+ def validate
109
+ end
110
+
111
+ ::Thrift::Struct.generate_accessors self
112
+ end
113
+
114
+ # A BatchMutation object is used to apply a number of Mutations to a single row.
115
+ class BatchMutation
116
+ include ::Thrift::Struct, ::Thrift::Struct_Union
117
+ ROW = 1
118
+ MUTATIONS = 2
119
+
120
+ FIELDS = {
121
+ ROW => {:type => ::Thrift::Types::STRING, :name => 'row', :binary => true},
122
+ MUTATIONS => {:type => ::Thrift::Types::LIST, :name => 'mutations', :element => {:type => ::Thrift::Types::STRUCT, :class => Apache::Hadoop::Hbase::Thrift::Mutation}}
123
+ }
124
+
125
+ def struct_fields; FIELDS; end
126
+
127
+ def validate
128
+ end
129
+
130
+ ::Thrift::Struct.generate_accessors self
131
+ end
132
+
133
+ # Holds row name and then a map of columns to cells.
134
+ class TRowResult
135
+ include ::Thrift::Struct, ::Thrift::Struct_Union
136
+ ROW = 1
137
+ COLUMNS = 2
138
+
139
+ FIELDS = {
140
+ ROW => {:type => ::Thrift::Types::STRING, :name => 'row', :binary => true},
141
+ COLUMNS => {:type => ::Thrift::Types::MAP, :name => 'columns', :key => {:type => ::Thrift::Types::STRING, :binary => true}, :value => {:type => ::Thrift::Types::STRUCT, :class => Apache::Hadoop::Hbase::Thrift::TCell}}
142
+ }
143
+
144
+ def struct_fields; FIELDS; end
145
+
146
+ def validate
147
+ end
148
+
149
+ ::Thrift::Struct.generate_accessors self
150
+ end
151
+
152
+ # An IOError exception signals that an error occurred communicating
153
+ # to the Hbase master or an Hbase region server. Also used to return
154
+ # more general Hbase error conditions.
155
+ class IOError < ::Thrift::Exception
156
+ include ::Thrift::Struct, ::Thrift::Struct_Union
157
+ def initialize(message=nil)
158
+ super()
159
+ self.message = message
160
+ end
161
+
162
+ MESSAGE = 1
163
+
164
+ FIELDS = {
165
+ MESSAGE => {:type => ::Thrift::Types::STRING, :name => 'message'}
166
+ }
167
+
168
+ def struct_fields; FIELDS; end
169
+
170
+ def validate
171
+ end
172
+
173
+ ::Thrift::Struct.generate_accessors self
174
+ end
175
+
176
+ # An IllegalArgument exception indicates an illegal or invalid
177
+ # argument was passed into a procedure.
178
+ class IllegalArgument < ::Thrift::Exception
179
+ include ::Thrift::Struct, ::Thrift::Struct_Union
180
+ def initialize(message=nil)
181
+ super()
182
+ self.message = message
183
+ end
184
+
185
+ MESSAGE = 1
186
+
187
+ FIELDS = {
188
+ MESSAGE => {:type => ::Thrift::Types::STRING, :name => 'message'}
189
+ }
190
+
191
+ def struct_fields; FIELDS; end
192
+
193
+ def validate
194
+ end
195
+
196
+ ::Thrift::Struct.generate_accessors self
197
+ end
198
+
199
+ # An AlreadyExists exceptions signals that a table with the specified
200
+ # name already exists
201
+ class AlreadyExists < ::Thrift::Exception
202
+ include ::Thrift::Struct, ::Thrift::Struct_Union
203
+ def initialize(message=nil)
204
+ super()
205
+ self.message = message
206
+ end
207
+
208
+ MESSAGE = 1
209
+
210
+ FIELDS = {
211
+ MESSAGE => {:type => ::Thrift::Types::STRING, :name => 'message'}
212
+ }
213
+
214
+ def struct_fields; FIELDS; end
215
+
216
+ def validate
217
+ end
218
+
219
+ ::Thrift::Struct.generate_accessors self
220
+ end
221
+
222
+ end
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,3 @@
1
+ module MassiveRecord
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,28 @@
1
+ require 'yaml'
2
+ require 'massive_record/wrapper/connection'
3
+ require 'massive_record/wrapper/tables_collection'
4
+ require 'massive_record/wrapper/table'
5
+ require 'massive_record/wrapper/row'
6
+ require 'massive_record/wrapper/column_families_collection'
7
+ require 'massive_record/wrapper/column_family'
8
+ require 'massive_record/wrapper/cell'
9
+ require 'massive_record/wrapper/scanner'
10
+
11
+ module MassiveRecord
12
+ module Wrapper
13
+ class Base
14
+
15
+ def self.config
16
+ config = YAML.load_file(Rails.root.join('config', 'hbase.yml'))[Rails.env]
17
+ { :host => config['host'], :port => config['port'] }
18
+ end
19
+
20
+ def self.connection(opts = {})
21
+ conn = Connection.new(opts.empty? ? config : opts)
22
+ conn.open
23
+ conn
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,45 @@
1
+ module MassiveRecord
2
+ module Wrapper
3
+ class Cell
4
+ attr_writer :value
5
+ attr_accessor :created_at
6
+
7
+ class << self
8
+ def serialize_value(v)
9
+ serialize?(v) ? v.to_yaml : v.to_s.force_encoding(Encoding::BINARY)
10
+ end
11
+
12
+ private
13
+
14
+ def serialize?(v)
15
+ [Hash, Array, NilClass].include?(v.class)
16
+ end
17
+ end
18
+
19
+ def initialize(opts = {})
20
+ @value = opts[:value]
21
+ @created_at = opts[:created_at]
22
+ end
23
+
24
+ def value
25
+ @value.is_a?(String) ? @value.to_s.force_encoding(Encoding::UTF_8) : @value
26
+ end
27
+
28
+ def deserialize_value
29
+ is_yaml? ? YAML.load(@value) : value
30
+ end
31
+
32
+ def serialize_value(v)
33
+ @value = self.class.serialize_value(v)
34
+ end
35
+
36
+ def serialized_value
37
+ self.class.serialize_value(@value)
38
+ end
39
+
40
+ def is_yaml?
41
+ @value =~ /^--- \n/ || @value =~ /^--- {}/ || @value =~ /^--- \[\]/
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,19 @@
1
+ module MassiveRecord
2
+ module Wrapper
3
+ class ColumnFamiliesCollection < Array
4
+
5
+ attr_accessor :table
6
+
7
+ def create(column_family, opts = {})
8
+ if column_family.is_a?(MassiveRecord::Wrapper::ColumnFamily)
9
+ self.push(column_family)
10
+ else
11
+ self.push(MassiveRecord::Wrapper::ColumnFamily.new(column_family, opts))
12
+ end
13
+
14
+ true
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ module MassiveRecord
2
+ module Wrapper
3
+ class ColumnFamily
4
+
5
+ attr_accessor :name, :max_versions, :columns
6
+
7
+ def initialize(column_name, opts = {})
8
+ @name = column_name
9
+ @max_versions = opts[:max_versions] || 10
10
+ @columns = opts[:columns] || []
11
+ end
12
+
13
+ def descriptor
14
+ Apache::Hadoop::Hbase::Thrift::ColumnDescriptor.new do |col|
15
+ col.name = "#{name}:"
16
+ col.maxVersions = max_versions
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,71 @@
1
+ module MassiveRecord
2
+ module Wrapper
3
+ class Connection
4
+
5
+ attr_accessor :host, :port, :timeout
6
+
7
+ def initialize(opts = {})
8
+ @timeout = 4000
9
+ @host = opts[:host]
10
+ @port = opts[:port] || 9090
11
+ end
12
+
13
+ def transport
14
+ @transport ||= Thrift::BufferedTransport.new(Thrift::Socket.new(@host, @port, @timeout))
15
+ end
16
+
17
+ def open
18
+ protocol = Thrift::BinaryProtocol.new(transport)
19
+ @client = Apache::Hadoop::Hbase::Thrift::Hbase::Client.new(protocol)
20
+
21
+ begin
22
+ transport.open()
23
+ true
24
+ rescue
25
+ raise MassiveRecord::ConnectionException.new, "Unable to connect to HBase on #{@host}, port #{@port}"
26
+ end
27
+ end
28
+
29
+ def close
30
+ @transport.close.nil?
31
+ end
32
+
33
+ def client
34
+ @client
35
+ end
36
+
37
+ def active?
38
+ @transport.open?
39
+ end
40
+
41
+ def tables
42
+ collection = TablesCollection.new
43
+ collection.connection = self
44
+ getTableNames().each{|table_name| collection.push(table_name)}
45
+ collection
46
+ end
47
+
48
+ def load_table(table_name)
49
+ MassiveRecord::Wrapper::Table.new(self, table_name)
50
+ end
51
+
52
+ # Wrapp HBase API to be able to catch errors and try reconnect
53
+ def method_missing(method, *args)
54
+ begin
55
+ open if not @client
56
+ client.send(method, *args) if @client
57
+ rescue IOError
58
+ @client = nil
59
+ open
60
+ client.send(method, *args) if @client
61
+ rescue Thrift::TransportException
62
+ @transport = nil
63
+ @client = nil
64
+ open
65
+ client.send(method, *args) if @client
66
+ end
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,170 @@
1
+ require 'json'
2
+
3
+ module MassiveRecord
4
+ module Wrapper
5
+ class Row
6
+
7
+ attr_accessor :id, :column_families, :columns, :new_record, :table
8
+
9
+ def initialize(opts = {})
10
+ @id = opts[:id]
11
+ self.values = opts[:values] || {}
12
+ @table = opts[:table]
13
+ @column_families = opts[:column_families] || []
14
+ @columns = opts[:columns] || {}
15
+ @new_record = true
16
+ end
17
+
18
+ def column_names
19
+ columns.keys
20
+ end
21
+
22
+ def fetch_all_column_families
23
+ @table.fetch_column_family
24
+ fetch_column_families(@table.column_family_names)
25
+ end
26
+
27
+ def fetch_column_families(list)
28
+ @column_families = table.column_families.collect do |column_name, description|
29
+ ColumnFamily.new(column_name.split(":").first, {
30
+ :row => self,
31
+ :name => description.name,
32
+ :max_versions => description.maxVersions,
33
+ :compression => description.compression,
34
+ :in_memory => description.inMemory
35
+ # bloomFilterType, bloomFilterVectorSize, bloomFilterNbHashes, blockCacheEnabled, timeToLive
36
+ })
37
+ end
38
+ end
39
+
40
+ # = Parse columns / cells and create a Hash from them
41
+ def values
42
+ @columns.inject({"id" => id}) {|h, (column_name, cell)| h[column_name] = cell.deserialize_value; h}
43
+ end
44
+
45
+ def values=(data)
46
+ @values = {}
47
+ update_columns(data)
48
+ end
49
+
50
+ def update_columns(data = {})
51
+ data.each do |column_family_name, columns|
52
+ columns.each do |column_name, values|
53
+ update_column(column_family_name, column_name, values)
54
+ end
55
+ end
56
+ end
57
+
58
+ def update_column(column_family_name, column_name, value)
59
+ column = "#{column_family_name}:#{column_name}"
60
+
61
+ if @columns[column].nil?
62
+ @columns[column] = Cell.new({ :value => Cell.serialize_value(value), :created_at => Time.now })
63
+ else
64
+ @columns[column].serialize_value(value)
65
+ end
66
+ end
67
+
68
+ # = Merge column values with new data : it implies that column values is a JSON encoded string
69
+ def merge_columns(data)
70
+ data.each do |column_family_name, columns|
71
+ columns.each do |column_name, values|
72
+ if values.is_a?(Hash)
73
+ unless @columns["#{column_family_name}:#{column_name}"].nil?
74
+ column_value = @columns["#{column_family_name}:#{column_name}"].deserialize_value.merge(values)
75
+ else
76
+ column_value = values
77
+ end
78
+ elsif values.is_a?(Array)
79
+ unless @columns["#{column_family_name}:#{column_name}"].nil?
80
+ column_value = @columns["#{column_family_name}:#{column_name}"].deserialize_value | values
81
+ else
82
+ column_value = values
83
+ end
84
+ else
85
+ column_value = values
86
+ end
87
+ update_column(column_family_name, column_name, column_value)
88
+ end
89
+ end
90
+ end
91
+
92
+ # = Parse columns cells and save them
93
+ def save
94
+ mutations = []
95
+
96
+ @columns.each do |column_name, cell|
97
+ m = Apache::Hadoop::Hbase::Thrift::Mutation.new
98
+ m.column = column_name
99
+ m.value = cell.serialized_value
100
+
101
+ mutations.push(m)
102
+ end
103
+
104
+ @table.client.mutateRow(@table.name, id.to_s, mutations).nil?
105
+ end
106
+
107
+
108
+ #
109
+ # FIXME
110
+ #
111
+ # The thrift wrapper is only working with strings as far as I can see,
112
+ # and the atomicIncrement call on strings kinda doesn't make sense on strings
113
+ #
114
+ # For now I'll implement this without atomicIncrement, to get the behaviour we want.
115
+ # Guess this in time will either be fixed or raised an not-supported-error. If the
116
+ # latter is the case I guess we'll need to shift over to a jruby adapter and use the
117
+ # java api instead of thrift.
118
+ #
119
+ def atomic_increment(column_name, by = 1)
120
+ # @table.client.atomicIncrement(@table.name, id.to_s, column_name, by)
121
+ value_to_increment = @columns[column_name.to_s].value
122
+
123
+ raise "Value to increment (#{value_to_increment}) doesnt seem to be a number!" unless value_to_increment =~ /^\d+$/
124
+ raise "Argument by must be an integer" unless by.is_a? Fixnum
125
+
126
+ value_to_increment = value_to_increment.to_i
127
+ value_to_increment += by
128
+ value_to_increment = value_to_increment.to_s
129
+
130
+ mutation = Apache::Hadoop::Hbase::Thrift::Mutation.new
131
+ mutation.column = column_name
132
+ mutation.value = value_to_increment
133
+
134
+ if @table.client.mutateRow(@table.name, id.to_s, [mutation]).nil?
135
+ value_to_increment
136
+ end
137
+ end
138
+
139
+ def self.populate_from_trow_result(result, connection, table_name, column_families = [])
140
+ row = self.new
141
+ row.id = result.row
142
+ row.new_record = false
143
+ row.table = Table.new(connection, table_name)
144
+ row.column_families = column_families
145
+
146
+ result.columns.each do |name, value|
147
+ row.columns[name] = Cell.new({
148
+ :value => value.value,
149
+ :created_at => Time.at(value.timestamp / 1000, (value.timestamp % 1000) * 1000)
150
+ })
151
+ end
152
+
153
+ row
154
+ end
155
+
156
+ def destroy
157
+ @table.client.deleteAllRow(@table.name, @id).nil?
158
+ end
159
+
160
+ def new_record?
161
+ @new_record
162
+ end
163
+
164
+ def prev
165
+ self
166
+ end
167
+
168
+ end
169
+ end
170
+ end