massive_record 0.2.1 → 0.2.2.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. data/CHANGELOG.md +58 -2
  2. data/Gemfile.lock +17 -17
  3. data/README.md +98 -41
  4. data/lib/massive_record.rb +2 -1
  5. data/lib/massive_record/adapters/thrift/hbase/hbase.rb +2425 -2154
  6. data/lib/massive_record/adapters/thrift/hbase/hbase_constants.rb +3 -3
  7. data/lib/massive_record/adapters/thrift/hbase/hbase_types.rb +195 -195
  8. data/lib/massive_record/adapters/thrift/row.rb +35 -4
  9. data/lib/massive_record/adapters/thrift/table.rb +49 -12
  10. data/lib/massive_record/orm/attribute_methods.rb +77 -5
  11. data/lib/massive_record/orm/attribute_methods/cast_numbers_on_write.rb +24 -0
  12. data/lib/massive_record/orm/attribute_methods/dirty.rb +18 -0
  13. data/lib/massive_record/orm/attribute_methods/time_zone_conversion.rb +24 -3
  14. data/lib/massive_record/orm/attribute_methods/write.rb +8 -1
  15. data/lib/massive_record/orm/base.rb +62 -8
  16. data/lib/massive_record/orm/column.rb +7 -11
  17. data/lib/massive_record/orm/default_id.rb +1 -1
  18. data/lib/massive_record/orm/embedded.rb +66 -0
  19. data/lib/massive_record/orm/errors.rb +17 -0
  20. data/lib/massive_record/orm/finders.rb +124 -71
  21. data/lib/massive_record/orm/finders/rescue_missing_table_on_find.rb +1 -1
  22. data/lib/massive_record/orm/finders/scope.rb +58 -34
  23. data/lib/massive_record/orm/id_factory.rb +22 -105
  24. data/lib/massive_record/orm/id_factory/atomic_incrementation.rb +117 -0
  25. data/lib/massive_record/orm/id_factory/timestamp.rb +60 -0
  26. data/lib/massive_record/orm/identity_map.rb +256 -0
  27. data/lib/massive_record/orm/log_subscriber.rb +18 -0
  28. data/lib/massive_record/orm/observer.rb +69 -0
  29. data/lib/massive_record/orm/persistence.rb +47 -119
  30. data/lib/massive_record/orm/persistence/operations.rb +100 -0
  31. data/lib/massive_record/orm/persistence/operations/atomic_operation.rb +71 -0
  32. data/lib/massive_record/orm/persistence/operations/destroy.rb +17 -0
  33. data/lib/massive_record/orm/persistence/operations/embedded/destroy.rb +26 -0
  34. data/lib/massive_record/orm/persistence/operations/embedded/insert.rb +27 -0
  35. data/lib/massive_record/orm/persistence/operations/embedded/operation_helpers.rb +66 -0
  36. data/lib/massive_record/orm/persistence/operations/embedded/reload.rb +39 -0
  37. data/lib/massive_record/orm/persistence/operations/embedded/update.rb +29 -0
  38. data/lib/massive_record/orm/persistence/operations/insert.rb +19 -0
  39. data/lib/massive_record/orm/persistence/operations/reload.rb +26 -0
  40. data/lib/massive_record/orm/persistence/operations/suppress.rb +15 -0
  41. data/lib/massive_record/orm/persistence/operations/table_operation_helpers.rb +106 -0
  42. data/lib/massive_record/orm/persistence/operations/update.rb +25 -0
  43. data/lib/massive_record/orm/query_instrumentation.rb +26 -49
  44. data/lib/massive_record/orm/raw_data.rb +47 -0
  45. data/lib/massive_record/orm/relations.rb +4 -0
  46. data/lib/massive_record/orm/relations/interface.rb +134 -0
  47. data/lib/massive_record/orm/relations/metadata.rb +58 -12
  48. data/lib/massive_record/orm/relations/proxy.rb +17 -12
  49. data/lib/massive_record/orm/relations/proxy/embedded_in.rb +54 -0
  50. data/lib/massive_record/orm/relations/proxy/embedded_in_polymorphic.rb +15 -0
  51. data/lib/massive_record/orm/relations/proxy/embeds_many.rb +215 -0
  52. data/lib/massive_record/orm/relations/proxy/references_many.rb +112 -88
  53. data/lib/massive_record/orm/relations/proxy/references_one.rb +1 -1
  54. data/lib/massive_record/orm/relations/proxy/references_one_polymorphic.rb +1 -1
  55. data/lib/massive_record/orm/relations/proxy_collection.rb +84 -0
  56. data/lib/massive_record/orm/schema/column_family.rb +3 -2
  57. data/lib/massive_record/orm/schema/{column_interface.rb → embedded_interface.rb} +38 -4
  58. data/lib/massive_record/orm/schema/field.rb +2 -0
  59. data/lib/massive_record/orm/schema/table_interface.rb +19 -2
  60. data/lib/massive_record/orm/single_table_inheritance.rb +37 -2
  61. data/lib/massive_record/orm/timestamps.rb +17 -7
  62. data/lib/massive_record/orm/validations.rb +4 -0
  63. data/lib/massive_record/orm/validations/associated.rb +50 -0
  64. data/lib/massive_record/rails/railtie.rb +31 -0
  65. data/lib/massive_record/version.rb +1 -1
  66. data/lib/massive_record/wrapper/cell.rb +8 -1
  67. data/massive_record.gemspec +4 -4
  68. data/spec/adapter/thrift/atomic_increment_spec.rb +16 -0
  69. data/spec/adapter/thrift/table_find_spec.rb +14 -2
  70. data/spec/adapter/thrift/table_spec.rb +6 -6
  71. data/spec/adapter/thrift/utf8_encoding_of_id_spec.rb +71 -0
  72. data/spec/orm/cases/attribute_methods_spec.rb +215 -22
  73. data/spec/orm/cases/auto_generate_id_spec.rb +1 -1
  74. data/spec/orm/cases/change_id_spec.rb +62 -0
  75. data/spec/orm/cases/default_id_spec.rb +25 -6
  76. data/spec/orm/cases/default_values_spec.rb +6 -3
  77. data/spec/orm/cases/dirty_spec.rb +150 -102
  78. data/spec/orm/cases/embedded_spec.rb +250 -0
  79. data/spec/orm/cases/{finder_default_scope.rb → finder_default_scope_spec.rb} +4 -0
  80. data/spec/orm/cases/finder_scope_spec.rb +96 -29
  81. data/spec/orm/cases/finders_spec.rb +57 -10
  82. data/spec/orm/cases/id_factory/atomic_incrementation_spec.rb +72 -0
  83. data/spec/orm/cases/id_factory/timestamp_spec.rb +61 -0
  84. data/spec/orm/cases/identity_map/identity_map_spec.rb +357 -0
  85. data/spec/orm/cases/identity_map/middleware_spec.rb +74 -0
  86. data/spec/orm/cases/log_subscriber_spec.rb +15 -2
  87. data/spec/orm/cases/observing_spec.rb +61 -0
  88. data/spec/orm/cases/persistence_spec.rb +151 -60
  89. data/spec/orm/cases/raw_data_spec.rb +58 -0
  90. data/spec/orm/cases/single_table_inheritance_spec.rb +58 -2
  91. data/spec/orm/cases/table_spec.rb +3 -3
  92. data/spec/orm/cases/time_zone_awareness_spec.rb +27 -0
  93. data/spec/orm/cases/timestamps_spec.rb +23 -109
  94. data/spec/orm/cases/validation_spec.rb +9 -0
  95. data/spec/orm/models/address.rb +5 -1
  96. data/spec/orm/models/address_with_timestamp.rb +12 -0
  97. data/spec/orm/models/car.rb +5 -0
  98. data/spec/orm/models/person.rb +13 -1
  99. data/spec/orm/models/person_with_timestamp.rb +4 -2
  100. data/spec/orm/models/test_class.rb +1 -0
  101. data/spec/orm/persistence/operations/atomic_operation_spec.rb +58 -0
  102. data/spec/orm/persistence/operations/destroy_spec.rb +22 -0
  103. data/spec/orm/persistence/operations/embedded/destroy_spec.rb +71 -0
  104. data/spec/orm/persistence/operations/embedded/insert_spec.rb +59 -0
  105. data/spec/orm/persistence/operations/embedded/operation_helpers_spec.rb +92 -0
  106. data/spec/orm/persistence/operations/embedded/reload_spec.rb +67 -0
  107. data/spec/orm/persistence/operations/embedded/update_spec.rb +60 -0
  108. data/spec/orm/persistence/operations/insert_spec.rb +31 -0
  109. data/spec/orm/persistence/operations/reload_spec.rb +48 -0
  110. data/spec/orm/persistence/operations/suppress_spec.rb +17 -0
  111. data/spec/orm/persistence/operations/table_operation_helpers_spec.rb +98 -0
  112. data/spec/orm/persistence/operations/update_spec.rb +25 -0
  113. data/spec/orm/persistence/operations_spec.rb +58 -0
  114. data/spec/orm/relations/interface_spec.rb +188 -0
  115. data/spec/orm/relations/metadata_spec.rb +92 -15
  116. data/spec/orm/relations/proxy/embedded_in_polymorphic_spec.rb +37 -0
  117. data/spec/orm/relations/proxy/embedded_in_spec.rb +66 -0
  118. data/spec/orm/relations/proxy/embeds_many_spec.rb +651 -0
  119. data/spec/orm/relations/proxy/references_many_spec.rb +466 -2
  120. data/spec/orm/schema/column_family_spec.rb +21 -0
  121. data/spec/orm/schema/embedded_interface_spec.rb +181 -0
  122. data/spec/orm/schema/field_spec.rb +7 -0
  123. data/spec/orm/schema/table_interface_spec.rb +31 -1
  124. data/spec/shared/orm/id_factories.rb +44 -0
  125. data/spec/shared/orm/model_with_timestamps.rb +132 -0
  126. data/spec/shared/orm/persistence/a_persistence_embedded_operation_class.rb +3 -0
  127. data/spec/shared/orm/persistence/a_persistence_operation_class.rb +11 -0
  128. data/spec/shared/orm/persistence/a_persistence_table_operation_class.rb +11 -0
  129. data/spec/shared/orm/relations/proxy.rb +9 -2
  130. data/spec/spec_helper.rb +9 -0
  131. data/spec/support/mock_massive_record_connection.rb +2 -1
  132. metadata +106 -21
  133. data/spec/orm/cases/column_spec.rb +0 -49
  134. data/spec/orm/cases/id_factory_spec.rb +0 -92
  135. data/spec/orm/schema/column_interface_spec.rb +0 -136
