massive_record 0.2.0.beta2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,11 +1,24 @@
1
- # v0.2.0.beta2 (git develop)
1
+ # v0.2.1 (git develop)
2
+
3
+
4
+ # v0.2.0 (git master)
5
+
6
+ - Intersection and union operations on arrays containing MassiveRecord objects is now working as expected.
7
+ - You can now disallow nil values, and in that case we will ensure that given field has its default value.
8
+ - Rails will now handle MassiveRecord::ORM::RecordNotFound correctly in a production environment, rendering 404.
9
+ - record.attributes no longer returns internal @attributes-hash directly. Instead it iterates over all attributes,
10
+ fetches their values through read_attribute and then returns a new hash with these attribute-name-values pairs.
11
+ - Fixed problem with STI sub classes in Rails development environment: Attributes defined in a sub class
12
+ was not loaded correctly first time you loaded a record through its parent class.
13
+
14
+ # v0.2.0.beta2
2
15
 
3
16
  - We are now raising error if MassiveRecordClass.new(attributes) receives unknown attributes.
4
17
  - Added support for Record.new(:references_many => [record, record]) and a_record.references_many = [record, record]
5
18
 
6
19
 
7
20
 
8
- # v0.2.0.beta (git master)
21
+ # v0.2.0.beta
9
22
 
10
23
  - ORM will now take care of serialize and de-serialize of attributes like arrays, hashes etc. It is doing so
11
24
  based on the type of your fields. You can select either JSON or YAML serialization for your data. As a default it
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- massive_record (0.2.0.beta2)
4
+ massive_record (0.2.0)
5
5
  activemodel
6
6
  activesupport
7
7
  thrift (>= 0.5.0)
@@ -9,11 +9,11 @@ PATH
9
9
  GEM
10
10
  remote: http://rubygems.org/
11
11
  specs:
12
- activemodel (3.0.5)
13
- activesupport (= 3.0.5)
12
+ activemodel (3.0.7)
13
+ activesupport (= 3.0.7)
14
14
  builder (~> 2.1.2)
15
- i18n (~> 0.4)
16
- activesupport (3.0.5)
15
+ i18n (~> 0.5.0)
16
+ activesupport (3.0.7)
17
17
  builder (2.1.2)
18
18
  diff-lcs (1.1.2)
19
19
  i18n (0.5.0)
@@ -21,7 +21,7 @@ GEM
21
21
  rspec-core (~> 2.5.0)
22
22
  rspec-expectations (~> 2.5.0)
23
23
  rspec-mocks (~> 2.5.0)
24
- rspec-core (2.5.1)
24
+ rspec-core (2.5.2)
25
25
  rspec-expectations (2.5.0)
26
26
  diff-lcs (~> 1.1.2)
27
27
  rspec-mocks (2.5.0)
data/README.md CHANGED
@@ -13,6 +13,9 @@ http://jimbojw.com/wiki/index.php?title=Understanding_Hbase_and_BigTable
13
13
 
14
14
  ## Installation
15
15
 
16
+ First of all: Please make sure you are using Ruby 1.9.2. For now, we are only ensuring
17
+ that Massive Record works on that Ruby version, and we know it has some problems with 1.8.7.
18
+
16
19
  gem install massive_record
17
20
 
18
21
  ### Ruby on Rails
@@ -83,9 +86,10 @@ Here is an example of usage, both for Table and Column:
83
86
  field :email
84
87
  field :phone_number
85
88
  field :points, :integer, :default => 0
86
- field :date_of_birth, :date
89
+ field :date_of_birth, :date, :allow_nil => false # Defaults to today
87
90
  field :newsletter, :boolean, :default => false
88
91
  field :type # Used for single table inheritance
92
+ field :in_the_future, :time, :default => Proc.new { 2.hours.from_now }
89
93
 
90
94
  timestamps # ..or field :created_at, :time
91
95
  end
@@ -119,6 +123,23 @@ Here is an example of usage, both for Table and Column:
119
123
  end
120
124
 
121
125
 
126
+ ### Related gems
127
+
128
+ We have developed some gems which adds support for MassiveRecord. These are:
129
+
130
+ #### ORM Adapter
131
+ https://github.com/CompanyBook/orm_adapter
132
+ Used by Devise. I guess we'll might release the code used to get Devise support in MR.
133
+
134
+ #### Database Cleaner
135
+ https://github.com/CompanyBook/database_cleaner
136
+ User by for instance Cucumber and ourself with Rspec.
137
+
138
+ #### Sunspot Rails
139
+ https://github.com/CompanyBook/sunspot_massive_record
140
+ Makes it easier to make things searchable with solr.
141
+
142
+
122
143
  ## Wrapper (adapter) API
