massive_record 0.1.1 → 0.2.0.beta

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 (83) hide show
  1. data/CHANGELOG.md +28 -5
  2. data/Gemfile.lock +12 -12
  3. data/README.md +29 -1
  4. data/lib/massive_record/adapters/initialize.rb +18 -0
  5. data/lib/massive_record/adapters/thrift/adapter.rb +25 -0
  6. data/lib/massive_record/adapters/thrift/column_family.rb +24 -0
  7. data/lib/massive_record/adapters/thrift/connection.rb +73 -0
  8. data/lib/massive_record/{thrift → adapters/thrift/hbase}/hbase.rb +0 -0
  9. data/lib/massive_record/{thrift → adapters/thrift/hbase}/hbase_constants.rb +0 -0
  10. data/lib/massive_record/{thrift → adapters/thrift/hbase}/hbase_types.rb +0 -0
  11. data/lib/massive_record/adapters/thrift/row.rb +150 -0
  12. data/lib/massive_record/adapters/thrift/scanner.rb +59 -0
  13. data/lib/massive_record/adapters/thrift/table.rb +169 -0
  14. data/lib/massive_record/orm/attribute_methods/read.rb +2 -1
  15. data/lib/massive_record/orm/base.rb +61 -3
  16. data/lib/massive_record/orm/coders/chained.rb +71 -0
  17. data/lib/massive_record/orm/coders/json.rb +17 -0
  18. data/lib/massive_record/orm/coders/yaml.rb +15 -0
  19. data/lib/massive_record/orm/coders.rb +3 -0
  20. data/lib/massive_record/orm/errors.rb +15 -2
  21. data/lib/massive_record/orm/finders/scope.rb +166 -0
  22. data/lib/massive_record/orm/finders.rb +45 -24
  23. data/lib/massive_record/orm/persistence.rb +4 -4
  24. data/lib/massive_record/orm/relations/interface.rb +170 -0
  25. data/lib/massive_record/orm/relations/metadata.rb +150 -0
  26. data/lib/massive_record/orm/relations/proxy/references_many.rb +229 -0
  27. data/lib/massive_record/orm/relations/proxy/references_one.rb +40 -0
  28. data/lib/massive_record/orm/relations/proxy/references_one_polymorphic.rb +49 -0
  29. data/lib/massive_record/orm/relations/proxy.rb +174 -0
  30. data/lib/massive_record/orm/relations.rb +6 -0
  31. data/lib/massive_record/orm/schema/column_interface.rb +1 -1
  32. data/lib/massive_record/orm/schema/field.rb +62 -27
  33. data/lib/massive_record/orm/single_table_inheritance.rb +21 -0
  34. data/lib/massive_record/version.rb +1 -1
  35. data/lib/massive_record/wrapper/adapter.rb +6 -0
  36. data/lib/massive_record/wrapper/base.rb +6 -7
  37. data/lib/massive_record/wrapper/cell.rb +9 -32
  38. data/lib/massive_record/wrapper/column_families_collection.rb +2 -2
  39. data/lib/massive_record/wrapper/errors.rb +10 -0
  40. data/lib/massive_record/wrapper/tables_collection.rb +1 -1
  41. data/lib/massive_record.rb +5 -12
  42. data/spec/orm/cases/attribute_methods_spec.rb +5 -1
  43. data/spec/orm/cases/base_spec.rb +77 -4
  44. data/spec/orm/cases/column_spec.rb +1 -1
  45. data/spec/orm/cases/finder_default_scope.rb +53 -0
  46. data/spec/orm/cases/finder_scope_spec.rb +288 -0
  47. data/spec/orm/cases/finders_spec.rb +56 -13
  48. data/spec/orm/cases/persistence_spec.rb +20 -5
  49. data/spec/orm/cases/single_table_inheritance_spec.rb +26 -0
  50. data/spec/orm/cases/table_spec.rb +1 -1
  51. data/spec/orm/cases/timestamps_spec.rb +16 -16
  52. data/spec/orm/coders/chained_spec.rb +73 -0
  53. data/spec/orm/coders/json_spec.rb +6 -0
  54. data/spec/orm/coders/yaml_spec.rb +6 -0
  55. data/spec/orm/models/best_friend.rb +7 -0
  56. data/spec/orm/models/friend.rb +4 -0
  57. data/spec/orm/models/person.rb +20 -6
  58. data/spec/orm/models/{person_with_timestamps.rb → person_with_timestamp.rb} +1 -1
  59. data/spec/orm/models/test_class.rb +3 -0
  60. data/spec/orm/relations/interface_spec.rb +207 -0
  61. data/spec/orm/relations/metadata_spec.rb +202 -0
  62. data/spec/orm/relations/proxy/references_many_spec.rb +624 -0
  63. data/spec/orm/relations/proxy/references_one_polymorphic_spec.rb +106 -0
  64. data/spec/orm/relations/proxy/references_one_spec.rb +111 -0
  65. data/spec/orm/relations/proxy_spec.rb +13 -0
  66. data/spec/orm/schema/field_spec.rb +101 -2
  67. data/spec/shared/orm/coders/an_orm_coder.rb +14 -0
  68. data/spec/shared/orm/relations/proxy.rb +154 -0
  69. data/spec/shared/orm/relations/singular_proxy.rb +68 -0
  70. data/spec/spec_helper.rb +1 -0
  71. data/spec/thrift/cases/encoding_spec.rb +28 -7
  72. data/spec/wrapper/cases/adapter_spec.rb +9 -0
  73. data/spec/wrapper/cases/connection_spec.rb +13 -10
  74. data/spec/wrapper/cases/table_spec.rb +85 -85
  75. metadata +74 -22
  76. data/TODO.md +0 -8
  77. data/lib/massive_record/exceptions.rb +0 -11
  78. data/lib/massive_record/wrapper/column_family.rb +0 -22
  79. data/lib/massive_record/wrapper/connection.rb +0 -71
  80. data/lib/massive_record/wrapper/row.rb +0 -173
  81. data/lib/massive_record/wrapper/scanner.rb +0 -61
  82. data/lib/massive_record/wrapper/table.rb +0 -149
  83. data/spec/orm/cases/hbase/connection_spec.rb +0 -13
