datastax_rails 1.0.19.0 → 1.1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/README.rdoc +13 -2
  3. data/config/solrconfig.xml +3 -0
  4. data/lib/datastax_rails/associations/collection_association.rb +31 -0
  5. data/lib/datastax_rails/attribute_methods/definition.rb +2 -2
  6. data/lib/datastax_rails/base.rb +3 -7
  7. data/lib/datastax_rails/connection.rb +1 -0
  8. data/lib/datastax_rails/cql/alter_column_family.rb +9 -0
  9. data/lib/datastax_rails/cql/base.rb +2 -1
  10. data/lib/datastax_rails/cql/create_column_family.rb +3 -3
  11. data/lib/datastax_rails/cql/create_index.rb +25 -0
  12. data/lib/datastax_rails/cql/create_keyspace.rb +3 -3
  13. data/lib/datastax_rails/cql/delete.rb +3 -3
  14. data/lib/datastax_rails/cql/drop_index.rb +13 -0
  15. data/lib/datastax_rails/cql/insert.rb +2 -2
  16. data/lib/datastax_rails/cql/select.rb +2 -2
  17. data/lib/datastax_rails/cql/update.rb +20 -20
  18. data/lib/datastax_rails/cql.rb +2 -0
  19. data/lib/datastax_rails/persistence.rb +2 -10
  20. data/lib/datastax_rails/railtie.rb +7 -0
  21. data/lib/datastax_rails/relation/batches.rb +23 -10
  22. data/lib/datastax_rails/relation/facet_methods.rb +17 -0
  23. data/lib/datastax_rails/relation/finder_methods.rb +2 -2
  24. data/lib/datastax_rails/relation/search_methods.rb +1 -1
  25. data/lib/datastax_rails/relation.rb +14 -6
  26. data/lib/datastax_rails/tasks/column_family.rb +97 -18
  27. data/lib/datastax_rails/tasks/ds.rake +11 -0
  28. data/lib/datastax_rails/types/array_type.rb +1 -1
  29. data/lib/datastax_rails/types/boolean_type.rb +1 -1
  30. data/lib/datastax_rails/types/date_type.rb +1 -1
  31. data/lib/datastax_rails/types/float_type.rb +1 -1
  32. data/lib/datastax_rails/types/integer_type.rb +1 -1
  33. data/lib/datastax_rails/types/string_type.rb +2 -2
  34. data/lib/datastax_rails/types/text_type.rb +3 -4
  35. data/lib/datastax_rails/types/time_type.rb +1 -1
  36. data/lib/datastax_rails/validations/associated.rb +43 -0
  37. data/lib/datastax_rails/validations.rb +14 -2
  38. data/lib/datastax_rails/version.rb +1 -1
  39. data/lib/datastax_rails.rb +14 -14
  40. data/spec/datastax_rails/associations/has_many_association_spec.rb +1 -0
  41. data/spec/datastax_rails/base_spec.rb +6 -0
  42. data/spec/datastax_rails/cql/select_spec.rb +3 -3
  43. data/spec/datastax_rails/cql/update_spec.rb +2 -2
  44. data/spec/datastax_rails/persistence_spec.rb +16 -12
  45. data/spec/datastax_rails/relation/batches_spec.rb +20 -16
  46. data/spec/datastax_rails/relation/finder_methods_spec.rb +2 -2
  47. data/spec/dummy/log/test.log +3316 -0
  48. data/spec/spec.opts +0 -1
  49. data/spec/support/connection_double.rb +6 -0
  50. data/spec/support/default_consistency_shared_examples.rb +4 -2
  51. metadata +86 -107
@@ -46,7 +46,7 @@ module DatastaxRails
46
46
  if coder.options[:solr_type]
47
47
  @fields.push({ :name => attr.name,
48
48
  :type => coder.options[:solr_type].to_s,
49
- :indexed => coder.options[:indexed].to_s,
49
+ :indexed => (coder.options[:indexed] == :solr).to_s,
50
50
  :stored => coder.options[:stored].to_s,
51
51
  :multi_valued => coder.options[:multi_valued].to_s })
52
52
  end
@@ -72,7 +72,10 @@ module DatastaxRails
72
72
  end
73
73
 
74
74
  def reindex_solr(model)
75
- if model == ':all'
75
+ if model == 'all'
76
+ Dir[Rails.root.join("app","models",'*.rb').to_s].each do |file|
77
+ require File.basename(file, File.extname(file))
78
+ end
76
79
  models_to_index = DatastaxRails::Base.models
