datastax_rails 1.0.5

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 (148) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +62 -0
  3. data/Rakefile +34 -0
  4. data/config/schema.xml +266 -0
  5. data/config/schema.xml.erb +70 -0
  6. data/config/solrconfig.xml +1564 -0
  7. data/config/stopwords.txt +58 -0
  8. data/lib/datastax_rails/associations/association.rb +224 -0
  9. data/lib/datastax_rails/associations/association_scope.rb +25 -0
  10. data/lib/datastax_rails/associations/belongs_to_association.rb +64 -0
  11. data/lib/datastax_rails/associations/builder/association.rb +56 -0
  12. data/lib/datastax_rails/associations/builder/belongs_to.rb +30 -0
  13. data/lib/datastax_rails/associations/builder/collection_association.rb +48 -0
  14. data/lib/datastax_rails/associations/builder/has_and_belongs_to_many.rb +36 -0
  15. data/lib/datastax_rails/associations/builder/has_many.rb +54 -0
  16. data/lib/datastax_rails/associations/builder/has_one.rb +52 -0
  17. data/lib/datastax_rails/associations/builder/singular_association.rb +56 -0
  18. data/lib/datastax_rails/associations/collection_association.rb +274 -0
  19. data/lib/datastax_rails/associations/collection_proxy.rb +118 -0
  20. data/lib/datastax_rails/associations/has_and_belongs_to_many_association.rb +44 -0
  21. data/lib/datastax_rails/associations/has_many_association.rb +58 -0
  22. data/lib/datastax_rails/associations/has_one_association.rb +68 -0
  23. data/lib/datastax_rails/associations/singular_association.rb +58 -0
  24. data/lib/datastax_rails/associations.rb +86 -0
  25. data/lib/datastax_rails/attribute_methods/definition.rb +20 -0
  26. data/lib/datastax_rails/attribute_methods/dirty.rb +43 -0
  27. data/lib/datastax_rails/attribute_methods/typecasting.rb +50 -0
  28. data/lib/datastax_rails/attribute_methods.rb +104 -0
  29. data/lib/datastax_rails/base.rb +587 -0
  30. data/lib/datastax_rails/batches.rb +35 -0
  31. data/lib/datastax_rails/callbacks.rb +37 -0
  32. data/lib/datastax_rails/collection.rb +9 -0
  33. data/lib/datastax_rails/connection.rb +21 -0
  34. data/lib/datastax_rails/consistency.rb +33 -0
  35. data/lib/datastax_rails/cql/base.rb +15 -0
  36. data/lib/datastax_rails/cql/column_family.rb +38 -0
  37. data/lib/datastax_rails/cql/consistency.rb +13 -0
  38. data/lib/datastax_rails/cql/create_column_family.rb +63 -0
  39. data/lib/datastax_rails/cql/create_keyspace.rb +30 -0
  40. data/lib/datastax_rails/cql/delete.rb +41 -0
  41. data/lib/datastax_rails/cql/drop_column_family.rb +13 -0
  42. data/lib/datastax_rails/cql/drop_keyspace.rb +13 -0
  43. data/lib/datastax_rails/cql/insert.rb +53 -0
  44. data/lib/datastax_rails/cql/select.rb +51 -0
  45. data/lib/datastax_rails/cql/truncate.rb +13 -0
  46. data/lib/datastax_rails/cql/update.rb +68 -0
  47. data/lib/datastax_rails/cql/use_keyspace.rb +13 -0
  48. data/lib/datastax_rails/cql.rb +25 -0
  49. data/lib/datastax_rails/cursor.rb +90 -0
  50. data/lib/datastax_rails/errors.rb +16 -0
  51. data/lib/datastax_rails/identity/abstract_key_factory.rb +26 -0
  52. data/lib/datastax_rails/identity/custom_key_factory.rb +36 -0
  53. data/lib/datastax_rails/identity/hashed_natural_key_factory.rb +10 -0
  54. data/lib/datastax_rails/identity/natural_key_factory.rb +37 -0
  55. data/lib/datastax_rails/identity/uuid_key_factory.rb +23 -0
  56. data/lib/datastax_rails/identity.rb +53 -0
  57. data/lib/datastax_rails/log_subscriber.rb +37 -0
  58. data/lib/datastax_rails/migrations/migration.rb +15 -0
  59. data/lib/datastax_rails/migrations.rb +36 -0
  60. data/lib/datastax_rails/mocking.rb +15 -0
  61. data/lib/datastax_rails/persistence.rb +133 -0
  62. data/lib/datastax_rails/railtie.rb +20 -0
  63. data/lib/datastax_rails/reflection.rb +472 -0
  64. data/lib/datastax_rails/relation/finder_methods.rb +184 -0
  65. data/lib/datastax_rails/relation/modification_methods.rb +80 -0
  66. data/lib/datastax_rails/relation/search_methods.rb +349 -0
  67. data/lib/datastax_rails/relation/spawn_methods.rb +107 -0
  68. data/lib/datastax_rails/relation.rb +393 -0
  69. data/lib/datastax_rails/schema/migration.rb +106 -0
  70. data/lib/datastax_rails/schema/migration_proxy.rb +25 -0
  71. data/lib/datastax_rails/schema/migrator.rb +212 -0
  72. data/lib/datastax_rails/schema.rb +37 -0
  73. data/lib/datastax_rails/scoping.rb +394 -0
  74. data/lib/datastax_rails/serialization.rb +6 -0
  75. data/lib/datastax_rails/tasks/column_family.rb +162 -0
  76. data/lib/datastax_rails/tasks/ds.rake +63 -0
  77. data/lib/datastax_rails/tasks/keyspace.rb +57 -0
  78. data/lib/datastax_rails/timestamps.rb +19 -0
  79. data/lib/datastax_rails/type.rb +16 -0
  80. data/lib/datastax_rails/types/array_type.rb +77 -0
  81. data/lib/datastax_rails/types/base_type.rb +26 -0
  82. data/lib/datastax_rails/types/binary_type.rb +15 -0
  83. data/lib/datastax_rails/types/boolean_type.rb +22 -0
  84. data/lib/datastax_rails/types/date_type.rb +17 -0
  85. data/lib/datastax_rails/types/float_type.rb +18 -0
  86. data/lib/datastax_rails/types/integer_type.rb +18 -0
  87. data/lib/datastax_rails/types/string_type.rb +16 -0
  88. data/lib/datastax_rails/types/text_type.rb +16 -0
  89. data/lib/datastax_rails/types/time_type.rb +17 -0
  90. data/lib/datastax_rails/types.rb +9 -0
  91. data/lib/datastax_rails/validations/uniqueness.rb +119 -0
  92. data/lib/datastax_rails/validations.rb +48 -0
  93. data/lib/datastax_rails/version.rb +3 -0
  94. data/lib/datastax_rails.rb +87 -0
  95. data/lib/solr_no_escape.rb +28 -0
  96. data/spec/datastax_rails/associations/belongs_to_association_spec.rb +7 -0
  97. data/spec/datastax_rails/associations/has_many_association_spec.rb +37 -0
  98. data/spec/datastax_rails/associations_spec.rb +22 -0
  99. data/spec/datastax_rails/attribute_methods_spec.rb +23 -0
  100. data/spec/datastax_rails/base_spec.rb +15 -0
  101. data/spec/datastax_rails/cql/select_spec.rb +12 -0
  102. data/spec/datastax_rails/cql/update_spec.rb +0 -0
  103. data/spec/datastax_rails/relation/finder_methods_spec.rb +54 -0
  104. data/spec/datastax_rails/relation/modification_methods_spec.rb +41 -0
  105. data/spec/datastax_rails/relation/search_methods_spec.rb +117 -0
  106. data/spec/datastax_rails/relation/spawn_methods_spec.rb +28 -0
  107. data/spec/datastax_rails/relation_spec.rb +130 -0
  108. data/spec/datastax_rails/validations/uniqueness_spec.rb +41 -0
  109. data/spec/datastax_rails_spec.rb +5 -0
  110. data/spec/dummy/Rakefile +8 -0
  111. data/spec/dummy/app/assets/javascripts/application.js +9 -0
  112. data/spec/dummy/app/assets/stylesheets/application.css +7 -0
  113. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  114. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  115. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  116. data/spec/dummy/config/application.rb +47 -0
  117. data/spec/dummy/config/boot.rb +10 -0
  118. data/spec/dummy/config/database.yml +25 -0
  119. data/spec/dummy/config/datastax.yml +18 -0
  120. data/spec/dummy/config/environment.rb +5 -0
  121. data/spec/dummy/config/environments/development.rb +30 -0
  122. data/spec/dummy/config/environments/production.rb +60 -0
  123. data/spec/dummy/config/environments/test.rb +39 -0
  124. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  125. data/spec/dummy/config/initializers/inflections.rb +10 -0
  126. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  127. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  128. data/spec/dummy/config/initializers/session_store.rb +8 -0
  129. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  130. data/spec/dummy/config/locales/en.yml +5 -0
  131. data/spec/dummy/config/routes.rb +58 -0
  132. data/spec/dummy/config/sunspot.yml +17 -0
  133. data/spec/dummy/config.ru +4 -0
  134. data/spec/dummy/ks/migrate/20111117224534_models.rb +20 -0
  135. data/spec/dummy/ks/schema.json +180 -0
  136. data/spec/dummy/log/development.log +298 -0
  137. data/spec/dummy/log/production.log +0 -0
  138. data/spec/dummy/log/test.log +20307 -0
  139. data/spec/dummy/public/404.html +26 -0
  140. data/spec/dummy/public/422.html +26 -0
  141. data/spec/dummy/public/500.html +26 -0
  142. data/spec/dummy/public/favicon.ico +0 -0
  143. data/spec/dummy/script/rails +6 -0
  144. data/spec/spec.opts +5 -0
  145. data/spec/spec_helper.rb +29 -0
  146. data/spec/support/datastax_test_hook.rb +14 -0
  147. data/spec/support/models.rb +72 -0
  148. metadata +353 -0
