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.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +62 -0
- data/Rakefile +34 -0
- data/config/schema.xml +266 -0
- data/config/schema.xml.erb +70 -0
- data/config/solrconfig.xml +1564 -0
- data/config/stopwords.txt +58 -0
- data/lib/datastax_rails/associations/association.rb +224 -0
- data/lib/datastax_rails/associations/association_scope.rb +25 -0
- data/lib/datastax_rails/associations/belongs_to_association.rb +64 -0
- data/lib/datastax_rails/associations/builder/association.rb +56 -0
- data/lib/datastax_rails/associations/builder/belongs_to.rb +30 -0
- data/lib/datastax_rails/associations/builder/collection_association.rb +48 -0
- data/lib/datastax_rails/associations/builder/has_and_belongs_to_many.rb +36 -0
- data/lib/datastax_rails/associations/builder/has_many.rb +54 -0
- data/lib/datastax_rails/associations/builder/has_one.rb +52 -0
- data/lib/datastax_rails/associations/builder/singular_association.rb +56 -0
- data/lib/datastax_rails/associations/collection_association.rb +274 -0
- data/lib/datastax_rails/associations/collection_proxy.rb +118 -0
- data/lib/datastax_rails/associations/has_and_belongs_to_many_association.rb +44 -0
- data/lib/datastax_rails/associations/has_many_association.rb +58 -0
- data/lib/datastax_rails/associations/has_one_association.rb +68 -0
- data/lib/datastax_rails/associations/singular_association.rb +58 -0
- data/lib/datastax_rails/associations.rb +86 -0
- data/lib/datastax_rails/attribute_methods/definition.rb +20 -0
- data/lib/datastax_rails/attribute_methods/dirty.rb +43 -0
- data/lib/datastax_rails/attribute_methods/typecasting.rb +50 -0
- data/lib/datastax_rails/attribute_methods.rb +104 -0
- data/lib/datastax_rails/base.rb +587 -0
- data/lib/datastax_rails/batches.rb +35 -0
- data/lib/datastax_rails/callbacks.rb +37 -0
- data/lib/datastax_rails/collection.rb +9 -0
- data/lib/datastax_rails/connection.rb +21 -0
- data/lib/datastax_rails/consistency.rb +33 -0
- data/lib/datastax_rails/cql/base.rb +15 -0
- data/lib/datastax_rails/cql/column_family.rb +38 -0
- data/lib/datastax_rails/cql/consistency.rb +13 -0
- data/lib/datastax_rails/cql/create_column_family.rb +63 -0
- data/lib/datastax_rails/cql/create_keyspace.rb +30 -0
- data/lib/datastax_rails/cql/delete.rb +41 -0
- data/lib/datastax_rails/cql/drop_column_family.rb +13 -0
- data/lib/datastax_rails/cql/drop_keyspace.rb +13 -0
- data/lib/datastax_rails/cql/insert.rb +53 -0
- data/lib/datastax_rails/cql/select.rb +51 -0
- data/lib/datastax_rails/cql/truncate.rb +13 -0
- data/lib/datastax_rails/cql/update.rb +68 -0
- data/lib/datastax_rails/cql/use_keyspace.rb +13 -0
- data/lib/datastax_rails/cql.rb +25 -0
- data/lib/datastax_rails/cursor.rb +90 -0
- data/lib/datastax_rails/errors.rb +16 -0
- data/lib/datastax_rails/identity/abstract_key_factory.rb +26 -0
- data/lib/datastax_rails/identity/custom_key_factory.rb +36 -0
- data/lib/datastax_rails/identity/hashed_natural_key_factory.rb +10 -0
- data/lib/datastax_rails/identity/natural_key_factory.rb +37 -0
- data/lib/datastax_rails/identity/uuid_key_factory.rb +23 -0
- data/lib/datastax_rails/identity.rb +53 -0
- data/lib/datastax_rails/log_subscriber.rb +37 -0
- data/lib/datastax_rails/migrations/migration.rb +15 -0
- data/lib/datastax_rails/migrations.rb +36 -0
- data/lib/datastax_rails/mocking.rb +15 -0
- data/lib/datastax_rails/persistence.rb +133 -0
- data/lib/datastax_rails/railtie.rb +20 -0
- data/lib/datastax_rails/reflection.rb +472 -0
- data/lib/datastax_rails/relation/finder_methods.rb +184 -0
- data/lib/datastax_rails/relation/modification_methods.rb +80 -0
- data/lib/datastax_rails/relation/search_methods.rb +349 -0
- data/lib/datastax_rails/relation/spawn_methods.rb +107 -0
- data/lib/datastax_rails/relation.rb +393 -0
- data/lib/datastax_rails/schema/migration.rb +106 -0
- data/lib/datastax_rails/schema/migration_proxy.rb +25 -0
- data/lib/datastax_rails/schema/migrator.rb +212 -0
- data/lib/datastax_rails/schema.rb +37 -0
- data/lib/datastax_rails/scoping.rb +394 -0
- data/lib/datastax_rails/serialization.rb +6 -0
- data/lib/datastax_rails/tasks/column_family.rb +162 -0
- data/lib/datastax_rails/tasks/ds.rake +63 -0
- data/lib/datastax_rails/tasks/keyspace.rb +57 -0
- data/lib/datastax_rails/timestamps.rb +19 -0
- data/lib/datastax_rails/type.rb +16 -0
- data/lib/datastax_rails/types/array_type.rb +77 -0
- data/lib/datastax_rails/types/base_type.rb +26 -0
- data/lib/datastax_rails/types/binary_type.rb +15 -0
- data/lib/datastax_rails/types/boolean_type.rb +22 -0
- data/lib/datastax_rails/types/date_type.rb +17 -0
- data/lib/datastax_rails/types/float_type.rb +18 -0
- data/lib/datastax_rails/types/integer_type.rb +18 -0
- data/lib/datastax_rails/types/string_type.rb +16 -0
- data/lib/datastax_rails/types/text_type.rb +16 -0
- data/lib/datastax_rails/types/time_type.rb +17 -0
- data/lib/datastax_rails/types.rb +9 -0
- data/lib/datastax_rails/validations/uniqueness.rb +119 -0
- data/lib/datastax_rails/validations.rb +48 -0
- data/lib/datastax_rails/version.rb +3 -0
- data/lib/datastax_rails.rb +87 -0
- data/lib/solr_no_escape.rb +28 -0
- data/spec/datastax_rails/associations/belongs_to_association_spec.rb +7 -0
- data/spec/datastax_rails/associations/has_many_association_spec.rb +37 -0
- data/spec/datastax_rails/associations_spec.rb +22 -0
- data/spec/datastax_rails/attribute_methods_spec.rb +23 -0
- data/spec/datastax_rails/base_spec.rb +15 -0
- data/spec/datastax_rails/cql/select_spec.rb +12 -0
- data/spec/datastax_rails/cql/update_spec.rb +0 -0
- data/spec/datastax_rails/relation/finder_methods_spec.rb +54 -0
- data/spec/datastax_rails/relation/modification_methods_spec.rb +41 -0
- data/spec/datastax_rails/relation/search_methods_spec.rb +117 -0
- data/spec/datastax_rails/relation/spawn_methods_spec.rb +28 -0
- data/spec/datastax_rails/relation_spec.rb +130 -0
- data/spec/datastax_rails/validations/uniqueness_spec.rb +41 -0
- data/spec/datastax_rails_spec.rb +5 -0
- data/spec/dummy/Rakefile +8 -0
- data/spec/dummy/app/assets/javascripts/application.js +9 -0
- data/spec/dummy/app/assets/stylesheets/application.css +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config/application.rb +47 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/datastax.yml +18 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +30 -0
- data/spec/dummy/config/environments/production.rb +60 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/config/sunspot.yml +17 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/ks/migrate/20111117224534_models.rb +20 -0
- data/spec/dummy/ks/schema.json +180 -0
- data/spec/dummy/log/development.log +298 -0
- data/spec/dummy/log/production.log +0 -0
- data/spec/dummy/log/test.log +20307 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/datastax_test_hook.rb +14 -0
- data/spec/support/models.rb +72 -0
- 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)
|