77
80
  else
78
81
  models_to_index = [model.constantize]
@@ -90,14 +93,38 @@ module DatastaxRails
90
93
  end
91
94
  end
92
95
 
96
+ def create_solr_core(model)
97
+ if model == 'all'
98
+ Dir[Rails.root.join("app","models",'*.rb').to_s].each do |file|
99
+ require File.basename(file, File.extname(file))
100
+ end
101
+ cores_to_create = DatastaxRails::Base.models
102
+ else
103
+ cores_to_create = [model.constantize]
104
+ end
105
+ cores_to_create.each do |m|
106
+ next if m.payload_model?
107
+ # Create the SOLR Core
108
+ url = "#{DatastaxRails::Base.solr_base_url}/admin/cores?action=CREATE&name=#{DatastaxRails::Base.config[:keyspace]}.#{m.column_family}&recovery=true"
109
+ puts "Posting create command to '#{url}'"
110
+ `curl -s -X POST '#{url}'`
111
+ if Rails.env.production?
112
+ sleep(5)
113
+ end
114
+ end
115
+ end
116
+
93
117
  def upload_solr_schemas(column_family)
94
118
  force = !column_family.nil?
95
119
  column_family ||= :all
96
120
  # Ensure schema migrations CF exists
97
- unless connection.schema.column_families['schema_migrations']
121
+ unless column_family_exists?('schema_migrations')
122
+ puts "Creating schema_migrations column family"
98
123
  connection.execute_cql_query(DatastaxRails::Cql::CreateColumnFamily.new('schema_migrations').key_type(:text).columns(:digest => :text, :solrconfig => :text, :stopwords => :text).to_cql)
99
124
  end
100
125
 
126
+ check_key_name('schema_migrations')
127
+
101
128
  solrconfig = File.read(File.join(File.dirname(__FILE__),"..","..","..","config","solrconfig.xml"))
102
129
  stopwords = File.read(File.join(File.dirname(__FILE__),"..","..","..","config","stopwords.txt"))
103
130
  solrconfig_digest = Digest::SHA1.hexdigest(solrconfig)
@@ -121,28 +148,23 @@ module DatastaxRails
121
148
  models_to_upload.each do |model|
122
149
  if model.payload_model?
123
150
  next if model == DatastaxRails::PayloadModel
124
- unless connection.schema.column_families[model.column_family.to_s]
151
+ unless column_family_exists?(model.column_family.to_s)
125
152
  puts "Creating payload model #{model.column_family}"
126
153
  columns = {:chunk => :int, :payload => :text}
127
- cql = DatastaxRails::Cql::CreateColumnFamily.new(model.column_family).key_name(:digest).key_columns("digest\", \"chunk").key_type(:text).columns(columns).with("COMPACT STORAGE").to_cql
154
+ cql = DatastaxRails::Cql::CreateColumnFamily.new(model.column_family).key_name(:digest).key_columns("digest, chunk").key_type(:text).columns(columns).with("COMPACT STORAGE").to_cql
128
155
  puts cql
129
156
  connection.execute_cql_query(cql)
130
157
  end
131
158
  else
132
159
  newcf = false
133
- newschema = false
134
- unless connection.schema.column_families[model.column_family.to_s]
160
+ unless column_family_exists?(model.column_family.to_s)
135
161
  newcf = true
136
162
  puts "Creating normal model #{model.column_family}"
137
- cql = DatastaxRails::Cql::CreateColumnFamily.new(model.column_family).key_type(:text).columns(:updated_at => :text, :created_at => :text).to_cql
138
- puts cql
139
- connection.execute_cql_query(cql)
140
- sleep(5) if Rails.env.production?
141
163
  end
142
164
  schema = generate_solr_schema(model)
143
165
  schema_digest = Digest::SHA1.hexdigest(schema)
144
166
 
145
- results = DatastaxRails::Cql::Select.new(SchemaMigration, ['*']).conditions(:KEY => model.column_family).execute
167
+ results = DatastaxRails::Cql::Select.new(SchemaMigration, ['*']).conditions(:key => model.column_family).execute
146
168
  sm_digests = CassandraCQL::Result.new(results).fetch.try(:to_hash) || {}
147
169
 
148
170
  solr_url = "#{DatastaxRails::Base.solr_base_url}/resource/#{DatastaxRails::Base.config[:keyspace]}.#{model.column_family}"