@@ -0,0 +1,80 @@
1
+ module DatastaxRails
2
+ module ModificationMethods
3
+ # Destroys the records matching +conditions+ by instantiating each
4
+ # record and calling its +destroy+ method. Each object's callbacks are
5
+ # executed (including <tt>:dependent</tt> association options and
6
+ # +before_destroy+/+after_destroy+ Observer methods). Returns the
7
+ # collection of objects that were destroyed; each will be frozen, to
8
+ # reflect that no changes should be made (since they can't be
9
+ # persisted).
10
+ #
11
+ # Note: Instantiation, callback execution, and deletion of each
12
+ # record can be time consuming when you're removing many records at
13
+ # once. However, it is necessary to perform it this way to ensure
14
+ # that the SOLR index stays in sync with the Cassandra data store.
15
+ #
16
+ # +delete_all+ is aliased to this because you can't delete without running
17
+ # the requisite callbacks. (at least not yet)
18
+ #
19
+ # ==== Parameters
20
+ #
21
+ # * +conditions+ - A string, array, or hash that specifies which records
22
+ # to destroy. If omitted, all records matching the current relation are
23
+ # destroyed. See the Conditions section in the introduction to
24
+ # DatastaxRails::Base for more information.
25
+ #
26
+ # ==== Examples
27
+ #
28
+ # Person.destroy_all(:status => "inactive")
29
+ # Person.where(:age => 0..18).destroy_all
30
+ # Person.where_not(:status => "active").destroy_all
31
+ def destroy_all(conditions = nil)
32
+ if conditions
33
+ ret = where(conditions).destroy_all
34
+ else
35
+ ret = to_a.each {|object| object.destroy }
36
+ end
37
+ reset
38
+ ret
39
+ end
40
+ # TODO: Find a way to delete from both without instantiating
41
+ alias :delete_all :destroy_all
42
+
43
+ # Destroy an object (or multiple objects) that has the given id, the object is instantiated first,
44
+ # therefore all callbacks and filters are fired off before the object is deleted. This method is
45
+ # in-efficient since it actually has to instantiate the object just to delte it but allows cleanup
46
+ # methods such as the ones that remove the object from SOLR to be run.
47
+ #
48
+ # This essentially finds the object (or multiple objects) with the given id, creates a new object
49
+ # from the attributes, and then calls destroy on it.
50
+ #
51
+ # Note: this will only find objects that are matched by the current relation even if the ID would
52
+ # otherwise be valid.
53
+ #
54
+ # +delete+ is aliased to this because you can't delete without running
55
+ # the requisite callbacks. (at least not yet)
56
+ #
57
+ # ==== Parameters
58
+ #
59
+ # * +id+ - Can be either an Integer or an Array of Integers.
60
+ #
61
+ # ==== Examples
62
+ #
63
+ # # Destroy a single object
64
+ # Todo.destroy(1)
65
+ #
66
+ # # Destroy multiple objects
67
+ # todos = [1,2,3]
68
+ # Todo.destroy(todos)
69
+ def destroy(id)
70
+ if id.is_a?(Array)
71
+ ret = id.map { |one_id| destroy(one_id) }
72
+ else
73
+ ret = find(id).destroy
74
+ end
75
+ reset
76
+ ret
77
+ end
78
+ alias :delete :destroy
79
+ end
80
+ end
@@ -0,0 +1,349 @@
1
+ module DatastaxRails
2
+ module SearchMethods
3
+
4
+ # Normally special characters (other than wild cards) are escaped before the search
5
+ # is submitted. If you want to handle escaping yourself because you need to use
6
+ # those special characters, then just include this in your chain.
7
+ #
8
+ # Model.dont_escape.where("(some stuff I don\'t want escaped)")
9
+ #
10
+ # Note that fulltext searches are NEVER escaped. Use Relation.solr_escape if you
11
+ # want that done.
12
+ def dont_escape
13
+ clone.tap do |r|
14
+ r.escape_value = false
15
+ end
16
+ end
17
+
18
+ # Used to extend a scope with additional methods, either through
19
+ # a module or a block provided
20
+ #
21
+ # The object returned is a relation which can be further extended
22
+ def extending(*modules)
23
+ modules << Module.new(&Proc.new) if block_given?
24
+
25
+ return self if modules.empty?
26
+
27
+ clone.tap do |r|
28
+ r.send(:apply_modules, modules.flatten)
29
+ end
30
+ end
31
+
32
+ # Limit a single page to +value+ records
33
+ #
34
+ # Model.limit(1)
35
+ # Model.per_page(50)
36
+ #
37
+ # Normally DatastaxRails searches are paginated at a really high number
38
+ # so as to effectively disable pagination. However, you can cause
39
+ # all requests to be paginated on a per-model basis by overriding the
40
+ # +default_page_size+ class method in your model:
41
+ #
42
+ # class Model < DatastaxRails::Base
43
+ # def self.default_page_size
44
+ # 30
45
+ # end
46
+ # end
47
+ def limit(value)
48
+ clone.tap do |r|
49
+ r.per_page_value = value
50
+ end
51
+ end
52
+ alias :per_page :limit
53
+
54
+ # Sets the page number to retrieve
55
+ #
56
+ # Model.page(2)
57
+ def page(value)
58
+ clone.tap do |r|
59
+ r.page_value = value
60
+ end
61
+ end
62
+
63
+ # WillPaginate compatible method for paginating
64
+ #
65
+ # Model.paginate(:page => 2, :per_page => 10)
66
+ def paginate(options = {})
67
+ options = options.reverse_merge({:page => 1, :per_page => 30})
68
+ clone.tap do |r|
69
+ r.page_value = options[:page]
70
+ r.per_page_value = options[:per_page]
71
+ end
72
+ end
73
+
74
+ # Group results by one or more attributes only returning the top result
75
+ # for each group.
76
+ #
77
+ # Model.group(:program_id)
78
+ def group(*attrs)
79
+ return self if attrs.blank?
80
+
81
+ clone.tap do |r|
82
+ r.group_values += args.flatten
83
+ end
84
+ end
85
+
86
+ # Orders the result set by a particular attribute. Note that text fields
87
+ # may not be used for ordering as they are tokenized. Valid candidates
88
+ # are fields of type +string+, +integer+, +long+, +float+, +double+, and
89
+ # +time+. In addition, the symbol +:score+ can be used to sort on the
90
+ # relevance rating returned by Solr. The default direction is ascending
91
+ # but may be reversed by passing a hash where the field is the key and
92
+ # the value is :desc
93
+ #
94
+ # Model.order(:name)
95
+ # Model.order(:name => :desc)
96
+ def order(attribute)
97
+ return self if attribute.blank?
98
+
99
+ clone.tap do |r|
100
+ order_by = attribute.is_a?(Hash) ? attribute.dup : {attribute => :asc}
101
+
102
+ r.order_values << order_by
103
+ end
104
+ end
105
+
106
+ # Works in two unique ways.
107
+ #
108
+ # _First_: takes a block so it can be used just like Array#select.
109
+ #
110
+ # Model.scoped.select { |m| m.field == value }
111
+ #
112
+ # This will build an array of objects from the database for the scope,
113
+ # converting them into an array and iterating through them using Array#select.
114
+ #
115
+ # _Second_: Modifies the query so that only certain fields are retrieved:
116
+ #
117
+ # >> Model.select(:field)
118
+ # => [#<Model field:value>]
119
+ #
120
+ # Although in the above example it looks as though this method returns an
121
+ # array, it actually returns a relation object and can have other query
122
+ # methods appended to it, such as the other methods in DatastaxRails::SearchMethods.
123
+ #
124
+ # This method will also take multiple parameters:
125
+ #
126
+ # >> Model.select(:field, :other_field, :and_one_more)
127
+ # => [#<Model field: "value", other_field: "value", and_one_more: "value">]
128
+ #
129
+ # Any attributes that do not have fields retrieved by a select
130
+ # will return `nil` when the getter method for that attribute is used:
131
+ #
132
+ # >> Model.select(:field).first.other_field
133
+ # => nil
134
+ #
135
+ # The exception to this rule is when an attribute is lazy-loaded (e.g., binary).
136
+ # In that case, it is never retrieved until you call the getter method.
137
+ def select(value = Proc.new)
138
+ if block_given?
139
+ to_a.select {|*block_args| value.call(*block_args) }
140
+ else
141
+ clone.tap do |r|
142
+ r.select_values += Array.wrap(value)
143
+ end
144
+ end
145
+ end
146
+
147
+ # Reverses the order of the results
148
+ #
149
+ # Model.order(:name).reverse_order
150
+ # is equivalent to
151
+ # Model.order(:name => :desc)
152
+ #
153
+ # Model.order(:name).reverse_order.reverse_order
154
+ # is equivalent to
155
+ # Model.order(:name => :asc)
156
+ def reverse_order
157
+ clone.tap do |r|
158
+ r.reverse_order_value == !r.reverse_order_value
159
+ end
160
+ end
161
+
162
+ # By default, DatastaxRails uses the LuceneQueryParser. Note that this
163
+ # is a change from the underlying Sunspot gem. Sunspot defaults to the
164
+ # +disMax+ query parser. If you want to use that, then pass that in here.
165
+ #
166
+ # *This only applies to fulltext queries*
167
+ #
168
+ # Model.query_parser('disMax').fulltext("john smith")
169
+ def query_parser(attribute)
170
+ return self if attribute.blank?
171
+
172
+ clone.tap do |r|
173
+ r.query_parser_value = attribute
174
+ end
175
+ end
176
+
177
+ # By default, DatastaxRails will try to pick the right method of performing
178
+ # a search. You can use this method to force it to make the query via SOLR.
179
+ #
180
+ # NOTE that the time between when a record is placed into Cassandra and when
181
+ # it becomes available in SOLR is not guaranteed to be insignificant. It's
182
+ # very possible to insert a new record and not find it when immediately doing
183
+ # a SOLR search for it.
184
+ def with_solr
185
+ clone.tap do |r|
186
+ r.use_solr_value = true
187
+ end
188
+ end
189
+
190
+ # By default, DatastaxRails will try to pick the right method of performing
191
+ # a search. You can use this method to force it to make the query via
192
+ # cassandra.
193
+ #
194
+ # NOTE that this method assumes that you have all the proper secondary indexes
195
+ # in place before you attempt to use it. If not, you will get an error.
196
+ def with_cassandra
197
+ clone.tap do |r|
198
+ r.use_solr_value = false
199
+ end
200
+ end
201
+
202
+ # Specifies restrictions (scoping) on the result set. Expects a hash
203
+ # in the form +attribute => value+ for equality comparisons.
204
+ #
205
+ # Model.where(:group_id => '1234', :active => 'Y')
206
+ #
207
+ # The value of the comparison does not need to be a scalar. For example:
208
+ #
209
+ # Model.where(:name => ["Bob", "Tom", "Sally"])
210
+ # Model.where(:age => 18..65)
211
+ #
212
+ # Inequality comparisons such as greater_than and less_than are
213
+ # specified via chaining:
214
+ #
215
+ # Model.where(:created_at).greater_than(1.day.ago)
216
+ # Model.where(:age).less_than(65)
217
+ #
218
+ # NOTE: Due to the way SOLR handles range queries, all greater/less than
219
+ # queries are actually greater/less than or equal to queries.
220
+ # There is no way to perform a strictly greater/less than query.
221
+ def where(attribute)
222
+ return self if attribute.blank?
223
+
224
+ if attribute.is_a?(Symbol)
225
+ WhereProxy.new(self, attribute)
226
+ else
227
+ attributes = attribute.dup
228
+ attributes.each do |k,v|
229
+ attributes[k] = solr_format(v)
230
+ end
231
+ clone.tap do |r|
232
+ r.where_values << attributes
233
+ end
234
+ end
235
+ end
236
+
237
+ # Specifies restrictions (scoping) that should not match the result set.
238
+ # Expects a hash in the form +attribute => value+.
239
+ #
240
+ # Model.where_not(:group_id => '1234', :active => 'N')
241
+ #
242
+ # Passing an array will search for records where none of the array entries
243
+ # are present
244
+ #
245
+ # Model.where_not(:group_id => ['1234', '5678'])
246
+ #
247
+ # The above would find all models where group id is neither 1234 or 5678.
248
+ def where_not(attribute)
249
+ return self if attribute.blank?
250
+
251
+ attributes = []
252
+ attribute.each do |k,v|
253
+ if v.is_a?(Array)
254
+ v.each do |val|
255
+ attributes << {k => solr_format(val)}
256
+ end
257
+ else
258
+ attributes << {k => solr_format(v)}
259
+ end
260
+ end
261
+ clone.tap do |r|
262
+ r.where_not_values += attributes
263
+ end
264
+ end
265
+
266
+ # Specifies a full text search string to be processed by SOLR
267
+ #
268
+ # Model.fulltext("john smith")
269
+ #
270
+ # You can also pass in an options hash with the following options:
271
+ #
272
+ # * :fields => list of fields to search instead of the default of all fields
273
+ # * :highlight => List of fields to retrieve highlights for. Note that highlighted fields *must* be +:stored+
274
+ #
275
+ # Model.fulltext("john smith", :fields => [:title])
276
+ # Model.fulltext("john smith", :hightlight => [:body])
277
+ def fulltext(query, opts = {})
278
+ return self if query.blank?
279
+
280
+ opts[:query] = query
281
+
282
+ clone.tap do |r|
283
+ r.fulltext_values << opts
284
+ end
285
+ end
286
+
287
+ # See documentation for +where+
288
+ def less_than(value)
289
+ raise ArgumentError, "#less_than can only be called after an appropriate where call. e.g. where(:created_at).less_than(1.day.ago)"
290
+ end
291
+
292
+ # See documentation for +where+
293
+ def greater_than(value)
294
+ raise ArgumentError, "#greater_than can only be called after an appropriate where call. e.g. where(:created_at).greater_than(1.day.ago)"
295
+ end
296
+
297
+ def solr_format(value)
298
+ case
299
+ when value.is_a?(Date), value.is_a?(Time)
300
+ value.strftime('%Y-%m-%dT%H\:%M\:%SZ')
301
+ when value.is_a?(Array)
302
+ value.collect {|v| v.gsub(/ /,"\\ ") }.join(" OR ")
303
+ when value.is_a?(Fixnum)
304
+ value < 0 ? "\\#{value}" : value
305
+ when value.is_a?(String)
306
+ value.gsub(/ /,"\\ ")
307
+ else
308
+ value
309
+ end
310
+ end
311
+
312
+ protected
313
+ def find_by_attributes(match, attributes, *args) #:nodoc:
314
+ conditions = Hash[attributes.map {|a| [a, args[attributes.index(a)]]}]
315
+ result = where(conditions).send(match.finder)
316
+
317
+ if match.blank? && result.blank?
318
+ raise RecordNotFound, "Couldn't find #{klass.name} with #{conditions.to_a.collect {|p| p.join('=')}.join(', ')}"
319
+ else
320
+ yield(result) if block_given?
321
+ result
322
+ end
323
+ end
324
+
325
+ class WhereProxy #:nodoc:
326
+ def initialize(relation, attribute) #:nodoc:
327
+ @relation, @attribute = relation, attribute
328
+ end
329
+
330
+ def equal_to(value) #:nodoc:
331
+ @relation.clone.tap do |r|
332
+ r.where_values << {@attribute => r.solr_format(value)}
333
+ end
334
+ end
335
+
336
+ def greater_than(value) #:nodoc:
337
+ @relation.clone.tap do |r|
338
+ r.greater_than_values << {@attribute => r.solr_format(value)}
339
+ end
340
+ end
341
+
342
+ def less_than(value) #:nodoc:
343
+ @relation.clone.tap do |r|
344
+ r.less_than_values << {@attribute => r.solr_format(value)}
345
+ end
346
+ end
347
+ end
348
+ end
349
+ end
@@ -0,0 +1,107 @@
1
+ module DatastaxRails
2
+ module SpawnMethods
3
+ # def scoped #:nodoc:
4
+ # self
5
+ # end
6
+
7
+ def merge(r) #:nodoc:
8
+ return self unless r
9
+ return to_a & r if r.is_a?(Array)
10
+
11
+ merged_relation = clone
12
+
13
+ (Relation::MULTI_VALUE_METHODS - [:where, :where_not]).each do |method|
14
+ value = r.send(:"#{method}_values")
15
+ merged_relation.send(:"#{method}_values=", merged_relation.send(:"#{method}_values") + value) if value.present?
16
+ end
17
+
18
+ merged_wheres = {}
19
+ # This will merge all the where clauses into a single hash. If the same attribute is
20
+ # specified multiple times, the last one will win.
21
+ (@where_values + r.where_values).each { |w| merged_wheres.merge!(w)}
22
+
23
+ merged_relation.where_values = [merged_wheres] unless merged_wheres.empty?
24
+
25
+ merged_where_nots = {}
26
+ # This will merge all the where not clauses into a single hash. If the same attribute is
27
+ # specified multiple times, the last one will win.
28
+ (@where_not_values + r.where_not_values).each { |w| merged_where_nots.merge!(w)}
29
+
30
+ merged_relation.where_not_values = [merged_where_nots] unless merged_where_nots.empty?
31
+
32
+ (Relation::SINGLE_VALUE_METHODS).each do |method|
33
+ value = r.send(:"#{method}_value")
34
+ merged_relation.send(:"#{method}_value=", value) unless value.nil?
35
+ end
36
+
37
+ merged_relation
38
+ end
39
+
40
+ # Removes from the query the condition(s) specified in +skips+.
41
+ #
42
+ # Example:
43
+ #
44
+ # Post.where(:active => true).order('id').except(:order) # discards the order condition
45
+ # Post.where(:active => true).order('id').except(:where) # discards the where condition but keeps the order
46
+ def except(*skips)
47
+ result = self.class.new(@klass, table)
48
+ result.default_scoped = default_scoped
49
+
50
+ ((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) - skips).each do |method|
51
+ result.send(:"#{method}_values=", send(:"#{method}_values"))
52
+ end
53
+
54
+ (Relation::SINGLE_VALUE_METHODS - skips).each do |method|
55
+ result.send(:"#{method}_value=", send(:"#{method}_value"))
56
+ end
57
+
58
+ # Apply scope extension modules
59
+ result.send(:apply_modules, extensions)
60
+
61
+ result
62
+ end
63
+
64
+ # Removes any condition from the query other than the one(s) specified in +onlies+.
65
+ #
66
+ # Example:
67
+ #
68
+ # Post.order('id').only(:where) # discards the order condition
69
+ # Post.order('id').only(:where, :order) # uses the specified order
70
+ #
71
+ def only(*onlies)
72
+ result = self.class.new(@klass, table)
73
+ result.default_scoped = default_scoped
74
+
75
+ ((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) & onlies).each do |method|
76
+ result.send(:"#{method}_values=", send(:"#{method}_values"))
77
+ end
78
+
79
+ (Relation::SINGLE_VALUE_METHODS & onlies).each do |method|
80
+ result.send(:"#{method}_value=", send(:"#{method}_value"))
81
+ end
82
+
83
+ # Apply scope extension modules
84
+ result.send(:apply_modules, extensions)
85
+
86
+ result
87
+ end
88
+
89
+ VALID_FIND_OPTIONS = [:conditions, :limit, :offset, :order, :group, :page, :per_page]
90
+ def apply_finder_options(options) #:nodoc:
91
+ relation = clone
92
+ return relation unless options
93
+
94
+ options.assert_valid_keys(VALID_FIND_OPTIONS)
95
+ finders = options.dup
96
+ finders.delete_if { |key, value| value.nil? }
97
+
98
+ ([:group, :order, :limit, :offset, :page, :per_page] & finders.keys).each do |finder|
99
+ relation = relation.send(finder, finders[finder])
100
+ end
101
+
102
+ relation.where(finders[:conditions]) if options.has_key?(:conditions)
103
+
104
+ relation
105
+ end
106
+ end
107
+ end