123
144
 
124
145
  You can, if you'd like, work directly against the adapter.
@@ -76,7 +76,7 @@ module MassiveRecord
76
76
  mutations.push(m)
77
77
  end
78
78
 
79
- @table.client.mutateRow(@table.name, id.to_s, mutations).nil?
79
+ @table.client.mutateRow(@table.name, id.to_s.dup.force_encoding(Encoding::BINARY), mutations).nil?
80
80
  end
81
81
 
82
82
 
@@ -12,7 +12,7 @@ module MassiveRecord
12
12
 
13
13
 
14
14
  def attributes
15
- @attributes ||= {}
15
+ Hash[@attributes.collect { |attr_name, raw_value| [attr_name, read_attribute(attr_name)] }]
16
16
  end
17
17
 
18
18
  def attributes=(new_attributes)
@@ -56,6 +56,12 @@ module MassiveRecord
56
56
  attributes.merge! self.class.default_attributes_from_schema if self.class.respond_to? :default_attributes_from_schema
57
57
  attributes
58
58
  end
59
+
60
+ def fill_attributes_with_default_values_where_nil_is_not_allowed
61
+ attributes_schema.reject { |attr_name, field| field.allow_nil? || self[attr_name].present? }.each do |attr_name, field|
62
+ self[attr_name] = field.default
63
+ end
64
+ end
59
65
  end
60
66
  end
61
67
  end
@@ -128,6 +128,9 @@ module MassiveRecord
128
128
  # trust the coder's attributes and not load it with default values.
129
129
  #
130
130
  # class Person < MassiveRecord::ORM::Table
131
+ # column_family :base do
132
+ # field :name
133
+ # end
131
134
  # end
132
135
  #
133
136
  # person = Person.allocate
@@ -139,6 +142,7 @@ module MassiveRecord
139
142
  @relation_proxy_cache = {}
140
143
 
141
144
  self.attributes_raw = coder['attributes']
145
+ fill_attributes_with_default_values_where_nil_is_not_allowed
142
146
 
143
147
  _run_find_callbacks
144
148
  _run_initialize_callbacks
@@ -152,6 +156,9 @@ module MassiveRecord
152
156
  end
153
157
  alias_method :eql?, :==
154
158
 
159
+ def hash
160
+ id.hash
161
+ end
155
162
 
156
163
  def freeze
157
164
  @attributes.freeze
@@ -139,7 +139,7 @@ module MassiveRecord
139
139
  end
140
140
 
141
141
  if metadata.persisting_foreign_key?
142
- add_field_to_column_family(metadata.store_in, metadata.foreign_key, :type => :array, :default => [])
142
+ add_field_to_column_family(metadata.store_in, metadata.foreign_key, :type => :array, :allow_nil => false)
143
143
  end
144
144
  end
145
145
  end
@@ -4,10 +4,21 @@ module MassiveRecord
4
4
  class Field
5
5
  include ActiveModel::Validations
6
6
 
7
- TYPES = [:string, :integer, :float, :boolean, :array, :hash, :date, :time]
7
+ TYPES_DEFAULTS_TO = {
8
+ :string => '',
9
+ :integer => 0,
10
+ :float => 0.0,
11
+ :boolean => false,
12
+ :array => [],
13
+ :hash => {},
14
+ :date => lambda { Date.today },
15
+ :time => lambda { Time.now }
16
+ }
17
+
18
+ TYPES = TYPES_DEFAULTS_TO.keys
8
19
 
9
20
  attr_writer :default
10
- attr_accessor :name, :column, :type, :fields, :coder
21
+ attr_accessor :name, :column, :type, :fields, :coder, :allow_nil
11
22
 
12
23
 
13
24
  validates_presence_of :name
@@ -40,6 +51,7 @@ module MassiveRecord
40
51
  self.column = options[:column]
41
52
  self.type = options[:type] || :string
42
53
  self.default = options[:default]
54
+ self.allow_nil = options.has_key?(:allow_nil) ? options[:allow_nil] : true
43
55
 
44
56
  self.coder = options[:coder] || Base.coder
45
57
 
@@ -67,7 +79,12 @@ module MassiveRecord
67
79
  end
68
80
 
69
81
  def default