@@ -194,7 +216,7 @@ module DatastaxRails
194
216
  break
195
217
  end
196
218
  DatastaxRails::Cql::Update.new(SchemaMigration, model.column_family).columns(:digest => schema_digest).execute
197
- newschema = true
219
+ reindex_solr(model.to_s) unless newcf
198
220
  end
199
221
 
200
222
  if newcf
@@ -207,21 +229,41 @@ module DatastaxRails
207
229
  end
208
230
  end
209
231
 
210
- # Check for unindexed columns
232
+ check_key_name(model.column_family)
233
+
234
+ # Check for missing columns or columns needing cassandra indexes
211
235
  model.attribute_definitions.each do |attribute, definition|
212
- if !connection.schema.column_families[model.column_family.to_s].columns.has_key?(attribute.to_s)# &&
213
- #!definition.coder.options[:stored] &&
214
- #!definition.coder.options[:indexed]
215
-
236
+ unless column_exists?(model.column_family.to_s, attribute.to_s)
216
237
  puts "Adding column '#{attribute}' to '#{model.column_family}'"
217
238
  DatastaxRails::Cql::AlterColumnFamily.new(model.column_family).add(attribute => :text).execute
218
239
  end
240
+ if(definition.coder.options[:indexed] == :cassandra)
241
+ unless index_exists?(model.column_family.to_s, attribute.to_s)
242
+ if index_exists?(model.column_family.to_s, attribute.to_s)
243
+ puts "Dropping solr index on #{model.column_family.to_s}.#{attribute.to_s}"
244
+ DatastaxRails::Cql::DropIndex.new(solr_index_cql_name(model.column_family.to_s, attribute.to_s)).execute
245
+ end
246
+ puts "Creating cassandra index on #{model.column_family.to_s}.#{attribute.to_s}"
247
+ DatastaxRails::Cql::CreateIndex.new(cassandra_index_cql_name(model.column_family.to_s, attribute.to_s)).on(model.column_family.to_s).column(attribute.to_s).execute
248
+ end
249
+ end
219
250
  end
220
251
  end
221
252
  end
222
253
  end
223
254
 
224
255
  private
256
+
257
+ def check_key_name(cf)
258
+ klass = OpenStruct.new(:column_family => 'system.schema_columnfamilies', :default_consistency => 'QUORUM')
259
+ cql = DatastaxRails::Cql::ColumnFamily.new(klass)
260
+ results = CassandraCQL::Result.new(cql.select("key_alias, key_aliases").conditions('keyspace_name' => @keyspace, 'columnfamily_name' => cf).execute)
261
+ result = results.fetch
262
+ if(result['key_alias'] == 'KEY' && (result['key_aliases'].blank? || !result['key_aliases'].include?('key')))
263
+ puts "Renaming KEY column for #{cf}"
264
+ DatastaxRails::Cql::AlterColumnFamily.new(cf).rename("KEY",'key').execute
265
+ end
266
+ end
225
267
 
226
268
  def connection
227
269
  DatastaxRails::Base.connection
@@ -245,6 +287,43 @@ module DatastaxRails
245
287
 
246
288
  cf
247
289
  end
290
+
291
+ # def solr_index_system_name(cf, column)
292
+ # "#{@keyspace}.#{cf.to_s}#{column.to_s}"
293
+ # end
294
+
295
+ def solr_index_cql_name(cf, column)
296
+ "#{@keyspace}_#{cf.to_s}_#{column.to_s}_index"
297
+ end
298
+
299
+ def cassandra_index_cql_name(cf, column)
300
+ "#{cf.to_s}_#{column.to_s}_idx"
301
+ end
302
+
303
+ # def cassandra_index_system_name(cf, column)
304
+ # "#{cf.to_s}.#{cf.to_s}_#{column.to_s}_idx"
305
+ # end
306
+
307
+ def column_family_exists?(cf)
308
+ klass = OpenStruct.new(:column_family => 'system.schema_columnfamilies', :default_consistency => 'QUORUM')
309
+ cql = DatastaxRails::Cql::ColumnFamily.new(klass)
310
+ results = CassandraCQL::Result.new(cql.select("count(*)").conditions('keyspace_name' => @keyspace, 'columnfamily_name' => cf).execute)
311
+ results.fetch['count'] > 0
312
+ end
313
+
314
+ def column_exists?(cf, col)
315
+ klass = OpenStruct.new(:column_family => 'system.schema_columns', :default_consistency => 'QUORUM')
316
+ cql = DatastaxRails::Cql::ColumnFamily.new(klass)
317
+ results = CassandraCQL::Result.new(cql.select("count(*)").conditions('keyspace_name' => @keyspace, 'columnfamily_name' => cf, 'column_name' => col).execute)
318
+ results.fetch['count'] > 0
319
+ end
320
+
321
+ def index_exists?(cf, col)
322
+ klass = OpenStruct.new(:column_family => 'system.schema_columns', :default_consistency => 'QUORUM')
323
+ cql = DatastaxRails::Cql::ColumnFamily.new(klass)
324
+ results = CassandraCQL::Result.new(cql.select("index_name").conditions('keyspace_name' => @keyspace, 'columnfamily_name' => cf, 'column_name' => col).execute)
325
+ results.fetch['index_name'] != nil
326
+ end
248
327
  end
