datastax_rails 1.0.16.3 → 1.0.17.1
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/lib/datastax_rails/attribute_methods.rb +5 -1
- data/lib/datastax_rails/base.rb +4 -0
- data/lib/datastax_rails/collection.rb +10 -1
- data/lib/datastax_rails/cql/alter_column_family.rb +48 -0
- data/lib/datastax_rails/cql/base.rb +1 -0
- data/lib/datastax_rails/cql/create_column_family.rb +17 -15
- data/lib/datastax_rails/cql/delete.rb +20 -3
- data/lib/datastax_rails/cql/select.rb +13 -3
- data/lib/datastax_rails/cql/update.rb +7 -10
- data/lib/datastax_rails/cql.rb +1 -0
- data/lib/datastax_rails/identity/natural_key_factory.rb +1 -0
- data/lib/datastax_rails/payload_model.rb +89 -0
- data/lib/datastax_rails/persistence.rb +7 -30
- data/lib/datastax_rails/relation/finder_methods.rb +2 -2
- data/lib/datastax_rails/relation.rb +2 -0
- data/lib/datastax_rails/tasks/column_family.rb +83 -55
- data/lib/datastax_rails/timestamps.rb +9 -3
- data/lib/datastax_rails/types/array_type.rb +1 -1
- data/lib/datastax_rails/types/binary_type.rb +3 -19
- data/lib/datastax_rails/version.rb +1 -1
- data/lib/datastax_rails.rb +7 -1
- data/spec/datastax_rails/base_spec.rb +1 -3
- data/spec/datastax_rails/cql/select_spec.rb +2 -2
- data/spec/datastax_rails/cql/update_spec.rb +1 -1
- data/spec/datastax_rails/persistence_spec.rb +23 -3
- data/spec/dummy/config/datastax.yml +3 -11
- data/spec/dummy/log/test.log +1636 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/support/models.rb +6 -2
- metadata +6 -4
@@ -44,11 +44,11 @@ module DatastaxRails
|
|
44
44
|
model.attribute_definitions.values.each do |attr|
|
45
45
|
coder = attr.coder
|
46
46
|
if coder.options[:solr_type] && (coder.options[:indexed] || coder.options[:stored])
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
@fields.push({ :name => attr.name,
|
48
|
+
:type => coder.options[:solr_type].to_s,
|
49
|
+
:indexed => coder.options[:indexed].to_s,
|
50
|
+
:stored => coder.options[:stored].to_s,
|
51
|
+
:multi_valued => coder.options[:multi_valued].to_s })
|
52
52
|
end
|
53
53
|
if coder.options[:sortable] && coder.options[:tokenized]
|
54
54
|
@fields.push({ :name => "sort_" + attr.name,
|
@@ -76,7 +76,7 @@ module DatastaxRails
|
|
76
76
|
column_family ||= :all
|
77
77
|
# Ensure schema migrations CF exists
|
78
78
|
unless connection.schema.column_families['schema_migrations']
|
79
|
-
connection.execute_cql_query(DatastaxRails::Cql::CreateColumnFamily.new('schema_migrations').key_type(:text).to_cql)
|
79
|
+
connection.execute_cql_query(DatastaxRails::Cql::CreateColumnFamily.new('schema_migrations').key_type(:text).columns(:digest => :text, :solrconfig => :text, :stopwords => :text).to_cql)
|
80
80
|
end
|
81
81
|
|
82
82
|
solrconfig = File.read(File.join(File.dirname(__FILE__),"..","..","..","config","solrconfig.xml"))
|
@@ -100,61 +100,89 @@ module DatastaxRails
|
|
100
100
|
puts "models: #{models_to_upload.collect(&:to_s).join(",")}"
|
101
101
|
|
102
102
|
models_to_upload.each do |model|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
103
|
+
if model.payload_model?
|
104
|
+
next if model == DatastaxRails::PayloadModel
|
105
|
+
unless connection.schema.column_families[model.column_family.to_s]
|
106
|
+
puts "Creating payload model #{model.column_family}"
|
107
|
+
columns = {:chunk => :int, :payload => :text}
|
108
|
+
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
|
109
|
+
puts cql
|
110
|
+
connection.execute_cql_query(cql)
|
111
|
+
end
|
112
|
+
else
|
113
|
+
unless connection.schema.column_families[model.column_family.to_s]
|
114
|
+
puts "Creating normal model #{model.column_family}"
|
115
|
+
cql = DatastaxRails::Cql::CreateColumnFamily.new(model.column_family).key_type(:text).columns(:updated_at => :text, :created_at => :text).to_cql
|
116
|
+
puts cql
|
117
|
+
connection.execute_cql_query(cql)
|
118
|
+
end
|
119
|
+
schema = generate_solr_schema(model)
|
120
|
+
schema_digest = Digest::SHA1.hexdigest(schema)
|
121
|
+
|
122
|
+
results = DatastaxRails::Cql::Select.new(SchemaMigration, ['*']).conditions(:KEY => model.column_family).execute
|
123
|
+
sm_digests = CassandraCQL::Result.new(results).fetch.try(:to_hash) || {}
|
124
|
+
|
125
|
+
solr_url = "#{DatastaxRails::Base.solr_base_url}/resource/#{DatastaxRails::Base.config[:keyspace]}.#{model.column_family}"
|
126
|
+
uri = URI.parse(solr_url)
|
127
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
128
|
+
if uri.scheme == 'https'
|
129
|
+
http.use_ssl = true
|
130
|
+
http.cert = OpenSSL::X509::Certificate.new(Rails.root.join("config","datastax_rails.crt").read)
|
131
|
+
http.key = OpenSSL::PKey::RSA.new(Rails.root.join("config","datastax_rails.key").read)
|
132
|
+
http.ca_path = Rails.root.join("config","sade_ca.crt").to_s
|
133
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
134
|
+
end
|
135
|
+
http.read_timeout = 300
|
136
|
+
if force || solrconfig_digest != sm_digests['solrconfig']
|
137
|
+
loop do
|
138
|
+
puts "Posting Solr Config file to '#{solr_url}/solrconfig.xml'"
|
139
|
+
http.post(uri.path+"/solrconfig.xml", solrconfig)
|
140
|
+
if Rails.env.production?
|
141
|
+
sleep(5)
|
142
|
+
resp = http.get(uri.path+"/solrconfig.xml")
|
143
|
+
continue unless resp.message == 'OK'
|
144
|
+
end
|
145
|
+
break
|
128
146
|
end
|
129
|
-
|
147
|
+
DatastaxRails::Cql::Update.new(SchemaMigration, model.column_family).columns(:solrconfig => solrconfig_digest).execute
|
130
148
|
end
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
149
|
+
if force || stopwords_digest != sm_digests['stopwords']
|
150
|
+
loop do
|
151
|
+
puts "Posting Solr Stopwords file to '#{solr_url}/stopwords.txt'"
|
152
|
+
http.post(uri.path+"/stopwords.txt", stopwords)
|
153
|
+
if Rails.env.production?
|
154
|
+
sleep(5)
|
155
|
+
resp = http.get(uri.path+"/stopwords.txt")
|
156
|
+
continue unless resp.message == 'OK'
|
157
|
+
end
|
158
|
+
break
|
141
159
|
end
|
142
|
-
|
160
|
+
DatastaxRails::Cql::Update.new(SchemaMigration, model.column_family).columns(:stopwords => stopwords_digest).execute
|
143
161
|
end
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
162
|
+
if force || schema_digest != sm_digests['digest']
|
163
|
+
loop do
|
164
|
+
puts "Posting Solr Schema file to '#{solr_url}/schema.xml'"
|
165
|
+
http.post(uri.path+"/schema.xml", schema)
|
166
|
+
if Rails.env.production?
|
167
|
+
sleep(5)
|
168
|
+
resp = http.get(uri.path+"/schema.xml")
|
169
|
+
continue unless resp.message == 'OK'
|
170
|
+
end
|
171
|
+
break
|
172
|
+
end
|
173
|
+
DatastaxRails::Cql::Update.new(SchemaMigration, model.column_family).columns(:digest => schema_digest).execute
|
174
|
+
end
|
175
|
+
|
176
|
+
# Check for unindexed columns
|
177
|
+
model.attribute_definitions.each do |attribute, definition|
|
178
|
+
if !connection.schema.column_families[model.column_family.to_s].columns.has_key?(attribute.to_s)# &&
|
179
|
+
#!definition.coder.options[:stored] &&
|
180
|
+
#!definition.coder.options[:indexed]
|
181
|
+
|
182
|
+
puts "Adding column '#{attribute}' to '#{model.column_family}'"
|
183
|
+
DatastaxRails::Cql::AlterColumnFamily.new(model.column_family).add(attribute => :text).execute
|
154
184
|
end
|
155
|
-
break
|
156
185
|
end
|
157
|
-
DatastaxRails::Cql::Update.new(SchemaMigration, model.column_family).columns(:digest => schema_digest).execute
|
158
186
|
end
|
159
187
|
end
|
160
188
|
end
|
@@ -7,12 +7,18 @@ module DatastaxRails
|
|
7
7
|
# attribute :updated_at, :type => :time#_with_zone
|
8
8
|
|
9
9
|
before_create do #|r|
|
10
|
-
self.created_at
|
11
|
-
|
10
|
+
if self.respond_to?(:created_at=)
|
11
|
+
self.created_at ||= Time.current
|
12
|
+
end
|
13
|
+
if self.respond_to?(:updated_at=)
|
14
|
+
self.updated_at ||= Time.current
|
15
|
+
end
|
12
16
|
end
|
13
17
|
|
14
18
|
before_update :if => :changed? do #|r|
|
15
|
-
self.updated_at
|
19
|
+
if self.respond_to?(:updated_at=)
|
20
|
+
self.updated_at = Time.current
|
21
|
+
end
|
16
22
|
end
|
17
23
|
end
|
18
24
|
end
|
@@ -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 =>
|
10
|
+
DEFAULTS = {:solr_type => 'array', :indexed => true, :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.
|
@@ -4,27 +4,11 @@ module DatastaxRails
|
|
4
4
|
DEFAULTS = {:solr_type => false, :indexed => false, :stored => false, :multi_valued => false, :sortable => false, :tokenized => false, :fulltext => false}
|
5
5
|
def encode(str)
|
6
6
|
raise ArgumentError.new("#{self} requires a String") unless str.kind_of?(String)
|
7
|
-
|
8
|
-
#io = StringIO.new(str)
|
9
|
-
chunks = []
|
10
|
-
while chunk = io.read(1.megabyte)
|
11
|
-
chunks << chunk
|
12
|
-
end
|
13
|
-
chunks
|
7
|
+
Base64.encode64(str)
|
14
8
|
end
|
15
9
|
|
16
|
-
def decode(
|
17
|
-
|
18
|
-
io = StringIO.new("","w+")
|
19
|
-
arr.each do |chunk|
|
20
|
-
io.write(chunk)
|
21
|
-
end
|
22
|
-
io.rewind
|
23
|
-
Base64.decode64(io.read)
|
24
|
-
#io.read
|
25
|
-
else
|
26
|
-
arr
|
27
|
-
end
|
10
|
+
def decode(str)
|
11
|
+
Base64.decode64(str)
|
28
12
|
end
|
29
13
|
|
30
14
|
def wrap(record, name, value)
|
data/lib/datastax_rails.rb
CHANGED
@@ -18,6 +18,7 @@ module DatastaxRails
|
|
18
18
|
autoload :GroupedCollection
|
19
19
|
autoload :Identity
|
20
20
|
autoload :Migrations
|
21
|
+
autoload :PayloadModel
|
21
22
|
autoload :Persistence
|
22
23
|
autoload :Reflection
|
23
24
|
autoload :Relation
|
@@ -77,7 +78,12 @@ require "thrift"
|
|
77
78
|
module Thrift
|
78
79
|
class BinaryProtocol
|
79
80
|
def write_string(str)
|
80
|
-
|
81
|
+
if(str.respond_to?(:bytesize))
|
82
|
+
size = str.bytesize
|
83
|
+
else
|
84
|
+
size = str.size
|
85
|
+
end
|
86
|
+
write_i32(size)
|
81
87
|
trans.write(str)
|
82
88
|
end
|
83
89
|
end
|
@@ -14,8 +14,6 @@ describe DatastaxRails::Base do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
it "should raise RecordNotFound when finding a bogus ID" do
|
17
|
-
|
18
|
-
lambda { Person.find("xyzzy") }.should raise_exception(DatastaxRails::RecordNotFound)
|
19
|
-
end
|
17
|
+
lambda { Person.find("xyzzy") }.should raise_exception(DatastaxRails::RecordNotFound)
|
20
18
|
end
|
21
19
|
end
|
@@ -7,8 +7,8 @@ describe DatastaxRails::Cql::Select do
|
|
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(:
|
11
|
-
cql.to_cql.should == "SELECT * FROM users USING CONSISTENCY QUORUM WHERE
|
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 "
|
12
12
|
end
|
13
13
|
|
14
14
|
it_has_behavior "default_consistency"
|
@@ -8,7 +8,7 @@ describe DatastaxRails::Cql::Update do
|
|
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 using consistency QUORUM 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"
|
@@ -4,7 +4,6 @@ describe "DatastaxRails::Base" do
|
|
4
4
|
describe "persistence" do
|
5
5
|
describe "#create" do
|
6
6
|
it "should persist at the given consistency level" do
|
7
|
-
DatastaxRails::Base.connection.stub(:execute_cql_query)
|
8
7
|
DatastaxRails::Base.connection.should_receive(:execute_cql_query).with(/USING CONSISTENCY LOCAL_QUORUM/i).and_return(true)
|
9
8
|
Person.create({:name => 'Steven'},{:consistency => 'LOCAL_QUORUM'})
|
10
9
|
end
|
@@ -12,7 +11,6 @@ describe "DatastaxRails::Base" do
|
|
12
11
|
|
13
12
|
describe "#save" do
|
14
13
|
it "should persist at the given consistency level" do
|
15
|
-
DatastaxRails::Base.connection.stub(:execute_cql_query)
|
16
14
|
DatastaxRails::Base.connection.should_receive(:execute_cql_query).with(/USING CONSISTENCY LOCAL_QUORUM/i).and_return(true)
|
17
15
|
p=Person.new(:name => 'Steven')
|
18
16
|
p.save(:consistency => 'LOCAL_QUORUM')
|
@@ -22,10 +20,32 @@ describe "DatastaxRails::Base" do
|
|
22
20
|
describe "#remove" do
|
23
21
|
it "should remove at the given consistency level" do
|
24
22
|
p=Person.create(:name => 'Steven')
|
25
|
-
DatastaxRails::Base.connection.stub(:execute_cql_query)
|
26
23
|
DatastaxRails::Base.connection.should_receive(:execute_cql_query).with(/USING CONSISTENCY LOCAL_QUORUM/i).and_return(true)
|
27
24
|
p.destroy(:consistency => :local_quorum)
|
28
25
|
end
|
29
26
|
end
|
27
|
+
|
28
|
+
describe "#store_file" do
|
29
|
+
it "should store a file" do
|
30
|
+
file = "abcd"*1.megabyte
|
31
|
+
CarPayload.create(:digest => 'limo', :payload => file)
|
32
|
+
CarPayload.find('limo').payload.should == file
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should store really large files" do
|
36
|
+
file = IO.read("/dev/urandom", 25.megabyte)
|
37
|
+
CarPayload.create(:digest => 'limo', :payload => file)
|
38
|
+
CarPayload.find('limo').payload.should == file
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should successfully overwrite a larger file with a smaller one" do
|
42
|
+
file = "abcd"*1.megabyte
|
43
|
+
car = CarPayload.create(:digest => 'limo', :payload => file)
|
44
|
+
smallfile = "e"*1.kilobyte
|
45
|
+
car.payload = smallfile
|
46
|
+
car.save
|
47
|
+
CarPayload.find('limo').payload.should == smallfile
|
48
|
+
end
|
49
|
+
end
|
30
50
|
end
|
31
51
|
end
|
@@ -6,12 +6,8 @@ development:
|
|
6
6
|
connection_options:
|
7
7
|
timeout: 10
|
8
8
|
solr:
|
9
|
-
port:
|
9
|
+
port: 8983
|
10
10
|
path: /solr
|
11
|
-
ssl:
|
12
|
-
use_ssl: true
|
13
|
-
cert: config/datastax_rails.crt
|
14
|
-
key: config/datastax_rails.key
|
15
11
|
|
16
12
|
test:
|
17
13
|
servers: ["127.0.0.1:9160"]
|
@@ -19,11 +15,7 @@ test:
|
|
19
15
|
strategy_class: "org.apache.cassandra.locator.SimpleStrategy"
|
20
16
|
strategy_options: {"replication_factor": "1"}
|
21
17
|
connection_options:
|
22
|
-
timeout:
|
18
|
+
timeout: 30
|
23
19
|
solr:
|
24
|
-
port:
|
20
|
+
port: 8983
|
25
21
|
path: /solr
|
26
|
-
ssl:
|
27
|
-
use_ssl: true
|
28
|
-
cert: config/datastax_rails.crt
|
29
|
-
key: config/datastax_rails.key
|