70
- @default.duplicable? ? @default.dup : @default
82
+ @default = TYPES_DEFAULTS_TO[type] if !allow_nil? && @default.nil?
83
+ if @default.respond_to? :call
84
+ @default.call
85
+ else
86
+ @default.duplicable? ? @default.dup : @default
87
+ end
71
88
  end
72
89
 
73
90
 
@@ -85,6 +102,9 @@ module MassiveRecord
85
102
  @column = column
86
103
  end
87
104
 
105
+ def allow_nil?
106
+ !!allow_nil
107
+ end
88
108
 
89
109
 
90
110
 
@@ -5,15 +5,43 @@ module MassiveRecord
5
5
 
6
6
  included do
7
7
  after_initialize :ensure_proper_type
8
+ end
9
+
10
+
11
+ module ClassMethods
12
+
13
+ private
14
+
15
+ #
16
+ # In Rails development environment class files are not required before they are needed.
17
+ #
18
+ # transpose_hbase_columns_to_record_attributes uses attributes_schema and
19
+ # for attributes_schema to have loaded all of it's fields correctly we need
20
+ # to make sure Rails loads class file before attributes_schema renders it's schema.
21
+ #
22
+ def transpose_hbase_columns_to_record_attributes(row) # :nodoc:
23
+ if field = attributes_schema[inheritance_attribute]
24
+ if cell_with_record_sti_class = row.columns[field.unique_name] and cell_with_record_sti_class.present?
25
+ if klass = field.decode(cell_with_record_sti_class.value) and klass.present?
26
+ ensure_sti_class_is_loaded(klass)
27
+ end
28
+ end
29
+ end
8
30
 
9
-
31
+ super
32
+ end
33
+
34
+ def ensure_sti_class_is_loaded(klass) # :nodoc:
35
+ klass.constantize
36
+ end
10
37
  end
11
38
 
39
+
12
40
  def ensure_proper_type
13
- attr = self.class.inheritance_attribute
41
+ inheritance_attribute = self.class.inheritance_attribute
14
42
 
15
- if respond_to?(attr) && self[attr].blank? && self.class.base_class != self.class
16
- self[attr] = self.class.to_s
43
+ if respond_to?(inheritance_attribute) && self[inheritance_attribute].blank? && self.class.base_class != self.class
44
+ self[inheritance_attribute] = self.class.to_s
17
45
  end
18
46
  end
19
47
  end
@@ -4,6 +4,12 @@ module MassiveRecord
4
4
  initializer "massive_record.logger" do
5
5
  MassiveRecord::ORM::Base.logger = ::Rails.logger
6
6
  end
7
+
8
+ initializer "massive_record.action_dispatch" do
9
+ ActiveSupport.on_load :action_controller do
10
+ ActionDispatch::ShowExceptions.rescue_responses.update('MassiveRecord::ORM::RecordNotFound' => :not_found)
11
+ end
12
+ end
7
13
  end
8
14
  end
9
15
  end
@@ -1,3 +1,3 @@
1
1
  module MassiveRecord
2
- VERSION = "0.2.0.beta2"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -1,5 +1,4 @@
1
1
  require 'yaml'
2
- require 'json'
3
2
 
4
3
  require 'massive_record/wrapper/adapter'
5
4
  require 'massive_record/wrapper/errors'
@@ -24,4 +23,4 @@ module MassiveRecord
24
23
 
25
24
  end
26
25
  end
27
- end
26
+ end
@@ -28,8 +28,19 @@ describe "attribute methods" do
28
28
  @model.read_attribute(:age).should == 15
29
29
  end
30
30
 
31
- it "should contains the id in the attributes getter" do
32
- @model.attributes.should include("id")
31
+ describe "#attributes" do
32
+ it "should contain the id" do
33
+ @model.attributes.should include("id")
34
+ end
35
+
36
+ it "should not return @attributes directly" do
37
+ @model.attributes.object_id.should_not == @model.instance_variable_get(:@attributes).object_id
38
+ end
39
+
40
+ it "should ask read_attribute for help" do
41
+ @model.should_receive(:read_attribute).any_number_of_times.and_return("stub")
42
+ @model.attributes['name'].should eq 'stub'
43
+ end
33
44
  end
34
45
 
35
46
  describe "#attributes=" do
@@ -91,6 +91,28 @@ describe MassiveRecord::ORM::Base do
91
91
  model.foo.should == 'bar'
92
92
  end
93
93
 