249
328
  end
250
329
  end
@@ -60,6 +60,17 @@ namespace :ds do
60
60
  end
61
61
  end
62
62
 
63
+ desc 'Create SOLR Core (Normally not needed) -- pass in a model name (:all creates everything)'
64
+ task :create_core, [:model] => :configure do |t, args|
65
+ if args[:model].blank?
66
+ puts "\nUSAGE: rake ds:create_core[Model]"
67
+ else
68
+ cf = DatastaxRails::Tasks::ColumnFamily.new(@config['keyspace'])
69
+ puts "Creating core #{args[:model]}"
70
+ cf.create_solr_core(args[:model])
71
+ end
72
+ end
73
+
63
74
  desc 'Load the seed data from ds/seeds.rb'
64
75
  task :seed => :environment do
65
76
  seed_file = Rails.root.join("ks","seeds.rb")
@@ -7,7 +7,7 @@ module DatastaxRails
7
7
  #
8
8
  # That would give you all the posts that have Technology somewhere in the tags array.
9
9
  class ArrayType < BaseType
10
- DEFAULTS = {:solr_type => 'array', :indexed => true, :stored => true, :multi_valued => false, :sortable => false, :tokenized => true, :fulltext => true}
10
+ DEFAULTS = {:solr_type => 'array', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => false, :tokenized => true, :fulltext => true}
11
11
 
12
12
  # An extension to normal arrays that allow for tracking of dirty values. This is
13
13
  # used by ActiveModel's change tracking framework.
@@ -1,7 +1,7 @@
1
1
  module DatastaxRails
2
2
  module Types
3
3
  class BooleanType < BaseType
4
- DEFAULTS = {:solr_type => 'boolean', :indexed => true, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false}
4
+ DEFAULTS = {:solr_type => 'boolean', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false}
5
5
  TRUE_VALS = [true, 'true', '1', 'Y']
6
6
  FALSE_VALS = [false, 'false', '0', '', 'N', nil, 'null']
7
7
  VALID_VALS = TRUE_VALS + FALSE_VALS
@@ -1,7 +1,7 @@
1
1
  module DatastaxRails
2
2
  module Types
3
3
  class DateType < BaseType
4
- DEFAULTS = {:solr_type => 'date', :indexed => true, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false}
4
+ DEFAULTS = {:solr_type => 'date', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false}
5
5
  FORMAT = '%Y-%m-%dT%H:%M:%SZ'
6
6
 
7
7
  def encode(value)
@@ -1,7 +1,7 @@
1
1
  module DatastaxRails
2
2
  module Types
3
3
  class FloatType < BaseType
4
- DEFAULTS = {:solr_type => 'float', :indexed => true, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false}
4
+ DEFAULTS = {:solr_type => 'float', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false}
5
5
  REGEX = /\A[-+]?(\d+(\.\d+)?|\.\d+)\Z/
6
6
  def encode(float)
7
7
  return -10191980.0 if float.blank?
@@ -1,7 +1,7 @@
1
1
  module DatastaxRails
2
2
  module Types
3
3
  class IntegerType < BaseType
4
- DEFAULTS = {:solr_type => 'int', :indexed => true, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false}
4
+ DEFAULTS = {:solr_type => 'int', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false}
5
5
  REGEX = /\A[-+]?\d+\Z/
6
6
  def encode(int)
7
7
  return -10191980 if int.blank?