@@ -5,6 +5,27 @@ module MassiveRecord
5
5
 
6
6
  attr_accessor :connection, :name, :column_families
7
7
 
8
+ #
9
+ # TODO
10
+ # Helper method to inform about changed options. Remove this in next version..
11
+ # Also note that this method is used other places to wrap same functionality.
12
+ #
13
+ def self.warn_and_change_deprecated_finder_options(options)
14
+ deprecations = {
15
+ :start => :starts_with
16
+ }
17
+
18
+ deprecations.each do |deprecated, current|
19
+ if options.has_key? deprecated
20
+ # TODO remove this for next version
21
+ ActiveSupport::Deprecation.warn("finder option '#{deprecated}' is deprecated. Please use: '#{current}'")
22
+ options[current] = options.delete deprecated
23
+ end
24
+ end
25
+
26
+ options
27
+ end
28
+
8
29
  def initialize(connection, table_name)
9
30
  @connection = connection
10
31
  @name = table_name.to_s
@@ -87,9 +108,14 @@ module MassiveRecord
87
108
  end
88
109
 
89
110
  def format_options_for_scanner(opts = {})
111
+ opts = self.class.warn_and_change_deprecated_finder_options(opts)
112
+
113
+ start = opts[:starts_with] && opts[:starts_with].dup.force_encoding(Encoding::BINARY)
114
+ offset = opts[:offset] && opts[:offset].dup.force_encoding(Encoding::BINARY)
115
+
90
116
  {
91
- :start_key => opts[:start],
92
- :offset_key => opts[:offset],
117
+ :start_key => start,
118
+ :offset_key => offset,
93
119
  :created_at => opts[:created_at],
94
120
  :columns => opts[:select], # list of column families to fetch from hbase
95
121
  :limit => opts[:limit] || opts[:batch_size]
@@ -115,8 +141,16 @@ module MassiveRecord
115
141
  # table.get("my_id", :info, :name) # => "Bob"
116
142
  #
117
143
  def get(id, column_family_name, column_name)
118
- if value = connection.get(name, id, "#{column_family_name.to_s}:#{column_name.to_s}").first.try(:value)
119
- MassiveRecord::Wrapper::Cell.new(:value => value).value # might seems a bit strange.. Just to "enforice" that the value is a supported type
144
+ get_cell(id, column_family_name, column_name).try :value
145
+ end
146
+
147
+
148
+ #
149
+ # Fast way of fetching one cell
150
+ #
151
+ def get_cell(id, column_family_name, column_name)
152
+ if cell = connection.get(name, id.dup.force_encoding(Encoding::BINARY), "#{column_family_name.to_s}:#{column_name.to_s}").first
153
+ MassiveRecord::Wrapper::Cell.populate_from_tcell(cell)
120
154
  end
121
155
  end
122
156
 
@@ -126,17 +160,20 @@ module MassiveRecord
126
160
  # Returns nil if id is not found
127
161
  #
128
162
  def find(*args)
129
- what_to_find = args.first
130
163
  options = args.extract_options!.symbolize_keys
164
+ what_to_find = args.first
131
165
 
132
- if what_to_find.is_a?(Array)
133
- what_to_find.collect { |id| find(id, options) }
134
- else
135
- if column_families_to_find = options[:select]
136
- column_families_to_find = column_families_to_find.collect { |c| c.to_s }
137
- end
166
+ if column_families_to_find = options[:select]
167
+ column_families_to_find = column_families_to_find.collect { |c| c.to_s }
168
+ end
138
169
 
139
- if t_row_result = connection.getRowWithColumns(name, what_to_find, column_families_to_find).first
170
+ if what_to_find.is_a? Array
171
+ what_to_find.collect! { |id| id.dup.force_encoding(Encoding::BINARY) }
172
+ connection.getRowsWithColumns(name, what_to_find, column_families_to_find).collect do |t_row_result|
173
+ Row.populate_from_trow_result(t_row_result, connection, name, column_families_to_find)
174
+ end
175
+ else
176
+ if t_row_result = connection.getRowWithColumns(name, what_to_find.dup.force_encoding(Encoding::BINARY), column_families_to_find).first
140
177
  Row.populate_from_trow_result(t_row_result, connection, name, column_families_to_find)
141
178
  end
142
179
  end
@@ -37,14 +37,22 @@ module MassiveRecord
37
37
  def attributes=(new_attributes)
38
38
  return unless new_attributes.is_a?(Hash)
39
39
 
40
+ multiparameter_attributes = []
41
+
40
42
  sanitize_for_mass_assignment(new_attributes).each do |attr, value|
41
- writer_method = "#{attr}="
42
- if respond_to? writer_method
43
- send(writer_method, value)
43
+ if multiparameter? attr
44
+ multiparameter_attributes << [attr, value]
44
45
  else
45
- raise UnknownAttributeError.new("Unkown attribute: #{attr}")
46
+ writer_method = "#{attr}="
47
+ if respond_to? writer_method
48
+ send(writer_method, value)
49
+ else
50
+ raise UnknownAttributeError.new("Unkown attribute: #{attr}")
51
+ end
46
52
  end
47
53
  end
54
+
55
+ assign_multiparameter_attributes(multiparameter_attributes)
48
56
  end
49
57
 
50
58
 
@@ -84,10 +92,74 @@ module MassiveRecord
84
92
  end
85
93
 
86
94
  def fill_attributes_with_default_values_where_nil_is_not_allowed
87
- attributes_schema.reject { |attr_name, field| field.allow_nil? || self[attr_name].present? }.each do |attr_name, field|
95
+ attributes_to_fill = attributes_schema.reject do |attr_name, field|
96
+ field.allow_nil? || self[attr_name].present? || (field.type == :boolean && self[attr_name] == false)
97
+ end
98
+
99
+ attributes_to_fill.each do |attr_name, field|
88
100
  self[attr_name] = field.default
89
101
  end
90
102
  end
103
+
104
+
105
+
106
+
107
+
108
+
109
+ def multiparameter?(attr)
110
+ attr.to_s.include?('(')
111
+ end
112
+
113
+ def assign_multiparameter_attributes(attribute_pairs)
114
+ convert_multiparameter_pairs_to_hash_with_initializer_arguments(attribute_pairs).each do |attr_name, initialize_values|
115
+ if field = attributes_schema[attr_name]
116
+ value = begin
117
+ if initialize_values.any?
118
+ case field.type
119
+ when :date
120
+ initialize_values = initialize_values[0, 3]
121
+ initialize_values.collect! { |v| v.nil? ? 1 : v }
122
+ Date.new(*initialize_values)
123
+ when :time
124
+ initialize_values = initialize_values[0, 6]
125
+ initialize_values.collect! { |v| v.nil? ? 0 : v }
126
+ Time.new(*initialize_values)
127
+ end
128
+ end
129
+ rescue ArgumentError
130
+ nil
131
+ end
132
+
133
+ self[attr_name] = value
134
+ end
135
+ end
136
+ end
137
+
138
+ def convert_multiparameter_pairs_to_hash_with_initializer_arguments(attribute_pairs)
139
+ out = Hash.new { |h, k| h[k] = [] }.tap do |hash|
140
+ attribute_pairs.each do |pair|
141
+ name, value = pair
142
+ attr_name = name.split('(').first
143
+ hash[attr_name] << [multiparameter_position(name), type_cast_multiparameter_value(name, value)]
144
+ end
145
+ end
146
+
147
+ out.each do |attr_name, positions_and_values|
148
+ out[attr_name] = positions_and_values.sort_by(&:first).collect(&:last)
149
+ end
150
+
151
+ out
152
+ end
153
+
154
+ def type_cast_multiparameter_value(name, value)
155
+ unless value.empty?
156
+ name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
157
+ end
158
+ end
159
+
160
+ def multiparameter_position(name)
161
+ name.scan(/\(([0-9]*).*\)/).first.first
162
+ end
91
163
  end
92
164
  end
93
165
  end
@@ -0,0 +1,24 @@
1
+ module MassiveRecord
2
+ module ORM
3
+ module AttributeMethods
4
+ module CastNumbersOnWrite
5
+ extend ActiveSupport::Concern
6
+
7
+ def write_attribute(attr_name, value)
8
+ if value.present?
9
+ if field = attributes_schema[attr_name]
10
+ case field.type
11
+ when :integer
12
+ value = value.to_i
13
+ when :float
14
+ value = value.to_f
15
+ end
16
+ end
17
+ end
18
+
19
+ super
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -45,6 +45,24 @@ module MassiveRecord
45
45
  end
46
46
 
47
47
 
48
+ def changed?
49
+ super || relation_proxies_for_embedded.any?(&:changed?)
50
+ end
51
+
52
+ def changed
53
+ super + relation_proxies_for_embedded.select(&:changed?).collect { |proxy| proxy.metadata.name }
54
+ end
55
+
56
+ def changes
57
+ changes_in_embedded_proxies = {}
58
+
59
+ relation_proxies_for_embedded.select(&:changed?).each do |proxy|
60
+ changes_in_embedded_proxies[proxy.metadata.name] = proxy.changes
61
+ end
62
+
63
+ super.update(changes_in_embedded_proxies)
64
+ end
65
+
48
66
  private
49
67
 
50
68
  def update(*)
@@ -56,16 +56,37 @@ module MassiveRecord
56
56
  time.in_time_zone
57
57
  end
58
58
  end
59
+ alias_method attr_name, internal_read_method
59
60
  end
60
61
  else
61
62
  super
62
63
  end
63
64
  end
64
65
 
65
- # Redefine writer method if we are to do time zone configuration on field
66
66
  def define_method_attribute=(attr_name)
67
- # Nothing special goes on here, at the moment
68
- super
67
+ if time_zone_conversion_on_field?(attributes_schema[attr_name])
68
+ internal_write_method = "_#{attr_name}="
69
+
70
+ if attr_name =~ ActiveModel::AttributeMethods::COMPILABLE_REGEXP
71
+ generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__
72
+ def #{internal_write_method}(time)
73
+ time = Time.zone.parse(time) if time.is_a? String
74
+ #{attr_name}_will_change! if will_change_attribute? #{attr_name}, time
75
+ @attributes['#{attr_name}'] = time
76
+ end
77
+ alias #{attr_name}= #{internal_write_method}
78
+ RUBY
79
+ else
80
+ generated_attribute_methods.send(:define_method, internal_write_method) do |time|
81
+ time = Time.zone.parse(time) if time.is_a? String
82
+ send("#{attr_name}_will_change!") if will_change_attribute? attr_name, time
83
+ @attributes[attr_name] = time
84
+ end
85
+ alias_method "#{attr_name}=", internal_write_method
86
+ end
87
+ else
88
+ super
89
+ end
69
90
  end
70
91
 
71
92
 
@@ -28,7 +28,14 @@ module MassiveRecord
28
28
  end
29
29
 
30
30
  def write_attribute(attr_name, value)
31
- @attributes[attr_name.to_s] = value
31
+ attr_name = attr_name.to_s
32
+ internal_write_method = "_#{attr_name}="
33
+
34
+ if respond_to? internal_write_method
35
+ send(internal_write_method, value)
36
+ else
37
+ @attributes[attr_name.to_s] = value
38
+ end
32
39
  end
33
40
 
34
41
  private
@@ -18,15 +18,19 @@ require 'massive_record/orm/finders/rescue_missing_table_on_find'
18
18
  require 'massive_record/orm/attribute_methods'
19
19
  require 'massive_record/orm/attribute_methods/time_zone_conversion'
20
20
  require 'massive_record/orm/attribute_methods/write'
21
+ require 'massive_record/orm/attribute_methods/cast_numbers_on_write'
21
22
  require 'massive_record/orm/attribute_methods/read'
22
23
  require 'massive_record/orm/attribute_methods/dirty'
23
24
  require 'massive_record/orm/single_table_inheritance'
24
25
  require 'massive_record/orm/validations'
26
+ require 'massive_record/orm/validations/associated'
25
27
  require 'massive_record/orm/callbacks'
26
28
  require 'massive_record/orm/timestamps'
27
29
  require 'massive_record/orm/persistence'
28
30
  require 'massive_record/orm/default_id'
29
31
  require 'massive_record/orm/query_instrumentation'
32
+ require 'massive_record/orm/observer'
33
+ require 'massive_record/orm/identity_map'
30
34
 
31
35
 
32
36
  module MassiveRecord
@@ -75,8 +79,21 @@ module MassiveRecord
75
79
  class_attribute :check_record_uniqueness_on_create, :instance_writer => false
76
80
  self.check_record_uniqueness_on_create = false
77
81
 
78
- class_attribute :auto_increment_id, :instance_writer => false
79
- self.auto_increment_id = true
82
+
83
+ #
84
+ # Default id and id facyory settings
85
+ # Should we set automatically the id, and which factory should we ask?
86
+ #
87
+ # The default id factory is set when massive record is fully loaded, as
88
+ # it uses MassiveRecord itself to communicate with the database.
89
+ # Take a look inside of orm/id_factory.rb; we are utilizing the on_load hook.
90
+ #
91
+ class_attribute :id_factory, :instance_writer => false
92
+ class_attribute :set_id_from_factory_before_create, :instance_writer => false
93
+ self.set_id_from_factory_before_create = true
94
+
95
+
96
+
80
97
 
81
98
  class << self
82
99
  def table_name
@@ -146,11 +163,13 @@ module MassiveRecord
146
163
  @new_record = true
147
164
  @destroyed = @readonly = false
148
165
  @relation_proxy_cache = {}
166
+ @raw_data = Hash.new { |h,k| h[k] = {} }
167
+
168
+ clear_dirty_states!
149
169
 
150
170
  self.attributes_raw = attributes_from_field_definition.merge('id' => id)
151
171
  self.attributes = attributes
152
172
 
153
- clear_dirty_states!
154
173
 
155
174
  _run_initialize_callbacks
156
175
  end
@@ -176,7 +195,7 @@ module MassiveRecord
176
195
  @destroyed = @readonly = false
177
196
  @relation_proxy_cache = {}
178
197
 
179
- self.attributes_raw = coder['attributes']
198
+ reinit_with(coder)
180
199
  fill_attributes_with_default_values_where_nil_is_not_allowed
181
200
 
182
201
  _run_find_callbacks
@@ -185,6 +204,12 @@ module MassiveRecord
185
204
  self
186
205
  end
187
206
 
207
+ def reinit_with(coder) # :nodoc:
208
+ @raw_data = Hash.new { |h,k| h[k] = {} }
209
+ @raw_data.update(coder['raw_data']) if coder.has_key? 'raw_data'
210
+ self.attributes_raw = coder['attributes']
211
+ end
212
+
188
213
 
189
214
  def ==(other)
190
215
  other.equal?(self) || other.instance_of?(self.class) && id == other.id
@@ -197,6 +222,7 @@ module MassiveRecord
197
222
 
198
223
  def freeze
199
224
  @attributes.freeze
225
+ self
200
226
  end
201
227
 
202
228
  def frozen?
@@ -242,6 +268,31 @@ module MassiveRecord
242
268
  object.init_with('attributes' => attributes.select{|k| !['id', 'created_at', 'updated_at'].include?(k)})
243
269
  object
244
270
  end
271
+
272
+
273
+ #
274
+ # The raw data is raw values returned by the adapter.
275
+ # It is a nested hash like:
276
+ #
277
+ # {
278
+ # 'family' => {
279
+ # 'attr1' => 'value'
280
+ # 'attr2' => 'value'
281
+ # },
282
+ #
283
+ # 'addresses' => {
284
+ # 'address-1' => {'serialized' => 'attributes', 'for' => 'address-2'}
285
+ # }
286
+ # ...
287
+ # }
288
+ #
289
+ def raw_data
290
+ @raw_data.dup
291
+ end
292
+
293
+ def update_raw_data_for_column_family(column_family, new_values) # :nodoc:
294
+ @raw_data[column_family] = new_values
295
+ end
245
296
 
246
297
 
247
298
  private
@@ -271,7 +322,7 @@ module MassiveRecord
271
322
 
272
323
 
273
324
  def next_id
274
- IdFactory.next_for(self.class).to_s
325
+ id_factory.next_for(self.class).to_s
275
326
  end
276
327
  end
277
328
 
@@ -279,19 +330,21 @@ module MassiveRecord
279
330
  Base.class_eval do
280
331
  include Config
281
332
  include Persistence
282
- include Relations::Interface
283
333
  include Finders
334
+ include IdentityMap
284
335
  extend RescueMissingTableOnFind
285
336
  include AttributeMethods
337
+ include Relations::Interface
286
338
  include AttributeMethods::Write, AttributeMethods::Read
287
339
  include AttributeMethods::TimeZoneConversion
288
340
  include AttributeMethods::Dirty
341
+ include AttributeMethods::CastNumbersOnWrite
289
342
  include Validations
290
- include Callbacks
343
+ include Callbacks, ActiveModel::Observing
291
344
  include Timestamps
292
345
  include SingleTableInheritance
293
346
  include DefaultId
294
- include QueryInstrumentation
347
+ include QueryInstrumentation::Table
295
348
 
296
349
 
297
350
  alias [] read_attribute
@@ -302,6 +355,7 @@ end
302
355
 
303
356
  require 'massive_record/orm/table'
304
357
  require 'massive_record/orm/column'
358
+ require 'massive_record/orm/embedded'
305
359
  require 'massive_record/orm/id_factory'
306
360
  require 'massive_record/orm/log_subscriber'
307
361