94
+ it "should set attributes where nil is not allowed if it is not included in attributes list" do
95
+ model = TestClass.allocate
96
+ model.init_with 'attributes' => {:foo => 'bar'}
97
+ model.hash_not_allow_nil.should == {}
98
+ end
99
+
100
+ it "should set attributes where nil is not allowed if it is included, but the value is nil" do
101
+ model = TestClass.allocate
102
+ model.init_with 'attributes' => {:hash_not_allow_nil => nil, :foo => 'bar'}
103
+ model.hash_not_allow_nil.should == {}
104
+ end
105
+
106
+ it "should not set override attributes where nil is allowed" do
107
+ model = TestClass.allocate
108
+ model.init_with 'attributes' => {:foo => nil}
109
+ model.foo.should be_nil
110
+ end
111
+
112
+ it "should set attributes where nil is not allowed when calling new" do
113
+ TestClass.new.hash_not_allow_nil.should == {}
114
+ end
115
+
94
116
  it "should stringify keys set on attributes" do
95
117
  model = TestClass.allocate
96
118
  model.init_with 'attributes' => {:foo => :bar}
@@ -129,6 +151,24 @@ describe MassiveRecord::ORM::Base do
129
151
  end
130
152
  end
131
153
 
154
+ describe "intersection and union operation" do
155
+ it "should correctly find intersection two sets" do
156
+ ([Person.find(1)] & [Person.find(1), Person.find(2)]).should == [Person.find(1)]
157
+ end
158
+
159
+ it "should correctly find union of two sets" do
160
+ ([Person.find(1)] | [Person.find(1), Person.find(2)]).should == [Person.find(1), Person.find(2)]
161
+ end
162
+
163
+ it "should correctly find intersection between two sets with different classes" do
164
+ ([Person.find(1)] & [TestClass.find(1)]).should == []
165
+ end
166
+
167
+ it "should correctly find union between two sets with different classes" do
168
+ ([Person.find(1)] | [TestClass.find(1)]).should == [Person.find(1), TestClass.find(1)]
169
+ end
170
+ end
171
+
132
172
  describe "#to_param" do
133
173
  it "should return nil if new record" do
134
174
  TestClass.new.to_param.should be_nil
@@ -4,5 +4,6 @@ class TestClass < MassiveRecord::ORM::Table
4
4
 
5
5
  column_family :test_family do
6
6
  field :foo, :string
7
+ field :hash_not_allow_nil, :hash, :allow_nil => false
7
8
  end
8
9
  end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe MassiveRecord::ORM::Schema::Field do
4
4
  describe "initializer" do
5
- %w(name column default).each do |attr_name|
5
+ %w(name column default allow_nil).each do |attr_name|
6
6
  it "should set #{attr_name}" do
7
7
  field = MassiveRecord::ORM::Schema::Field.new attr_name => "a_value"
8
8
  field.send(attr_name).should == "a_value"
@@ -16,6 +16,10 @@ describe MassiveRecord::ORM::Schema::Field do
16
16
  it "should default to type string" do
17
17
  MassiveRecord::ORM::Schema::Field.new(:name => "a_value").type.should == :string
18
18
  end
19
+
20
+ it "should default allow nil to true" do
21
+ MassiveRecord::ORM::Schema::Field.new(:name => "a_value").allow_nil.should be_true
22
+ end
19
23
  end
20
24
 
21
25
  describe "new_with_arguments_from_dsl" do
@@ -292,4 +296,59 @@ describe MassiveRecord::ORM::Schema::Field do
292
296
  field = MassiveRecord::ORM::Schema::Field.new :name => "array", :type => :array, :default => default_array
293
297
  field.default.object_id.should_not == default_array.object_id
294
298
  end
299
+
300
+
301
+ describe "default values" do
302
+ it "should be able to set to a proc" do
303
+ subject.type = :string
304
+ subject.default = Proc.new { "foo" }
305
+ subject.default.should == "foo"
306
+ end
307
+
308
+ context "when nil is allowed" do
309
+ MassiveRecord::ORM::Schema::Field::TYPES_DEFAULTS_TO.each do |type, default|
310
+ default = default.respond_to?(:call) ? default.call : default
311
+
312
+ it "should should default to nil" do
313
+ subject.type = type
314
+ subject.default.should == nil
315
+ end
316
+
317
+ it "should default to set value" do
318
+ subject.type = type
319
+ subject.default = default
320
+ subject.default.should == default
321
+ end
322
+ end
323
+ end
324
+
325
+
326
+ context "when nil is not allowed" do
327
+ subject { MassiveRecord::ORM::Schema::Field.new(:name => :test, :allow_nil => false) }
328
+
329
+ it { should_not be_allow_nil }
330
+
331
+ MassiveRecord::ORM::Schema::Field::TYPES_DEFAULTS_TO.reject { |type| type == :time }.each do |type, default|
332
+ default = default.respond_to?(:call) ? default.call : default
333
+
334
+ it "should default to #{default} when type is #{type}" do
335
+ subject.type = type
336
+ subject.default.should == default
337
+ end
338
+ end
339
+
340
+ it "should default to Time.now when type is time" do
341
+ subject.type = :time
342
+ time = Time.now
343
+ Time.should_receive(:now).and_return(time)
344
+ subject.default.should == time
345
+ end
346
+
347
+ it "should be possible to override the default nil-not-allowed-value" do
348
+ subject.type = :hash
349
+ subject.default = {:foo => :bar}
350
+ subject.default.should == {:foo => :bar}
351
+ end
352
+ end
353
+ end
295
354
  end