@@ -1,14 +1,14 @@
1
1
  module DatastaxRails
2
2
  module Types
3
3
  class StringType < BaseType
4
- DEFAULTS = {:solr_type => 'string', :indexed => true, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => true}
4
+ DEFAULTS = {:solr_type => 'string', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => true}
5
5
  def encode(str)
6
6
  str = "" unless str
7
7
  str.to_s
8
8
  end
9
9
 
10
10
  def wrap(record, name, value)
11
- txt = (value.frozen? ? value.dup : value)
11
+ txt = (value.frozen? ? value.to_s.dup : value)
12
12
  txt.respond_to?(:force_encoding) ? txt.force_encoding('UTF-8') : txt
13
13
  end
14
14
  end
@@ -1,10 +1,9 @@
1
1
  module DatastaxRails
2
2
  module Types
3
3
  class TextType < BaseType
4
- DEFAULTS = {:solr_type => 'text', :indexed => true, :stored => true, :multi_valued => false, :sortable => false, :tokenized => true, :fulltext => true}
4
+ DEFAULTS = {:solr_type => 'text', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => false, :tokenized => true, :fulltext => true}
5
5
  def encode(str)
6
- raise ArgumentError.new("#{self} requires a String") unless str.kind_of?(String)
7
- str.dup
6
+ str.to_s.dup
8
7
  end
9
8
 
10
9
  def wrap(record, name, value)
@@ -13,4 +12,4 @@ module DatastaxRails
13
12
  end
14
13
  end
15
14
  end
16
- end
15
+ end
@@ -1,7 +1,7 @@
1
1
  module DatastaxRails
2
2
  module Types
3
3
  class TimeType < BaseType
4
- DEFAULTS = {:solr_type => 'date', :indexed => true, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false}
4
+ DEFAULTS = {:solr_type => 'date', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false}
5
5
  FORMAT = "%Y-%m-%dT%H:%M:%SZ"
6
6
 
7
7
  def encode(time)
@@ -0,0 +1,43 @@
1
+ module DatastaxRails
2
+ module Validations
3
+ class AssociatedValidator < ActiveModel::EachValidator
4
+ def validate_each(record, attribute, value)
5
+ if Array.wrap(value).reject {|r| r.destroyed? || r.valid?}.any?
6
+ record.errors.add(attribute, :invalid, options.merge(:value => value))
7
+ end
8
+ end
9
+ end
10
+
11
+ module ClassMethods
12
+ # Validates whether the associated object or objects are all valid themselves. Works with any kind of association.
13
+ #
14
+ # class Book < DatastaxRails::Base
15
+ # has_many :pages
16
+ # belongs_to :library
17
+ #
18
+ # validates_associated :pages, :library
19
+ # end
20
+ #
21
+ # WARNING: This validation must not be used on both ends of an association. Doing so will lead to a circular dependency and cause infinite recursion.
22
+ #
23
+ # NOTE: This validation will not fail if the association hasn't been assigned. If you want to
24
+ # ensure that the association is both present and guaranteed to be valid, you also need to
25
+ # use +validates_presence_of+.
26
+ #
27
+ # Configuration options:
28
+ # * <tt>:message</tt> - A custom error message (default is: "is invalid")
29
+ # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
30
+ # validation contexts by default (+nil+), other options are <tt>:create</tt>
31
+ # and <tt>:update</tt>.
32
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
33
+ # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
34
+ # method, proc or string should return or evaluate to a true or false value.
35
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
36
+ # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
37
+ # method, proc or string should return or evaluate to a true or false value.
38
+ def validates_associated(*attr_names)
39
+ validates_with AssociatedValidator, _merge_attributes(attr_names)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -24,9 +24,20 @@ module DatastaxRails
24
24
  end
25
25
  end
26
26
 
27
- def valid?
27
+
28
+ # Runs all the validations within the specified context. Returns true if no errors are found,
29
+ # false otherwise.
30
+ #
31
+ # If the argument is false (default is +nil+), the context is set to <tt>:create</tt> if
32
+ # <tt>new_record?</tt> is true, and to <tt>:update</tt> if it is not.
33
+ #
34
+ # Validations with no <tt>:on</tt> option will run no matter the context. Validations with
35
+ # some <tt>:on</tt> option will only run in the specified context.
36
+ def valid?(context = nil)
28
37
  run_callbacks :validation do
