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,162 @@
1
+ require 'digest/sha1'
2
+
3
+ module DatastaxRails
4
+ module Tasks
5
+ class SchemaMigration
6
+ def self.column_family
7
+ 'schema_migrations'
8
+ end
9
+ end
10
+
11
+ class ColumnFamily
12
+ COMPARATOR_TYPES = [:blob, :ascii, :text, :varint, :bigint, :uuid, :timestamp, :boolean, :float, :doublt, :decimal]
13
+
14
+ COLUMN_TYPES = [:standard, :super]
15
+
16
+ def initialize(keyspace)
17
+ @keyspace = keyspace
18
+ end
19
+
20
+ def exists?(name)
21
+ connection.schema.column_family_names.include?(name)
22
+ end
23
+
24
+ def create(name, &block)
25
+ cql = DatastaxRails::Cql::CreateColumnFamily.new(name.to_s)
26
+ cql.comparator_type = 'text'
27
+ cql.column_type = 'Standard'
28
+
29
+ block.call cf if block
30
+
31
+ connection.execute_cql_query(cql.to_cql)
32
+ end
33
+
34
+ def drop(name)
35
+ connection.execute_cql_query(DatastaxRails::Cql::DropColumnFamily.new(name.to_s).to_cql)
36
+ end
37
+
38
+ def rename(old_name, new_name)
39
+ raise NotImplementedError, "Renaming of column families is not currently supported"
40
+ end
41
+
42
+ def clear(name)
43
+ connection.execute_cql_query(DatastaxRails::Cql::Truncate.new(name.to_s).to_cql)
44
+ end
45
+
46
+ def generate_solr_schema(model)
47
+ @fields = []
48
+ @copy_fields = []
49
+ @fulltext_fields = []
50
+ model.attribute_definitions.values.each do |attr|
51
+ coder = attr.coder
52
+ if coder.options[:solr_type] && (coder.options[:indexed] || coder.options[:stored])
53
+ @fields.push({ :name => attr.name,
54
+ :type => coder.options[:solr_type].to_s,
55
+ :indexed => coder.options[:indexed].to_s,
56
+ :stored => coder.options[:stored].to_s,
57
+ :multi_valued => coder.options[:multi_valued].to_s })
58
+ end
59
+ if coder.options[:sortable] && coder.options[:tokenized]
60
+ @fields.push({ :name => "sort_" + attr.name,
61
+ :type => "string",
62
+ :indexed => true,
63
+ :stored => false,
64
+ :multi_valued => false })
65
+ @copy_fields.push({ :source => attr.name, :dest => "sort_" + attr.name })
66
+ end
67
+ if coder.options[:fulltext]
68
+ @fulltext_fields << attr.name
69
+ end
70
+ end
71
+ # Sort the fields so that no matter what order the attributes are arranged into the
72
+ # same schema file gets generated
73
+ @fields.sort! {|a,b| a[:name] <=> b[:name]}
74
+ @copy_fields.sort! {|a,b| a[:source] <=> b[:source]}
75
+ @fulltext_fields.sort!
76
+
77
+ return ERB.new(File.read(File.join(File.dirname(__FILE__),"..","..","..","config","schema.xml.erb"))).result(binding)
78
+ end
79
+
80
+ def upload_solr_schemas(column_family = :all)
81
+ # Ensure schema migrations CF exists
82
+ unless connection.schema.column_families['schema_migrations']
83
+ connection.execute_cql_query(DatastaxRails::Cql::CreateColumnFamily.new('schema_migrations').key_type(:text).to_cql)
84
+ end
85
+
86
+ solrconfig = File.read(File.join(File.dirname(__FILE__),"..","..","..","config","solrconfig.xml"))
87
+ stopwords = File.read(File.join(File.dirname(__FILE__),"..","..","..","config","stopwords.txt"))
88
+ solrconfig_digest = Digest::SHA1.hexdigest(solrconfig)
89
+ stopwords_digest = Digest::SHA1.hexdigest(stopwords)
90
+
91
+ models_to_upload = []
92
+
93
+ if column_family == :all
94
+ # Ensure all models are loaded
95
+ Dir[Rails.root.join("app","models",'*.rb').to_s].each do |file|
96
+ require File.basename(file, File.extname(file))
97
+ end
98
+
99
+ models_to_upload += DatastaxRails::Base.models
100
+ else
101
+ models_to_upload << column_family.constantize
102
+ end
103
+
104
+ puts "models: #{models_to_upload.collect(&:to_s).join(",")}"
105
+
106
+ models_to_upload.each do |model|
107
+ schema = generate_solr_schema(model)
108
+ schema_digest = Digest::SHA1.hexdigest(schema)
109
+
110
+ results = DatastaxRails::Cql::Select.new(SchemaMigration, ['*']).conditions(:key => model.column_family).execute
111
+ sm_digests = CassandraCQL::Result.new(results).fetch.to_hash
112
+
113
+ solr_url = "#{DatastaxRails::Base.config[:solr][:url]}/resource/#{DatastaxRails::Base.config[:keyspace]}.#{model.column_family}"
114
+ uri = URI.parse(solr_url)
115
+ Net::HTTP.start(uri.host, uri.port) do |http|
116
+ if solrconfig_digest != sm_digests['solrconfig']
117
+ puts "Posting Solr Config file to '#{uri.path}/solrconfig.xml'"
118
+ http.post(uri.path+"/solrconfig.xml", solrconfig)
119
+ DatastaxRails::Cql::Update.new(SchemaMigration, model.column_family).columns(:solrconfig => solrconfig_digest).execute
120
+ end
121
+ if stopwords_digest != sm_digests['stopwords']
122
+ puts "Posting Solr Stopwords file to '#{uri.path}/stopwords.txt'"
123
+ http.post(uri.path+"/stopwords.txt", stopwords)
124
+ DatastaxRails::Cql::Update.new(SchemaMigration, model.column_family).columns(:stopwords => stopwords_digest).execute
125
+ end
126
+ if schema_digest != sm_digests['digest']
127
+ puts "Posting Solr Schema file to '#{uri.path}/schema.xml'"
128
+ http.post(uri.path+"/schema.xml", schema)
129
+ DatastaxRails::Cql::Update.new(SchemaMigration, model.column_family).columns(:digest => schema_digest).execute
130
+ end
131
+ end
132
+
133
+ end
134
+ end
135
+
136
+ private
137
+
138
+ def connection
139
+ DatastaxRails::Base.connection
140
+ end
141
+
142
+ def post_process_column_family(cf)
143
+ comp_type = cf.comparator_type
144
+ if comp_type && COMPARATOR_TYPES.has_key?(comp_type)
145
+ cf.comparator_type = COMPARATOR_TYPES[comp_type]
146
+ end
147
+
148
+ comp_type = cf.subcomparator_type
149
+ if comp_type && COMPARATOR_TYPES.has_key?(comp_type)
150
+ cf.subcomparator_type = COMPARATOR_TYPES[comp_type]
151
+ end
152
+
153
+ col_type = cf.column_type
154
+ if col_type && COLUMN_TYPES.has_key?(col_type)
155
+ cf.column_type = COLUMN_TYPES[col_type]
156
+ end
157
+
158
+ cf
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,63 @@
1
+ namespace :ds do
2
+ task :configure => :environment do
3
+ @configs = YAML.load_file(Rails.root.join("config", "datastax.yml"))
4
+ @config = @configs[Rails.env || 'development']
5
+ end
6
+
7
+ desc 'Create the keyspace in config/cassandra.yml for the current environment'
8
+ task :create do
9
+ @configs = YAML.load_file(Rails.root.join("config", "datastax.yml"))
10
+ @config = @configs[Rails.env || 'development']
11
+ ret = DatastaxRails::Tasks::Keyspace.create @config['keyspace'], @config
12
+ puts "Created keyspace: #{@config['keyspace']}" if ret
13
+ end
14
+
15
+ namespace :create do
16
+ desc 'Create keyspaces in config/cassandra.yml for all environments'
17
+ task :all => :configure do
18
+ created = []
19
+ @configs.values.each do |config|
20
+ DatastaxRails::Tasks::Keyspace.create config['keyspace'], config
21
+ created << config['keyspace']
22
+ end
23
+ puts "Created keyspaces: #{created.join(', ')}"
24
+ end
25
+ end
26
+
27
+ desc 'Drop keyspace in config/cassandra.yml for the current environment'
28
+ task :drop => :configure do
29
+ DatastaxRails::Tasks::Keyspace.drop @config['keyspace']
30
+ puts "Dropped keyspace: #{@config['keyspace']}"
31
+ end
32
+
33
+ namespace :drop do
34
+ desc 'Drop keyspaces in config/cassandra.yml for all environments'
35
+ task :all => :configure do
36
+ dropped = []
37
+ @configs.values.each do |config|
38
+ DatastaxRails::Tasks::Keyspace.drop config['keyspace']
39
+ dropped << config['keyspace']
40
+ end
41
+ puts "Dropped keyspaces: #{dropped.join(', ')}"
42
+ end
43
+ end
44
+
45
+ task :schema => :configure do
46
+ cf = DatastaxRails::Tasks::ColumnFamily.new(@config['keyspace'])
47
+ cf.upload_solr_schemas
48
+ end
49
+
50
+ desc 'Migrate the keyspace (options: VERSION=x)'
51
+ task :migrate => :configure do
52
+ version = ( ENV['VERSION'] ? ENV['VERSION'].to_i : nil )
53
+ DatastaxRails::Schema::Migrator.migrate DatastaxRails::Schema::Migrator.migrations_path, version
54
+ schema_dump
55
+ end
56
+
57
+ desc 'Load the seed data from ks/seeds.rb'
58
+ task :seed => :environment do
59
+ seed_file = Rails.root.join("ks","seeds.rb")
60
+ load(seed_file) if seed_file.exist?
61
+ end
62
+ end
63
+
@@ -0,0 +1,57 @@
1
+ module DatastaxRails
2
+ module Tasks
3
+ class Keyspace
4
+ class << self
5
+ def exists?(name)
6
+ connection.keyspaces.collect(&:name).include? name.to_s
7
+ end
8
+
9
+ def create(name, options = {})
10
+ opts = { :name => name.to_s,
11
+ :strategy_class => 'org.apache.cassandra.locator.NetworkTopologyStrategy'}.merge(options)
12
+
13
+ if(exists?(name.to_s))
14
+ puts "Keyspace #{name.to_s} already exists"
15
+ return false
16
+ else
17
+ cql = DatastaxRails::Cql::CreateKeyspace.new(opts.delete(:name))
18
+ cql.strategy_class(opts.delete(:strategy_class))
19
+ strategy_options = opts.delete('strategy_options')
20
+ cql.strategy_options(strategy_options.symbolize_keys)
21
+ puts cql.to_cql
22
+ connection.execute_cql_query(cql.to_cql)
23
+ return true
24
+ end
25
+ end
26
+
27
+ def drop(name)
28
+ connection.execute_cql_query(DatastaxRails::Cql::DropKeyspace.new(name.to_s).to_cql)
29
+ end
30
+
31
+ def set(name)
32
+ connection.execute_cql_query(DatastaxRails::Cql::UseKeyspace.new(name.to_s).to_cql)
33
+ end
34
+
35
+ def get
36
+ connection.keyspace
37
+ end
38
+
39
+ def clear
40
+ return puts 'Cannot clear system keyspace' if connection.keyspace == 'system'
41
+
42
+ connection.clear_keyspace!
43
+ end
44
+
45
+ private
46
+
47
+ def connection
48
+ unless @connection
49
+ config = YAML.load_file(Rails.root.join("config", "datastax.yml"))[Rails.env]
50
+ @connection = CassandraCQL::Database.new(config["servers"], :keyspace => 'system')
51
+ end
52
+ @connection
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,19 @@
1
+ module DatastaxRails
2
+ module Timestamps
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ # attribute :created_at, :type => :time#_with_zone
7
+ # attribute :updated_at, :type => :time#_with_zone
8
+
9
+ before_create do #|r|
10
+ self.created_at ||= Time.current
11
+ self.updated_at ||= Time.current
12
+ end
13
+
14
+ before_update :if => :changed? do #|r|
15
+ self.updated_at = Time.current
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+ module DatastaxRails
2
+ class Type
3
+ cattr_accessor :attribute_types
4
+ self.attribute_types = {}.with_indifferent_access
5
+
6
+ class << self
7
+ def register(name, coder)
8
+ attribute_types[name] = coder
9
+ end
10
+
11
+ def get_coder(name)
12
+ attribute_types[name]
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,77 @@
1
+ module DatastaxRails
2
+ module Types
3
+ class ArrayType < BaseType
4
+ DEFAULTS = {:solr_type => 'array', :indexed => true, :stored => true, :multi_valued => false, :sortable => false, :tokenized => true, :fulltext => false}
5
+ class DirtyArray < Array
6
+ attr_accessor :record, :name, :options
7
+ def initialize(record, name, array, options)
8
+ @record = record
9
+ @name = name.to_s
10
+ @options = options
11
+
12
+ super(array)
13
+ setify!
14
+ end
15
+
16
+ def <<(obj)
17
+ modifying do
18
+ super
19
+ setify!
20
+ end
21
+ end
22
+
23
+ def delete(obj)
24
+ modifying do
25
+ super
26
+ end
27
+ end
28
+
29
+ private
30
+ def setify!
31
+ if options[:unique]
32
+ compact!
33
+ uniq!
34
+ begin sort! rescue ArgumentError end
35
+ end
36
+ end
37
+
38
+ def modifying
39
+ unless record.changed_attributes.include?(name)
40
+ original = dup
41
+ end
42
+
43
+ result = yield
44
+
45
+ if !record.changed_attributes.key?(name) && original != self
46
+ record.changed_attributes[name] = original
47
+ end
48
+
49
+ record.send("#{name}=", self)
50
+
51
+ result
52
+ end
53
+ end
54
+
55
+ def default
56
+ []
57
+ end
58
+
59
+ def encode(array)
60
+ raise ArgumentError.new("#{self} requires an Array") unless array.kind_of?(Array)
61
+ ar = Array(array)
62
+ ar.uniq! if options[:unique]
63
+ ar.join("&&&&")
64
+ end
65
+
66
+ def decode(str)
67
+ return [] if str.blank?
68
+
69
+ str.split(/&&&&/)
70
+ end
71
+
72
+ def wrap(record, name, value)
73
+ DirtyArray.new(record, name, value, options)
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,26 @@
1
+ module DatastaxRails
2
+ module Types
3
+ class BaseType
4
+ attr_accessor :options
5
+ def initialize(options = {})
6
+ @options = self.class::DEFAULTS.merge(options)
7
+ end
8
+
9
+ def default
10
+ options[:default].dup if options[:default]
11
+ end
12
+
13
+ def encode(value)
14
+ value.to_s
15
+ end
16
+
17
+ def decode(str)
18
+ str
19
+ end
20
+
21
+ def wrap(record, name, value)
22
+ value
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ module DatastaxRails
2
+ module Types
3
+ class BinaryType < BaseType
4
+ DEFAULTS = {:solr_type => false, :indexed => false, :stored => false, :multi_valued => false, :sortable => false, :tokenized => false, :fulltext => false}
5
+ def encode(str)
6
+ raise ArgumentError.new("#{self} requires a String") unless str.kind_of?(String)
7
+ str.dup
8
+ end
9
+
10
+ def wrap(record, name, value)
11
+ (value.frozen? ? value.dup : value)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ module DatastaxRails
2
+ module Types
3
+ class BooleanType < BaseType
4
+ DEFAULTS = {:solr_type => 'boolean', :indexed => true, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false}
5
+ TRUE_VALS = [true, 'true', '1', 'Y']
6
+ FALSE_VALS = [false, 'false', '0', '', 'N', nil]
7
+ VALID_VALS = TRUE_VALS + FALSE_VALS
8
+
9
+ def encode(bool)
10
+ unless VALID_VALS.include?(bool)
11
+ raise ArgumentError.new("#{self} requires a boolean")
12
+ end
13
+ TRUE_VALS.include?(bool) ? '1' : '0'
14
+ end
15
+
16
+ def decode(str)
17
+ raise ArgumentError.new("Cannot convert #{str} into a boolean") unless VALID_VALS.include?(str)
18
+ TRUE_VALS.include?(str)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ module DatastaxRails
2
+ module Types
3
+ class DateType < BaseType
4
+ DEFAULTS = {:solr_type => 'date', :indexed => true, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false}
5
+ FORMAT = '%Y-%m-%dT%H:%M:%SZ'
6
+
7
+ def encode(value)
8
+ raise ArgumentError.new("#{self} requires a Date") unless value.to_date
9
+ value.to_date.strftime(FORMAT)
10
+ end
11
+
12
+ def decode(str)
13
+ Date.parse(str) rescue nil
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ module DatastaxRails
2
+ module Types
3
+ class FloatType < BaseType
4
+ DEFAULTS = {:solr_type => 'float', :indexed => true, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false}
5
+ REGEX = /\A[-+]?(\d+(\.\d+)?|\.\d+)\Z/
6
+ def encode(float)
7
+ raise ArgumentError.new("#{self} requires a Float") unless float.kind_of?(Float)
8
+ float.to_s
9
+ end
10
+
11
+ def decode(str)
12
+ return nil if str.empty?
13
+ return nil unless str.kind_of?(String) && str.match(REGEX)
14
+ str.to_f
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module DatastaxRails
2
+ module Types
3
+ class IntegerType < BaseType
4
+ DEFAULTS = {:solr_type => 'int', :indexed => true, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false}
5
+ REGEX = /\A[-+]?\d+\Z/
6
+ def encode(int)
7
+ raise ArgumentError.new("#{self} requires an Integer. You passed #{int.inspect}") unless int.kind_of?(Integer)
8
+ int.to_s
9
+ end
10
+
11
+ def decode(str)
12
+ return nil if str.empty?
13
+ return nil unless str.kind_of?(String) && str.match(REGEX)
14
+ str.to_i
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ module DatastaxRails
2
+ module Types
3
+ class StringType < BaseType
4
+ DEFAULTS = {:solr_type => 'string', :indexed => true, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => true}
5
+ def encode(str)
6
+ raise ArgumentError.new("#{self} requires a String") unless str.kind_of?(String)
7
+ str.dup
8
+ end
9
+
10
+ def wrap(record, name, value)
11
+ txt = (value.frozen? ? value.dup : value)
12
+ txt.respond_to?(:force_encoding) ? txt.force_encoding('UTF-8') : txt
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module DatastaxRails
2
+ module Types
3
+ class TextType < BaseType
4
+ DEFAULTS = {:solr_type => 'text', :indexed => true, :stored => true, :multi_valued => false, :sortable => false, :tokenized => true, :fulltext => true}
5
+ def encode(str)
6
+ raise ArgumentError.new("#{self} requires a String") unless str.kind_of?(String)
7
+ str.dup
8
+ end
9
+
10
+ def wrap(record, name, value)
11
+ txt = (value.frozen? ? value.dup : value)
12
+ txt.respond_to?(:force_encoding) ? txt.force_encoding('UTF-8') : txt
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ module DatastaxRails
2
+ module Types
3
+ class TimeType < BaseType
4
+ DEFAULTS = {:solr_type => 'date', :indexed => true, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false}
5
+ FORMAT = "%Y-%m-%dT%H:%M:%SZ"
6
+
7
+ def encode(time)
8
+ raise ArgumentError.new("#{self} requires a Time") unless time.kind_of?(Time)
9
+ time.strftime(FORMAT)
10
+ end
11
+
12
+ def decode(str)
13
+ Time.parse(str) rescue nil
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ DatastaxRails::Type.register(:array, DatastaxRails::Types::ArrayType)
2
+ DatastaxRails::Type.register(:binary, DatastaxRails::Types::BinaryType)
3
+ DatastaxRails::Type.register(:boolean, DatastaxRails::Types::BooleanType)
4
+ DatastaxRails::Type.register(:date, DatastaxRails::Types::DateType)
5
+ DatastaxRails::Type.register(:float, DatastaxRails::Types::FloatType)
6
+ DatastaxRails::Type.register(:integer, DatastaxRails::Types::IntegerType)
7
+ DatastaxRails::Type.register(:time, DatastaxRails::Types::TimeType)
8
+ DatastaxRails::Type.register(:string, DatastaxRails::Types::StringType)
9
+ DatastaxRails::Type.register(:text, DatastaxRails::Types::TextType)