massive_record 0.1.1 → 0.2.0.beta

Sign up to get free protection for your applications and to get access to all the features.
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