29
- super
38
+ context ||= (new_record? ? :create : :update)
39
+ output = super(context)
40
+ errors.empty? && output
30
41
  end
31
42
  end
32
43
 
@@ -46,3 +57,4 @@ module DatastaxRails
46
57
  end
47
58
 
48
59
  require 'datastax_rails/validations/uniqueness'
60
+ require 'datastax_rails/validations/associated'
@@ -1,4 +1,4 @@
1
1
  module DatastaxRails
2
2
  # The current version of the gem
3
- VERSION = "1.0.19.0"
3
+ VERSION = "1.1.0.3"
4
4
  end
@@ -1,5 +1,5 @@
1
1
  require 'active_support/all'
2
- require 'cassandra-cql/1.1'
2
+ require 'cassandra-cql/1.2'
3
3
  require 'blankslate'
4
4
  require 'schema_migration'
5
5
 
@@ -80,19 +80,19 @@ end
80
80
  require "thrift"
81
81
  # Thrift is how we communicate with Cassandra. We need to do a little fixup
82
82
  # work to handle UTF-8 properly in Ruby 1.8.6.
83
- module Thrift
84
- class BinaryProtocol
85
- def write_string(str)
86
- if(str.respond_to?(:bytesize))
87
- size = str.bytesize
88
- else
89
- size = str.size
90
- end
91
- write_i32(size)
92
- trans.write(str)
93
- end
94
- end
95
- end
83
+ # module Thrift
84
+ # class BinaryProtocol
85
+ # def write_string(str)
86
+ # if(str.respond_to?(:bytesize))
87
+ # size = str.bytesize
88
+ # else
89
+ # size = str.size
90
+ # end
91
+ # write_i32(size)
92
+ # trans.write(str)
93
+ # end
94
+ # end
95
+ # end
96
96
 
97
97
  require 'datastax_rails/railtie' if defined?(Rails)
98
98
  require 'datastax_rails/errors'
@@ -27,6 +27,7 @@ describe DatastaxRails::Base do
27
27
  end
28
28
 
29
29
  it "should create records with the proper foreign key" do
30
+ Person.commit_solr
30
31
  p = Person.create(:name => 'jason')
31
32
  p.cars.create(:name => 'Jeep')
32
33
  Car.commit_solr
@@ -16,4 +16,10 @@ describe DatastaxRails::Base do
16
16
  it "should raise RecordNotFound when finding a bogus ID" do
17
17
  lambda { Person.find("xyzzy") }.should raise_exception(DatastaxRails::RecordNotFound)
18
18
  end
19
+
20
+ xit "should skip records that are missing dsr in cassandra" do
21
+ p = Person.create(:name => 'Jason')
22
+ Person.cql.delete(p.id).columns(['dsr']).execute
23
+ Person.find_by_name('Jason').should be_nil
24
+ end
19
25
  end
@@ -2,13 +2,13 @@ require 'spec_helper'
2
2
 
3
3
  describe DatastaxRails::Cql::Select do
4
4
  before(:each) do
5
- @model_class = mock("Model Class", :column_family => 'users', :default_consistency => DatastaxRails::Cql::Consistency::QUORUM)
5
+ @model_class = double("Model Class", :column_family => 'users', :default_consistency => DatastaxRails::Cql::Consistency::QUORUM)
6
6
  end
7
7
 
8
8
  it "should generate valid CQL" do
9
9
  cql = DatastaxRails::Cql::Select.new(@model_class, ["*"])
10
- cql.using(DatastaxRails::Cql::Consistency::QUORUM).conditions(:KEY => '12345').limit(1)
11
- cql.to_cql.should == "SELECT * FROM users USING CONSISTENCY QUORUM WHERE \"KEY\" = '12345' LIMIT 1 "
10
+ cql.using(DatastaxRails::Cql::Consistency::QUORUM).conditions(:key => '12345').limit(1)
11
+ cql.to_cql.should == "SELECT * FROM users WHERE \"key\" = '12345' LIMIT 1 "
12
12
  end
13
13
 
14
14
  it_has_behavior "default_consistency"
@@ -2,13 +2,13 @@ require 'spec_helper'
2
2
 
3
3
  describe DatastaxRails::Cql::Update do
4
4
  before(:each) do
5
- @model_class = mock("Model Class", :column_family => 'users', :default_consistency => DatastaxRails::Cql::Consistency::QUORUM)
5
+ @model_class = double("Model Class", :column_family => 'users', :default_consistency => DatastaxRails::Cql::Consistency::QUORUM)
6
6
  end