@@ -0,0 +1,150 @@
1
+ module MassiveRecord
2
+ module ORM
3
+ module Relations
4
+
5
+ #
6
+ # The master of metadata related to a relation. For instance;
7
+ # references_one :employee, :foreign_key => "person_id", :class_name => "Person"
8
+ #
9
+ class Metadata
10
+ attr_writer :foreign_key, :store_in, :class_name, :name, :relation_type, :polymorphic
11
+ attr_accessor :find_with
12
+ attr_reader :records_starts_from
13
+
14
+ def initialize(name, options = {})
15
+ options.to_options!
16
+ self.name = name
17
+ self.foreign_key = options[:foreign_key]
18
+ if options.has_key? :store_in
19
+ self.store_in = options[:store_in]
20
+ elsif options.has_key? :store_foreign_key_in
21
+ self.store_foreign_key_in = options[:store_foreign_key_in]
22
+ end
23
+ self.class_name = options[:class_name]
24
+ self.find_with = options[:find_with]
25
+ self.records_starts_from = options[:records_starts_from] if options[:records_starts_from]
26
+ self.polymorphic = options[:polymorphic]
27
+ end
28
+
29
+
30
+ def name
31
+ @name.to_s if @name
32
+ end
33
+
34
+ def relation_type
35
+ if @relation_type
36
+ relation_type = @relation_type.to_s
37
+ relation_type += "_polymorphic" if polymorphic?
38
+ relation_type
39
+ end
40
+ end
41
+
42
+ def foreign_key
43
+ (@foreign_key || calculate_foreign_key).to_s
44
+ end
45
+
46
+ def foreign_key_setter
47
+ foreign_key+'='
48
+ end
49
+
50
+ def polymorphic_type_column
51
+ type_column = foreign_key.gsub(/_id$/, '')
52
+ type_column + "_type"
53
+ end
54
+
55
+ def polymorphic_type_column_setter
56
+ polymorphic_type_column+'='
57
+ end
58
+
59
+ def class_name
60
+ (@class_name || calculate_class_name).to_s
61
+ end
62
+
63
+ def proxy_target_class
64
+ class_name.constantize
65
+ end
66
+
67
+ def store_in
68
+ @store_in.to_s if @store_in
69
+ end
70
+
71
+ def store_foreign_key_in
72
+ ActiveSupport::Deprecation.warn("store_foreign_key_in is deprecated. Use store_in instead!")
73
+ store_in
74
+ end
75
+
76
+ def store_foreign_key_in=(column_family)
77
+ ActiveSupport::Deprecation.warn("store_foreign_key_in is deprecated. Use store_in instead!")
78
+ self.store_in = column_family
79
+ end
80
+
81
+ def persisting_foreign_key?
82
+ !!store_in && !records_starts_from
83
+ end
84
+
85
+
86
+ def polymorphic
87
+ !!@polymorphic
88
+ end
89
+
90
+ def polymorphic?
91
+ polymorphic
92
+ end
93
+
94
+
95
+ def new_relation_proxy(proxy_owner)
96
+ proxy_class_name.constantize.new(:proxy_owner => proxy_owner, :metadata => self)
97
+ end
98
+
99
+
100
+ def ==(other)
101
+ other.instance_of?(self.class) && other.hash == hash
102
+ end
103
+ alias_method :eql?, :==
104
+
105
+ def hash
106
+ name.hash
107
+ end
108
+
109
+
110
+
111
+ def represents_a_collection?
112
+ relation_type == 'references_many'
113
+ end
114
+
115
+
116
+ def records_starts_from=(method)
117
+ @records_starts_from = method
118
+
119
+ if @records_starts_from
120
+ self.find_with = Proc.new do |proxy_owner, options = {}|
121
+ start = proxy_owner.send(records_starts_from) and proxy_target_class.all(options.merge({:start => start}))
122
+ end
123
+ else
124
+ self.find_with = nil
125
+ end
126
+ end
127
+
128
+
129
+ private
130
+
131
+
132
+ def calculate_class_name
133
+ name.to_s.classify
134
+ end
135
+
136
+ def calculate_foreign_key
137
+ if represents_a_collection?
138
+ name.downcase.singularize + "_ids"
139
+ else
140
+ name.downcase + "_id"
141
+ end
142
+ end
143
+
144
+ def proxy_class_name
145
+ "MassiveRecord::ORM::Relations::Proxy::"+relation_type.classify
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,229 @@
1
+ module MassiveRecord
2
+ module ORM
3
+ module Relations
4
+ class Proxy
5
+ class ReferencesMany < Proxy
6
+ #
7
+ # Loading proxy_targets will merge it with records found currently in proxy,
8
+ # to make sure we don't remove any pushed proxy_targets only cause we load the
9
+ # proxy_targets.
10
+ #
11
+ # TODO - Implement methods like:
12
+ # * find_in_batches
13
+ # * find_each
14
+ # * etc :-)
15
+ #
16
+ # - A counter cache is also nice.
17
+ #
18
+ def load_proxy_target
19
+ proxy_target_before_load = proxy_target
20
+ proxy_target_after_load = super
21
+
22
+ self.proxy_target = (proxy_target_before_load + proxy_target_after_load).uniq
23
+ end
24
+
25
+ def reset
26
+ super
27
+ @proxy_target = []
28
+ end
29
+
30
+ def replace(*records)
31
+ records.flatten!
32
+
33
+ if records.length == 1 and records.first.nil?
34
+ reset
35
+ else
36
+ delete_all
37
+ concat(records)
38
+ end
39
+ end
40
+
41
+
42
+ #
43
+ # Adding record(s) to the collection.
44
+ #
45
+ def <<(*records)
46
+ save_records = proxy_owner.persisted?
47
+
48
+ if records.flatten.all? &:valid?
49
+ records.flatten.each do |record|
50
+ unless include? record
51
+ raise_if_type_mismatch(record)
52
+ add_foreign_key_in_proxy_owner(record.id)
53
+ proxy_target << record
54
+ record.save if save_records
55
+ end
56
+ end
57
+
58
+ proxy_owner.save if save_records
59
+
60
+ self
61
+ end
62
+ end
63
+ alias_method :push, :<<
64
+ alias_method :concat, :<<
65
+
66
+ #
67
+ # Destroy record(s) from the collection
68
+ # Each record will be asked to destroy itself as well
69
+ #
70
+ def destroy(*records)
71
+ delete_or_destroy *records, :destroy
72
+ end
73
+
74
+ #
75
+ # Deletes record(s) from the collection
76
+ #
77
+ def delete(*records)
78
+ delete_or_destroy *records, :delete
79
+ end
80
+
81
+ #
82
+ # Destroys all records
83
+ #
84
+ def destroy_all
85
+ destroy(load_proxy_target)
86
+ reset
87
+ loaded!
88
+ end
89
+
90
+ #
91
+ # Deletes all records from the relationship.
92
+ # Does not destroy the records
93
+ #
94
+ def delete_all
95
+ delete(load_proxy_target)
96
+ reset
97
+ loaded!
98
+ end
99
+
100
+ #
101
+ # Checks if record is included in collection
102
+ #
103
+ # TODO This needs a bit of work, depending on if proxy's proxy_target
104
+ # has been loaded or not. For now, we are just checking
105
+ # what we currently have in @proxy_target
106
+ #
107
+ def include?(record)
108
+ load_proxy_target.include? record
109
+ end
110
+
111
+ #
112
+ # Returns the length of targes
113
+ #
114
+ # TODO This can be smarter as well. For instance; if we have not
115
+ # loaded targets, and we have foreign keys in the owner we
116
+ # can simply do a owner's foreign keys and ask for it's length.
117
+ #
118
+ def length
119
+ load_proxy_target.length
120
+ end
121
+ alias_method :count, :length
122
+ alias_method :size, :length
123
+
124
+ def empty?
125
+ length == 0
126
+ end
127
+
128
+ def first
129
+ limit(1).first
130
+ end
131
+
132
+ def find(id)
133
+ if loaded?
134
+ record = proxy_target.find { |record| record.id == id }
135
+ elsif find_with_proc?
136
+ if id.starts_with? proxy_owner.send(metadata.records_starts_from)
137
+ record = proxy_target_class.find(id)
138
+ end
139
+ elsif foreign_key_in_proxy_owner_exists?(id)
140
+ record = proxy_target_class.find(id)
141
+ end
142
+
143
+ raise RecordNotFound.new("Could not find #{proxy_target_class.model_name} with id=#{id}") if record.nil?
144
+
145
+ record
146
+ end
147
+
148
+ #
149
+ # Returns a limited result set of target records.
150
+ #
151
+ # TODO If we know all our foreign keys (basically we also know our length)
152
+ # we can then mark our self as loaded if limit is equal to or greater
153
+ # than foreign keys length.
154
+ #
155
+ def limit(limit)
156
+ if loaded?
157
+ proxy_target.slice(0, limit)
158
+ elsif find_with_proc?
159
+ find_proxy_target_with_proc(:limit => limit)
160
+ else
161
+ ids = proxy_owner.send(metadata.foreign_key).slice(0, limit)
162
+ ids = ids.first if ids.length == 1
163
+ [find_proxy_target(ids)].flatten
164
+ end
165
+ end
166
+
167
+
168
+
169
+ private
170
+
171
+
172
+ def delete_or_destroy(*records, method)
173
+ records.flatten.each do |record|
174
+ if include? record
175
+ remove_foreign_key_in_proxy_owner(record.id)
176
+ proxy_target.delete(record)
177
+ record.destroy if method.to_sym == :destroy
178
+ end
179
+ end
180
+
181
+ proxy_owner.save if proxy_owner.persisted?
182
+ end
183
+
184
+
185
+
186
+ def find_proxy_target(ids = nil)
187
+ ids = proxy_owner.send(metadata.foreign_key) if ids.nil?
188
+ proxy_target_class.find(ids, :skip_expected_result_check => true)
189
+ end
190
+
191
+ def find_proxy_target_with_proc(options = {})
192
+ [super].compact.flatten
193
+ end
194
+
195
+ def can_find_proxy_target?
196
+ super || (proxy_owner.respond_to?(metadata.foreign_key) && proxy_owner.send(metadata.foreign_key).any?)
197
+ end
198
+
199
+
200
+
201
+
202
+
203
+ def add_foreign_key_in_proxy_owner(id)
204
+ if update_foreign_key_fields_in_proxy_owner? && proxy_owner.respond_to?(metadata.foreign_key)
205
+ proxy_owner.send(metadata.foreign_key) << id
206
+ notify_of_change_in_proxy_owner_foreign_key
207
+ end
208
+ end
209
+
210
+ def remove_foreign_key_in_proxy_owner(id)
211
+ if proxy_owner.respond_to? metadata.foreign_key
212
+ proxy_owner.send(metadata.foreign_key).delete(id)
213
+ notify_of_change_in_proxy_owner_foreign_key
214
+ end
215
+ end
216
+
217
+ def foreign_key_in_proxy_owner_exists?(id)
218
+ proxy_owner.send(metadata.foreign_key).include? id
219
+ end
220
+
221
+ def notify_of_change_in_proxy_owner_foreign_key
222
+ method = metadata.foreign_key+"_will_change!"
223
+ proxy_owner.send(method) if proxy_owner.respond_to? method
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,40 @@
1
+ module MassiveRecord
2
+ module ORM
3
+ module Relations
4
+ class Proxy
5
+ #
6
+ # Proxy used to reference one other object in another table.
7
+ #
8
+ class ReferencesOne < Proxy
9
+
10
+ def proxy_target=(proxy_target)
11
+ set_foreign_key_in_proxy_owner(proxy_target.id) if proxy_target
12
+ super(proxy_target)
13
+ end
14
+
15
+ def replace(proxy_target)
16
+ super
17
+ set_foreign_key_in_proxy_owner(nil) if proxy_target.nil?
18
+ end
19
+
20
+
21
+ private
22
+
23
+ def find_proxy_target
24
+ proxy_target_class.find(proxy_owner.send(metadata.foreign_key))
25
+ end
26
+
27
+ def can_find_proxy_target?
28
+ super || proxy_owner.send(metadata.foreign_key).present?
29
+ end
30
+
31
+ def set_foreign_key_in_proxy_owner(id)
32
+ if update_foreign_key_fields_in_proxy_owner? && proxy_owner.respond_to?(metadata.foreign_key_setter)
33
+ proxy_owner.send(metadata.foreign_key_setter, id)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,49 @@
1
+ module MassiveRecord
2
+ module ORM
3
+ module Relations
4
+ class Proxy
5
+ class ReferencesOnePolymorphic < Proxy
6
+ def proxy_target=(proxy_target)
7
+ set_foreign_key_and_type_in_proxy_owner(proxy_target.id, proxy_target.class.to_s.underscore) if proxy_target
8
+ super(proxy_target)
9
+ end
10
+
11
+ def proxy_target_class
12
+ proxy_owner.send(metadata.polymorphic_type_column).classify.constantize
13
+ end
14
+
15
+ def replace(proxy_target)
16
+ super
17
+ set_foreign_key_and_type_in_proxy_owner(nil, nil) if proxy_target.nil?
18
+ end
19
+
20
+
21
+ private
22
+
23
+ def find_proxy_target
24
+ proxy_target_class.find(proxy_owner.send(metadata.foreign_key))
25
+ end
26
+
27
+ def can_find_proxy_target?
28
+ super || (proxy_owner.send(metadata.foreign_key).present? && proxy_owner.send(metadata.polymorphic_type_column).present?)
29
+ end
30
+
31
+ def set_foreign_key_and_type_in_proxy_owner(id, type)
32
+ if update_foreign_key_fields_in_proxy_owner?
33
+ proxy_owner.send(metadata.foreign_key_setter, id) if proxy_owner.respond_to?(metadata.foreign_key_setter)
34
+ proxy_owner.send(metadata.polymorphic_type_column_setter, type) if proxy_owner.respond_to?(metadata.polymorphic_type_column_setter)
35
+ end
36
+ end
37
+
38
+
39
+
40
+ private
41
+
42
+ def raise_if_type_mismatch(record)
43
+ # By nature this can't be checked, as it should acept all types.
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end