@@ -43,7 +43,7 @@ module CreatePersonBeforeEach
43
43
 
44
44
  @row = MassiveRecord::Wrapper::Row.new
45
45
  @row.id = "ID1"
46
- @row.values = {:info => {:name => "John Doe", :email => "john@base.com", :age => "20"}}
46
+ @row.values = {:info => {:name => "John Doe", :email => "john@base.com", :age => "20", :test_class_ids => [].to_json}}
47
47
  @row.table = @table
48
48
  @row.save
49
49
  end
metadata CHANGED
@@ -1,13 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: massive_record
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: true
5
- segments:
6
- - 0
7
- - 2
8
- - 0
9
- - beta2
10
- version: 0.2.0.beta2
4
+ prerelease:
5
+ version: 0.2.0
11
6
  platform: ruby
12
7
  authors:
13
8
  - Companybook
@@ -15,8 +10,7 @@ autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
12
 
18
- date: 2011-04-08 00:00:00 +02:00
19
- default_executable:
13
+ date: 2011-05-11 00:00:00 Z
20
14
  dependencies:
21
15
  - !ruby/object:Gem::Dependency
22
16
  name: thrift
@@ -26,10 +20,6 @@ dependencies:
26
20
  requirements:
27
21
  - - ">="
28
22
  - !ruby/object:Gem::Version
29
- segments:
30
- - 0
31
- - 5
32
- - 0
33
23
  version: 0.5.0
34
24
  type: :runtime
35
25
  version_requirements: *id001
@@ -41,8 +31,6 @@ dependencies:
41
31
  requirements:
42
32
  - - ">="
43
33
  - !ruby/object:Gem::Version
44
- segments:
45
- - 0
46
34
  version: "0"
47
35
  type: :runtime
48
36
  version_requirements: *id002
@@ -54,8 +42,6 @@ dependencies:
54
42
  requirements:
55
43
  - - ">="
56
44
  - !ruby/object:Gem::Version
57
- segments:
58
- - 0
59
45
  version: "0"
60
46
  type: :runtime
61
47
  version_requirements: *id003
@@ -67,10 +53,6 @@ dependencies:
67
53
  requirements:
68
54
  - - ">="
69
55
  - !ruby/object:Gem::Version
70
- segments:
71
- - 2
72
- - 1
73
- - 0
74
56
  version: 2.1.0
75
57
  type: :development
76
58
  version_requirements: *id004
@@ -200,7 +182,6 @@ files:
200
182
  - spec/wrapper/cases/adapter_spec.rb
201
183
  - spec/wrapper/cases/connection_spec.rb
202
184
  - spec/wrapper/cases/table_spec.rb
203
- has_rdoc: true
204
185
  homepage: http://github.com/CompanyBook/massive_record
205
186
  licenses: []
206
187
 
@@ -214,23 +195,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
214
195
  requirements:
215
196
  - - ">="
216
197
  - !ruby/object:Gem::Version
217
- segments:
218
- - 0
219
198
  version: "0"
220
199
  required_rubygems_version: !ruby/object:Gem::Requirement
221
200
  none: false
222
201
  requirements:
223
- - - ">"
202
+ - - ">="
224
203
  - !ruby/object:Gem::Version
225
- segments:
226
- - 1
227
- - 3
228
- - 1
229
- version: 1.3.1
204
+ version: "0"
230
205
  requirements: []
231
206
 
232
207
  rubyforge_project: massive_record
233
- rubygems_version: 1.3.7
208
+ rubygems_version: 1.7.2
234
209
  signing_key:
235
210
  specification_version: 3
236
211
  summary: HBase Ruby client API