7
7
 
8
8
  it "should generate valid CQL" do
9
9
  cql = DatastaxRails::Cql::Update.new(@model_class, "12345")
10
10
  cql.using(DatastaxRails::Cql::Consistency::QUORUM).columns(:name => 'John', :age => '23')
11
- cql.to_cql.should match(/update users using consistency QUORUM SET ("name" = 'John', "age" = '23'|"age" = '23', "name" = 'John') WHERE "KEY" IN \('12345'\)/)
11
+ cql.to_cql.should match(/update users SET ("name" = 'John', "age" = '23'|"age" = '23', "name" = 'John') WHERE key IN \('12345'\)/)
12
12
  end
13
13
 
14
14
  it_has_behavior "default_consistency"
@@ -5,16 +5,16 @@ describe "DatastaxRails::Base" do
5
5
  describe "with cql" do
6
6
  describe "#create" do
7
7
  it "should persist at the given consistency level" do
8
- DatastaxRails::Base.connection.should_receive(:execute_cql_query).with(/USING CONSISTENCY LOCAL_QUORUM/i).and_return(true)
9
8
  Person.storage_method = :cql
9
+ DatastaxRails::Base.connection.should_receive(:execute_cql_query).with(an_instance_of(String), :consistency => CassandraCQL::Thrift::ConsistencyLevel::LOCAL_QUORUM)
10
10
  Person.create({:name => 'Steven'},{:consistency => 'LOCAL_QUORUM'})
11
11
  end
12
12
  end
13
13
 
14
14
  describe "#save" do
15
15
  it "should persist at the given consistency level" do
16
- DatastaxRails::Base.connection.should_receive(:execute_cql_query).with(/USING CONSISTENCY LOCAL_QUORUM/i).and_return(true)
17
16
  Person.storage_method = :cql
17
+ DatastaxRails::Base.connection.should_receive(:execute_cql_query).with(an_instance_of(String), :consistency => CassandraCQL::Thrift::ConsistencyLevel::LOCAL_QUORUM)
18
18
  p=Person.new(:name => 'Steven')
19
19
  p.save(:consistency => 'LOCAL_QUORUM')
20
20
  end
@@ -24,7 +24,7 @@ describe "DatastaxRails::Base" do
24
24
  describe "with solr" do
25
25
  describe "#create" do
26
26
  it "should persist at the given consistency level" do
27
- Person.solr_connection.should_receive(:update).with(hash_including(:params => {:replacefields => false, :cl => 'LOCAL_QUORUM'})).and_return(true)
27
+ Person.solr_connection.should_receive(:update).with(hash_including(:params => hash_including({:cl => 'LOCAL_QUORUM'}))).and_return(true)
28
28
  Person.storage_method = :solr
29
29
  Person.create({:name => 'Steven'},{:consistency => 'LOCAL_QUORUM'})
30
30
  end
@@ -32,19 +32,23 @@ describe "DatastaxRails::Base" do
32
32
 
33
33
  describe "#save" do
34
34
  it "should persist at the given consistency level" do
35
- Person.solr_connection.should_receive(:update).with(hash_including(:params => {:replacefields => false, :cl => 'LOCAL_QUORUM'})).and_return(true)
35
+ Person.solr_connection.should_receive(:update).with(hash_including(:params => hash_including({:cl => 'LOCAL_QUORUM'}))).and_return(true)
36
36
  Person.storage_method = :solr
37
37
  p=Person.new(:name => 'Steven')
38
38
  p.save(:consistency => 'LOCAL_QUORUM')
39
39
  end
40
40
 
41
41
  it "should successfully remove columns that are set to nil" do
42
- Person.storage_method = :solr
43
- p = Person.create(:name => 'Steven', :birthdate => Date.today)
44
- Person.commit_solr
45
- p.birthdate = nil
46
- p.save
47
- Person.find_by_name('Steven').birthdate.should be_nil
42
+ pending do
43
+ Person.storage_method = :solr
44
+ p = Person.create!(:name => 'Steven', :birthdate => Date.today)
45
+ Person.commit_solr
46
+ p = Person.find_by_name('Steven')
47
+ p.birthdate = nil
48
+ p.save
49
+ Person.commit_solr
50
+ Person.find by_name('Steven').birthdate.should be_nil
51
+ end
48
52
  end
49
53
  end
50
54
  end
