datastax_rails 1.0.19.0 → 1.1.0.3

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 (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