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,393 @@
1
+ require 'rsolr'
2
+
3
+ module DatastaxRails
4
+ class Relation
5
+ MULTI_VALUE_METHODS = [:group, :order, :where, :where_not, :fulltext, :greater_than, :less_than, :select]
6
+ SINGLE_VALUE_METHODS = [:page, :per_page, :reverse_order, :query_parser, :consistency, :ttl, :use_solr, :escape]
7
+
8
+ SOLR_CHAR_RX = /([\+\!\(\)\[\]\^\"\~\:\'\=]+)/
9
+
10
+ Relation::MULTI_VALUE_METHODS.each do |m|
11
+ attr_accessor :"#{m}_values"
12
+ end
13
+ Relation::SINGLE_VALUE_METHODS.each do |m|
14
+ attr_accessor :"#{m}_value"
15
+ end
16
+ attr_accessor :create_with_value, :default_scoped
17
+
18
+ include SearchMethods
19
+ include ModificationMethods
20
+ include FinderMethods
21
+ include SpawnMethods
22
+
23
+ attr_reader :klass, :column_family, :loaded, :cql
24
+ alias :loaded? :loaded
25
+ alias :default_scoped? :default_scoped
26
+
27
+ def initialize(klass, column_family) #:nodoc:
28
+ @klass, @column_family = klass, column_family
29
+ @loaded = false
30
+ @results = []
31
+ @default_scoped = false
32
+ @cql = DatastaxRails::Cql.for_class(klass)
33
+
34
+ SINGLE_VALUE_METHODS.each {|v| instance_variable_set(:"@#{v}_value", nil)}
35
+ MULTI_VALUE_METHODS.each {|v| instance_variable_set(:"@#{v}_values", [])}
36
+ @per_page_value = @klass.default_page_size
37
+ @page_value = 1
38
+ @use_solr_value = true
39
+ @consistency_value = "QUORUM"
40
+ @extensions = []
41
+ @create_with_value = {}
42
+ @escape_value = true
43
+ apply_default_scope
44
+ end
45
+
46
+ # Returns true if the two relations have the same query parameters
47
+ def ==(other)
48
+ case other
49
+ when Relation
50
+ # This is not a valid implementation. It's a placeholder till I figure out the right way.
51
+ MULTI_VALUE_METHODS.each do |m|
52
+ return false unless other.send("#{m}_values") == self.send("#{m}_values")
53
+ end
54
+ SINGLE_VALUE_METHODS.each do |m|
55
+ return false unless other.send("#{m}_value") == self.send("#{m}_value")
56
+ end
57
+ return true
58
+ when Array
59
+ to_a == other
60
+ end
61
+ end
62
+
63
+ # Returns true if there are any results given the current criteria
64
+ def any?
65
+ if block_given?
66
+ to_a.any? { |*block_args| yield(*block_args) }
67
+ else
68
+ !empty?
69
+ end
70
+ end
71
+ alias :exists? :any?
72
+
73
+ # Returns the total number of entries that match the given search.
74
+ # This means the total number of matches regardless of page size.
75
+ # If the relation has not been populated yet, a limit of 1 will be
76
+ # placed on the query before it is executed.
77
+ #
78
+ # Compare with #size.
79
+ #
80
+ # XXX: Count via CQL is useless unless criteria has been applied.
81
+ # Otherwise you get everything that has ever been in the CF.
82
+ def count
83
+ @count ||= self.use_solr_value ? count_via_solr : count_via_cql
84
+ end
85
+
86
+ # Returns the current page for will_paginate compatibility
87
+ def current_page
88
+ self.page_value.try(:to_i)
89
+ end
90
+
91
+ # current_page - 1 or nil if there is no previous page
92
+ def previous_page
93
+ current_page > 1 ? (current_page - 1) : nil
94
+ end
95
+
96
+ # current_page + 1 or nil if there is no next page
97
+ def next_page
98
+ current_page < total_pages ? (current_page + 1) : nil
99
+ end
100
+
101
+ # Gets a default scope with no conditions or search attributes set.
102
+ def default_scope
103
+ clone.tap do |r|
104
+ SINGLE_VALUE_METHODS.each {|v| r.instance_variable_set(:"@#{v}_value", nil)}
105
+ MULTI_VALUE_METHODS.each {|v| r.instance_variable_set(:"@#{v}_values", [])}
106
+ apply_default_scope
107
+ end
108
+ end
109
+
110
+ # Returns true if there are no results given the current criteria
111
+ def empty?
112
+ return @results.empty? if loaded?
113
+
114
+ c = count
115
+ c.respond_to?(:zero?) ? c.zero? : c.empty?
116
+ end
117
+
118
+ # Returns true if there are multiple results given the current criteria
119
+ def many?
120
+ if block_given?
121
+ to_a.many? { |*block_args| yield(*block_args) }
122
+ else
123
+ count > 1
124
+ end
125
+ end
126
+
127
+ # Constructs a new instance of the class this relation points to with
128
+ # any criteria from this relation applied
129
+ def new(*args, &block)
130
+ scoping { @klass.new(*args, &block) }
131
+ end
132
+
133
+ # Reloads the results from cassandra or solr as appropriate
134
+ def reload
135
+ reset
136
+ to_a
137
+ self
138
+ end
139
+
140
+ # Empties out the current results. The next call to to_a
141
+ # will re-run the query.
142
+ def reset
143
+ @loaded = @first = @last = @scope_for_create = @count = nil
144
+ @results = []
145
+ end
146
+
147
+ def initialize_copy(other) #:nodoc:
148
+ reset
149
+ @search = nil
150
+ end
151
+
152
+ def clone #:nodoc:
153
+ dup.tap do |r|
154
+ MULTI_VALUE_METHODS.each do |m|
155
+ r.send("#{m}_values=", Marshal.load(Marshal.dump(self.send("#{m}_values"))))
156
+ end
157
+ SINGLE_VALUE_METHODS.each do |m|
158
+ r.send("#{m}_value=", Marshal.load(Marshal.dump(self.send("#{m}_value")))) if self.send("#{m}_value")
159
+ end
160
+ end
161
+ end
162
+
163
+ # Returns the size of the total result set for the given criteria
164
+ # NOTE that this takes pagination into account so will only return
165
+ # the number of results in the current page. DatastaxRails models
166
+ # can have a +default_page_size+ set which will cause them to be
167
+ # paginated all the time.
168
+ # Compare with #count
169
+ def size
170
+ return @results.size if loaded?
171
+ total_entries = count
172
+ (per_page_value && total_entries > per_page_value) ? per_page_value : total_entries
173
+ end
174
+
175
+ # Returns the total number of pages required to display the results
176
+ # given the current page size. Used by will_paginate.
177
+ def total_pages
178
+ return 1 unless @per_page_value
179
+ (count / @per_page_value.to_f).ceil
180
+ end
181
+
182
+ # Actually executes the query if not already executed.
183
+ # Returns a standard array thus no more methods may be chained.
184
+ def to_a
185
+ return @results if loaded?
186
+ if use_solr_value
187
+ @results = query_via_solr
188
+ @count = @results.total_entries
189
+ else
190
+ @results = query_via_cql
191
+ end
192
+ @loaded = true
193
+ @results
194
+ end
195
+ alias :all :to_a
196
+ alias :results :to_a
197
+
198
+ # Create a new object with all of the criteria from this relation applied
199
+ def create(*args, &block)
200
+ scoping { @klass.create(*args, &block) }
201
+ end
202
+
203
+ # Like +create+ but throws an exception on failure
204
+ def create!(*args, &block)
205
+ scoping { @klass.create!(*args, &block) }
206
+ end
207
+
208
+ def respond_to?(method, include_private = false) #:nodoc:
209
+ Array.method_defined?(method) ||
210
+ @klass.respond_to?(method, include_private) ||
211
+ super
212
+ end
213
+
214
+ # NOTE: This method does not actually run a count via CQL because it only
215
+ # works if you run against a secondary index. So this currently just
216
+ # delegates to the count_via_solr method.
217
+ def count_via_cql
218
+ with_solr.count_via_solr
219
+ end
220
+
221
+ # Constructs a CQL query and runs it against Cassandra directly. For this to
222
+ # work, you need to run against either the primary key or a secondary index.
223
+ # For ad-hoc queries, you will have to use Solr.
224
+ def query_via_cql
225
+ select_columns = select_values.empty? ? (@klass.attribute_definitions.keys - @klass.lazy_attributes) : select_values.flatten
226
+ cql = @cql.select(select_columns)
227
+ cql.using(@consistency_value) if @consistency_value
228
+ @where_values.each do |wv|
229
+ cql.conditions(wv)
230
+ end
231
+ results = []
232
+ CassandraCQL::Result.new(cql.execute).fetch do |row|
233
+ results << @klass.instantiate(row.row.key,row.to_hash)
234
+ end
235
+ results
236
+ end
237
+
238
+ # Runs the query with a limit of 1 just to grab the total results attribute off
239
+ # the result set.
240
+ def count_via_solr
241
+ limit(1).select(:id).to_a.total_entries
242
+ end
243
+
244
+ def solr_escape(str)
245
+ if str.is_a?(String) && escape_value
246
+ str.gsub(SOLR_CHAR_RX, '\\\\\1')
247
+ else
248
+ str
249
+ end
250
+ end
251
+
252
+ # Constructs a solr query to run against SOLR. At this point, only where, where_not,
253
+ # fulltext, order and pagination are supported. More will be added.
254
+ #
255
+ # It's also worth noting that where and where_not make use of individual filter_queries.
256
+ # If that's not what you want, you might be better off constructing your own fulltext
257
+ # query and sending that in.
258
+ def query_via_solr
259
+ filter_queries = []
260
+ orders = []
261
+ @where_values.each do |wv|
262
+ wv.each do |k,v|
263
+ # If v is blank, check that there is no value for the field in the document
264
+ filter_queries << (v.blank? ? "-#{k}:[* TO *]" : "#{k}:(#{solr_escape(v)})")
265
+ end
266
+ end
267
+
268
+ @where_not_values.each do |wnv|
269
+ wnv.each do |k,v|
270
+ # If v is blank, check for any value for the field in document
271
+ filter_queries << (v.blank? ? "#{k}:[* TO *]" : "-#{k}:(#{solr_escape(v)})")
272
+ end
273
+ end
274
+
275
+ @greater_than_values.each do |gtv|
276
+ gtv.each do |k,v|
277
+ filter_queries << "#{k}:[#{solr_escape(v)} TO *]"
278
+ end
279
+ end
280
+
281
+ @less_than_values.each do |ltv|
282
+ ltv.each do |k,v|
283
+ filter_queries << "#{k}:[* TO #{solr_escape(v)}]"
284
+ end
285
+ end
286
+
287
+ @order_values.each do |ov|
288
+ ov.each do |k,v|
289
+ if(@reverse_order_value)
290
+ orders << "#{k} #{v == :asc ? 'desc' : 'asc'}"
291
+ else
292
+ orders << "#{k} #{v == :asc ? 'asc' : 'desc'}"
293
+ end
294
+ end
295
+ end
296
+
297
+ sort = orders.join(",")
298
+
299
+ if @fulltext_values.empty?
300
+ q = "*:*"
301
+ else
302
+ q = @fulltext_values.collect {|ftv| "(" + ftv[:query] + ")"}.join(' AND ')
303
+ end
304
+
305
+ #TODO highlighting and fielded queries of fulltext
306
+
307
+ params = {:q => q}
308
+ unless sort.empty?
309
+ params[:sort] = sort
310
+ end
311
+
312
+ unless filter_queries.empty?
313
+ params[:fq] = filter_queries
314
+ end
315
+
316
+ #TODO Need to escape URL stuff (I think)
317
+ response = rsolr.paginate(@page_value, @per_page_value, 'select', :params => params)["response"]
318
+ results = DatastaxRails::Collection.new
319
+ results.total_entries = response['numFound'].to_i
320
+ response['docs'].each do |doc|
321
+ key = doc.delete('id')
322
+ results << @klass.instantiate(key,doc)
323
+ end
324
+ results
325
+ end
326
+
327
+ def inspect(just_me = false)
328
+ just_me ? super() : to_a.inspect
329
+ end
330
+
331
+ # Scope all queries to the current scope.
332
+ #
333
+ # ==== Example
334
+ #
335
+ # Comment.where(:post_id => 1).scoping do
336
+ # Comment.first # SELECT * FROM comments WHERE post_id = 1
337
+ # end
338
+ #
339
+ # Please check unscoped if you want to remove all previous scopes (including
340
+ # the default_scope) during the execution of a block.
341
+ def scoping
342
+ @klass.send(:with_scope, self, :overwrite) { yield }
343
+ end
344
+
345
+ def where_values_hash #:nodoc:
346
+ where_values.inject({}) { |values,v| values.merge(v) }
347
+ end
348
+
349
+ def scope_for_create #:nodoc:
350
+ @scope_for_create ||= where_values_hash.merge(create_with_value)
351
+ end
352
+
353
+ # Sends a commit message to SOLR
354
+ def commit_solr
355
+ rsolr.commit :commit_attributes => {}
356
+ end
357
+
358
+ # Everything that gets indexed into solr is downcased as part of the analysis phase.
359
+ # Normally, this is done to the query as well, but if your query includes wildcards
360
+ # then analysis isn't performed. This means that the query does not get downcased.
361
+ # We therefore need to perform the downcasing ourselves. This does it while still
362
+ # leaving boolean operations (AND, OR, NOT) upcased.
363
+ def self.downcase_query(value)
364
+ if(value.is_a?(String))
365
+ value.split(/\bAND\b/).collect do |a|
366
+ a.split(/\bOR\b/).collect do |o|
367
+ o.split(/\bNOT\b/).collect do |n|
368
+ n.downcase
369
+ end.join("NOT")
370
+ end.join("OR")
371
+ end.join("AND")
372
+ else
373
+ value
374
+ end
375
+ end
376
+
377
+ protected
378
+
379
+ def method_missing(method, *args, &block) #:nodoc:
380
+ if Array.method_defined?(method)
381
+ to_a.send(method, *args, &block)
382
+ elsif @klass.respond_to?(method)
383
+ scoping { @klass.send(method, *args, &block) }
384
+ else
385
+ super
386
+ end
387
+ end
388
+
389
+ def rsolr #:nodoc:
390
+ @rsolr ||= RSolr.connect :url => "#{DatastaxRails::Base.config[:solr][:url]}/#{DatastaxRails::Base.connection.keyspace}.#{@klass.column_family}"
391
+ end
392
+ end
393
+ end
@@ -0,0 +1,106 @@
1
+ module DatastaxRails
2
+ module Schema
3
+
4
+ class Migration
5
+
6
+ @@verbose = true
7
+ cattr_accessor :verbose
8
+
9
+ # Returns the raw connection to Cassandra
10
+ def self.connection
11
+ DatastaxRails::Base.connection
12
+ end
13
+
14
+ def self.migrate(direction)
15
+ return unless respond_to?(direction)
16
+
17
+ case direction
18
+ when :up then announce "migrating"
19
+ when :down then announce "reverting"
20
+ end
21
+
22
+ result = nil
23
+ time = Benchmark.measure { result = send("#{direction}") }
24
+
25
+ case direction
26
+ when :up then announce "migrated (%.4fs)" % time.real; write
27
+ when :down then announce "reverted (%.4fs)" % time.real; write
28
+ end
29
+
30
+ result
31
+ end
32
+
33
+ # Creates a new column family with the given name. Column family configurations can be set within
34
+ # a block like this:
35
+ #
36
+ # create_column_family(:users) do |cf|
37
+ # cf.comment = 'Users column family'
38
+ # cf.comparator_type = 'TimeUUIDType'
39
+ # end
40
+ #
41
+ # A complete list of available configuration settings is here:
42
+ #
43
+ # http://github.com/fauna/cassandra/blob/master/vendor/0.7/gen-rb/cassandra_types.rb
44
+ #
45
+ # Scroll down to the CfDef definition.
46
+ def self.create_column_family(name, &block)
47
+ say_with_time("create_column_family #{name}") do
48
+ column_family_tasks.create(name, &block)
49
+ end
50
+ end
51
+
52
+ # Drops the given column family
53
+ def self.drop_column_family(name)
54
+ say_with_time("drop_column_family #{name}") do
55
+ column_family_tasks.drop(name)
56
+ end
57
+ end
58
+
59
+ # Renames the column family from the old name to the new name
60
+ def self.rename_column_family(old_name, new_name)
61
+ say_with_time("rename_column_family #{name}") do
62
+ column_family_tasks.rename(old_name, new_name)
63
+ end
64
+ end
65
+
66
+ def self.write(text="")
67
+ puts(text) if verbose
68
+ end
69
+
70
+ def self.announce(message)
71
+ version = defined?(@version) ? @version : nil
72
+
73
+ text = "#{version} #{name}: #{message}"
74
+ length = [0, 75 - text.length].max
75
+ write "== %s %s" % [text, "=" * length]
76
+ end
77
+
78
+ def self.say(message, subitem=false)
79
+ write "#{subitem ? " ->" : "--"} #{message}"
80
+ end
81
+
82
+ def self.say_with_time(message)
83
+ say(message)
84
+ result = nil
85
+ time = Benchmark.measure { result = yield }
86
+ say "%.4fs" % time.real, :subitem
87
+ say("#{result} rows", :subitem) if result.is_a?(Integer)
88
+ result
89
+ end
90
+
91
+ def self.suppress_messages
92
+ save, self.verbose = verbose, false
93
+ yield
94
+ ensure
95
+ self.verbose = save
96
+ end
97
+
98
+ private
99
+
100
+ def self.column_family_tasks
101
+ Tasks::ColumnFamily.new(DatastaxRails::Base.connection.keyspace)
102
+ end
103
+
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,25 @@
1
+ module DatastaxRails
2
+ module Schema
3
+
4
+ # MigrationProxy is used to defer loading of the actual migration classes
5
+ # until they are needed
6
+ class MigrationProxy
7
+
8
+ attr_accessor :name, :version, :filename
9
+
10
+ delegate :migrate, :announce, :write, :to=>:migration
11
+
12
+ private
13
+
14
+ def migration
15
+ @migration ||= load_migration
16
+ end
17
+
18
+ def load_migration
19
+ require(File.expand_path(filename))
20
+ name.constantize
21
+ end
22
+
23
+ end
24
+ end
25
+ end