@@ -52,7 +56,7 @@ describe "DatastaxRails::Base" do
52
56
  describe "#remove" do
53
57
  it "should remove at the given consistency level" do
54
58
  p=Person.create(:name => 'Steven')
55
- DatastaxRails::Base.connection.should_receive(:execute_cql_query).with(/USING CONSISTENCY LOCAL_QUORUM/i).and_return(true)
59
+ DatastaxRails::Base.connection.should_receive(:execute_cql_query).with(an_instance_of(String), :consistency => CassandraCQL::Thrift::ConsistencyLevel::LOCAL_QUORUM)
56
60
  p.destroy(:consistency => :local_quorum)
57
61
  end
58
62
  end
@@ -65,7 +69,7 @@ describe "DatastaxRails::Base" do
65
69
  end
66
70
 
67
71
  it "should store really large files" do
68
- file = IO.read("/dev/urandom", 25.megabyte)
72
+ file = IO.read("/dev/zero", 25.megabyte)
69
73
  CarPayload.create(:digest => 'limo', :payload => file)
70
74
  CarPayload.find('limo').payload.should == file
71
75
  end
@@ -3,30 +3,34 @@ require 'spec_helper'
3
3
  describe DatastaxRails::Relation do
4
4
  before(:each) do
5
5
  @relation = DatastaxRails::Relation.new(Hobby, "hobbies")
6
- ('a'..'l').each do |letter|
6
+ ('a'..'l').each_with_index do |letter, idx|
7
7
  Hobby.create(:name => letter)
8
+ sleep(1) if idx % 5 == 4 # Performance hack
8
9
  end
9
10
  Hobby.commit_solr
11
+ Hobby.commit_solr
10
12
  end
11
13
 
12
- describe "#find_each" do
13
- it "returns each record one at a time" do
14
- missed_hobbies = ('a'..'l').to_a
15
- @relation.find_each(:batch_size => 5) do |hobby|
16
- missed_hobbies.delete_if {|h| h == hobby.name}
14
+ ['cassandra', 'solr'].each do |method|
15
+ describe "#find_each" do
16
+ it "returns each record one at a time with #{method}" do
17
+ missed_hobbies = ('a'..'l').to_a
18
+ @relation.send('with_'+method).find_each(:batch_size => 5) do |hobby|
19
+ missed_hobbies.delete_if {|h| h == hobby.name}
20
+ end
21
+ missed_hobbies.should be_empty
17
22
  end
18
- missed_hobbies.should be_empty
19
23
  end
20
- end
21
-
22
- describe "#find_in_batches" do
23
- it "returns records in batches of the given size" do
24
- count = 12
25
- @relation.find_in_batches(:batch_size => 5) do |batch|
26
- batch.size.should <= 5
27
- count -= batch.size
24
+
25
+ describe "#find_in_batches" do
26
+ it "returns records in batches of the given size with #{method}" do
27
+ count = 12
28
+ @relation.send('with_'+method).find_in_batches(:batch_size => 5) do |batch|
29
+ batch.size.should <= 5
30
+ count -= batch.size
31
+ end
32
+ count.should == 0
28
33
  end
29
- count.should == 0
30
34
  end
31
35
  end
32
36
  end
@@ -16,7 +16,7 @@ describe DatastaxRails::Relation do
16
16
  it "should look up the first result if records are not already loaded" do
17
17
  a_record = mock_model(Hobby)
18
18
  @relation.stub(:loaded? => false)
19
- mock_relation = mock(DatastaxRails::Relation, :to_a => [a_record])
19
+ mock_relation = double(DatastaxRails::Relation, :to_a => [a_record])
20
20
  @relation.should_receive(:limit).with(1).and_return(mock_relation)
21
21
  @relation.first.should == a_record
22
22
  end
@@ -39,7 +39,7 @@ describe DatastaxRails::Relation do
39
39
  it "should look up the last result if records are not already loaded" do
40
40
  a_record = mock_model(Hobby)
41
41
  @relation.stub(:loaded? => false)
42
- mock_relation = mock(DatastaxRails::Relation, :to_a => [a_record])
42
+ mock_relation = double(DatastaxRails::Relation, :to_a => [a_record])
43
43
  @relation.should_receive(:reverse_order).and_return(mock_relation)
44
44
  mock_relation.should_receive(:limit).with(1).and_return(mock_relation)
45
45
  @relation.last.should == a_record