sessionm-cassandra_object 4.0.1 → 4.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +9 -1
- data/Gemfile +7 -1
- data/Gemfile.lock +60 -0
- data/config/cassandra.yml +3 -23
- data/lib/cassandra_object.rb +2 -1
- data/lib/cassandra_object/adapters/cassandra_driver.rb +47 -4
- data/lib/cassandra_object/associations.rb +10 -7
- data/lib/cassandra_object/railtie.rb +1 -1
- data/sessionm-cassandra_object.gemspec +1 -1
- data/spec/cassandra_object/base_spec.rb +30 -0
- data/spec/spec_helper.rb +60 -0
- data/spec/support/cassandra.rb +30 -0
- data/spec/support/models/counter.rb +3 -0
- data/spec/support/models/issue.rb +11 -0
- metadata +13 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e1cdbe60a0e5d0445c81ca4a2cbbfe124392c1f
|
4
|
+
data.tar.gz: 8dbd7771413345f0477fdd98d8ae2198bdb6d26f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47dc210f1595a73cbd8a900021facd42bfae16e81292d2b079f9ffb053e7f6dc72ad6864ccfe832110167f566c77540c417b4e75457e48347eabacac639a7c9a
|
7
|
+
data.tar.gz: 580fc7a117c73831b5b4cc6857a714e2cffdfb89187c741f0838afea11c701eec968b4f63f17b11985e6b52a4e73b9b70a201f8d74e4d4f13ad5d8bca861c4b6
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
source 'http://gems.prod.sessionm.com'
|
2
2
|
|
3
|
-
gem '
|
3
|
+
gem 'activesupport', :require => 'active_support/all'
|
4
|
+
gem 'activerecord', :require => 'active_record'
|
4
5
|
|
5
6
|
gem 'simple_uuid', '0.2.2'
|
6
7
|
gem 'cassandra-driver', :require => 'cassandra'
|
8
|
+
|
9
|
+
group :test, :spec do
|
10
|
+
gem 'rspec'
|
11
|
+
gem "mocha"
|
12
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://gems.prod.sessionm.com/
|
3
|
+
specs:
|
4
|
+
activemodel (4.2.1)
|
5
|
+
activesupport (= 4.2.1)
|
6
|
+
builder (~> 3.1)
|
7
|
+
activerecord (4.2.1)
|
8
|
+
activemodel (= 4.2.1)
|
9
|
+
activesupport (= 4.2.1)
|
10
|
+
arel (~> 6.0)
|
11
|
+
activesupport (4.2.1)
|
12
|
+
i18n (~> 0.7)
|
13
|
+
json (~> 1.7, >= 1.7.7)
|
14
|
+
minitest (~> 5.1)
|
15
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
16
|
+
tzinfo (~> 1.1)
|
17
|
+
arel (6.0.0)
|
18
|
+
builder (3.2.2)
|
19
|
+
cassandra-driver (2.1.3)
|
20
|
+
ione (~> 1.2)
|
21
|
+
diff-lcs (1.2.5)
|
22
|
+
i18n (0.7.0)
|
23
|
+
ione (1.2.0)
|
24
|
+
json (1.8.2)
|
25
|
+
macaddr (1.6.1)
|
26
|
+
systemu (~> 2.5.0)
|
27
|
+
metaclass (0.0.4)
|
28
|
+
minitest (5.6.1)
|
29
|
+
mocha (1.1.0)
|
30
|
+
metaclass (~> 0.0.1)
|
31
|
+
rspec (3.2.0)
|
32
|
+
rspec-core (~> 3.2.0)
|
33
|
+
rspec-expectations (~> 3.2.0)
|
34
|
+
rspec-mocks (~> 3.2.0)
|
35
|
+
rspec-core (3.2.3)
|
36
|
+
rspec-support (~> 3.2.0)
|
37
|
+
rspec-expectations (3.2.1)
|
38
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
39
|
+
rspec-support (~> 3.2.0)
|
40
|
+
rspec-mocks (3.2.1)
|
41
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
42
|
+
rspec-support (~> 3.2.0)
|
43
|
+
rspec-support (3.2.2)
|
44
|
+
simple_uuid (0.2.2)
|
45
|
+
macaddr (= 1.6.1)
|
46
|
+
systemu (2.5.2)
|
47
|
+
thread_safe (0.3.5)
|
48
|
+
tzinfo (1.2.2)
|
49
|
+
thread_safe (~> 0.1)
|
50
|
+
|
51
|
+
PLATFORMS
|
52
|
+
ruby
|
53
|
+
|
54
|
+
DEPENDENCIES
|
55
|
+
activerecord
|
56
|
+
activesupport
|
57
|
+
cassandra-driver
|
58
|
+
mocha
|
59
|
+
rspec
|
60
|
+
simple_uuid (= 0.2.2)
|
data/config/cassandra.yml
CHANGED
@@ -1,26 +1,9 @@
|
|
1
|
-
development: &id001
|
2
|
-
servers: ['127.0.0.1:9160']
|
3
|
-
keyspace: greyhound_development
|
4
|
-
replication:
|
5
|
-
strategy: org.apache.cassandra.locator.SimpleStrategy
|
6
|
-
factor: 1
|
7
|
-
consistency:
|
8
|
-
read_default: quorum
|
9
|
-
write_default: quorum
|
10
|
-
column_family_defaults:
|
11
|
-
compaction_strategy: org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy
|
12
|
-
row_cache_provider: org.apache.cassandra.cache.ConcurrentLinkedHashCacheProvider
|
13
|
-
thrift:
|
14
|
-
connect_timeout: 0.5
|
15
|
-
timeout: 3
|
16
|
-
retries: 2
|
17
|
-
production: *id001
|
18
1
|
test:
|
19
|
-
servers: ['127.0.0.1
|
20
|
-
keyspace:
|
2
|
+
servers: ['127.0.0.1']
|
3
|
+
keyspace: cassandra_object_test
|
21
4
|
replication:
|
22
5
|
strategy: org.apache.cassandra.locator.SimpleStrategy
|
23
|
-
|
6
|
+
replication_factor: 1
|
24
7
|
consistency:
|
25
8
|
read_default: quorum
|
26
9
|
write_default: quorum
|
@@ -31,6 +14,3 @@ test:
|
|
31
14
|
connect_timeout: 0.5
|
32
15
|
timeout: 3
|
33
16
|
retries: 2
|
34
|
-
exception_class_overrides:
|
35
|
-
- CassandraThrift::InvalidRequestException
|
36
|
-
- CassandraThrift::NotFoundException
|
data/lib/cassandra_object.rb
CHANGED
@@ -12,7 +12,11 @@ module CassandraObject
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def client
|
15
|
-
@client ||=
|
15
|
+
@client ||= self.new_client
|
16
|
+
end
|
17
|
+
|
18
|
+
def new_client
|
19
|
+
Client.new(cluster.connect(config[:keyspace]), cluster)
|
16
20
|
end
|
17
21
|
|
18
22
|
def close
|
@@ -26,21 +30,22 @@ module CassandraObject
|
|
26
30
|
:port => config[:port] || 9042,
|
27
31
|
:connect_timeout => config[:thrift][:connect_timeout] || 10,
|
28
32
|
:timeout => config[:thrift][:timeout] || 10,
|
29
|
-
:logger => Rails.logger || Logger.new(STDOUT),
|
33
|
+
:logger => config[:logger] || (defined?(Rails) && Rails.logger) || Logger.new(STDOUT),
|
30
34
|
:consistency => (config[:consistency] || {})[:write_default].try(:to_sym) || :one,
|
31
35
|
}
|
32
36
|
end
|
33
37
|
|
34
38
|
# The client class acts like the old cassandra gem
|
35
39
|
class Client
|
36
|
-
attr_reader :session
|
40
|
+
attr_reader :session, :cluster
|
37
41
|
|
38
42
|
KEY_FIELD = 'key'
|
39
43
|
NAME_FIELD = 'column1'
|
40
44
|
VALUE_FIELD = 'value'
|
41
45
|
|
42
|
-
def initialize(session)
|
46
|
+
def initialize(session, cluster)
|
43
47
|
@session = session
|
48
|
+
@cluster = cluster
|
44
49
|
end
|
45
50
|
|
46
51
|
def close
|
@@ -106,6 +111,40 @@ module CassandraObject
|
|
106
111
|
async ? self.execute_async(query, execute_options(opts)) : self.execute(query, execute_options(opts))
|
107
112
|
end
|
108
113
|
|
114
|
+
def remove(column_family, key, *args)
|
115
|
+
opts = args.pop if args.last.is_a?(Hash)
|
116
|
+
async = opts.try(:[], :async)
|
117
|
+
key = "textAsBlob('#{key}')"
|
118
|
+
|
119
|
+
query =
|
120
|
+
if args.first.nil? || args.first.is_a?(Hash)
|
121
|
+
"DELETE FROM \"#{column_family}\" WHERE #{KEY_FIELD} = #{key};"
|
122
|
+
else
|
123
|
+
"DELETE \"#{column_family}\" WHERE #{KEY_FIELD} = #{key} AND #{NAME_FIELD} = '#{args.first}';"
|
124
|
+
end
|
125
|
+
|
126
|
+
async ? self.execute_async(query, execute_options(opts)) : self.execute(query, execute_options(opts))
|
127
|
+
end
|
128
|
+
|
129
|
+
def get_range(column_family, opts={}, &blk)
|
130
|
+
key_count = opts[:key_count] || 100
|
131
|
+
query = "SELECT #{KEY_FIELD} FROM \"#{column_family}\" LIMIT #{key_count}"
|
132
|
+
keys = self.execute(query, execute_options(opts)).map { |result| result[KEY_FIELD] }
|
133
|
+
keys.size > 0 ? multi_get(column_family, keys) : {}
|
134
|
+
end
|
135
|
+
|
136
|
+
def multi_get(column_family, keys, *args)
|
137
|
+
opts = args.pop if args.last.is_a?(Hash)
|
138
|
+
keys = keys.map { |key| "textAsBlob('#{key}')" }.join(',')
|
139
|
+
results = {}
|
140
|
+
query = "SELECT * FROM \"#{column_family}\" WHERE #{KEY_FIELD} IN(#{keys})"
|
141
|
+
self.execute(query, execute_options(opts)).each do |row|
|
142
|
+
results[row[KEY_FIELD]] ||= {}
|
143
|
+
results[row[KEY_FIELD]][row[NAME_FIELD]] = row[VALUE_FIELD]
|
144
|
+
end
|
145
|
+
results
|
146
|
+
end
|
147
|
+
|
109
148
|
def execute_options(opts)
|
110
149
|
opts.try(:slice,
|
111
150
|
:consistency,
|
@@ -115,6 +154,10 @@ module CassandraObject
|
|
115
154
|
:serial_consistency
|
116
155
|
) || {}
|
117
156
|
end
|
157
|
+
|
158
|
+
def has_table?(name)
|
159
|
+
self.cluster.keyspace(session.keyspace).has_table? name
|
160
|
+
end
|
118
161
|
end
|
119
162
|
end
|
120
163
|
end
|
@@ -52,16 +52,19 @@ module CassandraObject
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def remove(key)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
55
|
+
if connection.has_table?(relationships_column_family)
|
56
|
+
begin
|
57
|
+
CassandraObject::Base.with_connection(key, :write) do
|
58
|
+
ActiveSupport::Notifications.instrument("remove.cassandra_object", column_family: relationships_column_family, key: key) do
|
59
|
+
connection.remove(relationships_column_family, key.to_s, consistency: thrift_write_consistency)
|
60
|
+
end
|
59
61
|
end
|
62
|
+
rescue Cassandra::Errors::InvalidError => e
|
63
|
+
# pretty sure this is not the correct message for cassandra-driver gem, will need to investigate the actual message
|
64
|
+
raise e unless e.message =~ /unconfigured columnfamily/i
|
60
65
|
end
|
61
|
-
rescue Cassandra::Error => e
|
62
|
-
# pretty sure this is not the correct message for cassandra-driver gem, will need to investigate the actual message
|
63
|
-
raise e unless e.message =~ /invalid column family/i
|
64
66
|
end
|
67
|
+
|
65
68
|
super
|
66
69
|
end
|
67
70
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CassandraObject::Base do
|
4
|
+
it "should be able to a create/fetch/destroy an issue" do
|
5
|
+
issue = Issue.create! :description => 'web site not working', :worth => 1.5
|
6
|
+
expect(issue.persisted?).to be true
|
7
|
+
issue = Issue.find issue.id
|
8
|
+
expect(issue.worth.to_f).to be 1.5
|
9
|
+
issue.destroy
|
10
|
+
expect(Issue.find_by_id(issue.id)).to be nil
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should be able to get the first issue" do
|
14
|
+
issue = Issue.create! :description => 'web site not working', :worth => 1.5
|
15
|
+
expect(Issue.first.id).to eq issue.id
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should be able to get all issues" do
|
19
|
+
issue1 = Issue.create! :description => 'web site not working', :worth => 1.5
|
20
|
+
issue2 = Issue.create! :description => 'button is disabled', :worth => 0.2
|
21
|
+
expect(Issue.all.map(&:id).sort).to eq [issue1.id, issue2.id].sort
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should be able to find issues by id" do
|
25
|
+
issue1 = Issue.create! :description => 'web site not working', :worth => 1.5
|
26
|
+
issue2 = Issue.create! :description => 'button is disabled', :worth => 0.2
|
27
|
+
issue3 = Issue.create! :description => 'button is disabled', :worth => 0.2
|
28
|
+
expect(Issue.find_with_ids(issue1.id, issue2.id).map(&:id).sort).to eq [issue1.id, issue2.id].sort
|
29
|
+
end
|
30
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
ENV["RAILS_ENV"] = 'test'
|
4
|
+
|
5
|
+
require File.expand_path('../../config/environment', __FILE__)
|
6
|
+
|
7
|
+
Bundler.require :default, :test
|
8
|
+
|
9
|
+
Dir[BASE_DIR.join("spec/support/**/*.rb")].each { |f| require f }
|
10
|
+
|
11
|
+
KEYSPACE = 'cassandra_object_test'
|
12
|
+
|
13
|
+
CassandraObject::Adapters::CassandraDriver.new(CassandraObject::Base.connection_spec).cluster.tap do |cluster|
|
14
|
+
cluster.connect.tap do |session|
|
15
|
+
session.execute("DROP KEYSPACE #{KEYSPACE}") if cluster.has_keyspace?(KEYSPACE)
|
16
|
+
replication = Cassandra::Keyspace::Replication.new('org.apache.cassandra.locator.SimpleStrategy', 'replication_factor' => 1)
|
17
|
+
keyspace = Cassandra::Keyspace.new(KEYSPACE, false, replication, [], [])
|
18
|
+
session.execute keyspace.to_cql
|
19
|
+
session.close
|
20
|
+
end
|
21
|
+
|
22
|
+
cluster.connect(KEYSPACE).tap do |session|
|
23
|
+
session.execute <<-CQL
|
24
|
+
CREATE TABLE "Issues" (
|
25
|
+
key blob,
|
26
|
+
column1 text,
|
27
|
+
value text,
|
28
|
+
PRIMARY KEY (key, column1)
|
29
|
+
)
|
30
|
+
CQL
|
31
|
+
|
32
|
+
session.execute <<-CQL
|
33
|
+
CREATE TABLE "Counters" (
|
34
|
+
key blob,
|
35
|
+
column1 text,
|
36
|
+
value counter,
|
37
|
+
PRIMARY KEY (key, column1)
|
38
|
+
)
|
39
|
+
CQL
|
40
|
+
session.close
|
41
|
+
end
|
42
|
+
|
43
|
+
cluster.close
|
44
|
+
end
|
45
|
+
|
46
|
+
RSpec.configure do |config|
|
47
|
+
|
48
|
+
config.before(:each) do
|
49
|
+
Cassandra::Session.delete_all_populated_column_families
|
50
|
+
end
|
51
|
+
|
52
|
+
config.after(:all) do
|
53
|
+
# we need to call this at the end of each set of tests because if the last test in an rspec file inserts data
|
54
|
+
# into Cassandra it will not be cleaned up at the beginning of the next test in the next rspec file. This was
|
55
|
+
# first spotted in ref #361. The test run in autotest called before_create on UserTransaction and added that
|
56
|
+
# class to @@populated_column_families, but when you run an individual file after autotest @@populated_column_families
|
57
|
+
# is empty, so Cassandra doesn't get cleaned up properly...
|
58
|
+
Cassandra::Session.delete_all_populated_column_families
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'cassandra'
|
2
|
+
|
3
|
+
module Cassandra
|
4
|
+
class Session
|
5
|
+
@@populated_column_families = Set.new
|
6
|
+
@@test_client = CassandraObject::Adapters::CassandraDriver.new(CassandraObject::Base.connection_spec).client
|
7
|
+
|
8
|
+
def self.truncate_populated_column_family(column_family)
|
9
|
+
@@test_client.execute "TRUNCATE \"#{column_family}\""
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.delete_all_populated_column_families
|
13
|
+
unless @@populated_column_families.empty?
|
14
|
+
@@populated_column_families.each { |column_family| self.truncate_populated_column_family(column_family) }
|
15
|
+
@@populated_column_families.clear
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
[:execute_async].each do |method|
|
20
|
+
define_method("#{method}_with_populated_tracking") do |*args|
|
21
|
+
send("#{method}_without_populated_tracking", *args).tap do
|
22
|
+
if args.first =~ /(insert into|update) "(.+?)"/i
|
23
|
+
@@populated_column_families.add $2
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
alias_method_chain method, :populated_tracking
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class Issue < CassandraObject::Base
|
2
|
+
key :uuid
|
3
|
+
attribute :description, :type => :string
|
4
|
+
attribute :worth, :type => :decimal, :precision => 100
|
5
|
+
attribute :name, :type => :string
|
6
|
+
before_validation :set_defaults, :on => :create
|
7
|
+
|
8
|
+
def set_defaults
|
9
|
+
self.name ||= 'default name'
|
10
|
+
end
|
11
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sessionm-cassandra_object
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.
|
4
|
+
version: 4.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Koziarski
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2015-05-
|
13
|
+
date: 2015-05-11 00:00:00.000000000 Z
|
14
14
|
dependencies: []
|
15
15
|
description: Cassandra ActiveModel
|
16
16
|
email: doug@sessionm.com
|
@@ -24,6 +24,7 @@ files:
|
|
24
24
|
- ".ruby-version"
|
25
25
|
- CHANGELOG
|
26
26
|
- Gemfile
|
27
|
+
- Gemfile.lock
|
27
28
|
- LICENSE
|
28
29
|
- MIT-LICENSE
|
29
30
|
- README.markdown
|
@@ -92,6 +93,11 @@ files:
|
|
92
93
|
- lib/cassandra_object/validations.rb
|
93
94
|
- script/console.rb
|
94
95
|
- sessionm-cassandra_object.gemspec
|
96
|
+
- spec/cassandra_object/base_spec.rb
|
97
|
+
- spec/spec_helper.rb
|
98
|
+
- spec/support/cassandra.rb
|
99
|
+
- spec/support/models/counter.rb
|
100
|
+
- spec/support/models/issue.rb
|
95
101
|
- test/README
|
96
102
|
- test/active_model_test.rb
|
97
103
|
- test/attributes_test.rb
|
@@ -141,6 +147,11 @@ signing_key:
|
|
141
147
|
specification_version: 4
|
142
148
|
summary: Cassandra ActiveModel
|
143
149
|
test_files:
|
150
|
+
- spec/cassandra_object/base_spec.rb
|
151
|
+
- spec/spec_helper.rb
|
152
|
+
- spec/support/cassandra.rb
|
153
|
+
- spec/support/models/counter.rb
|
154
|
+
- spec/support/models/issue.rb
|
144
155
|
- test/README
|
145
156
|
- test/active_model_test.rb
|
146
157
|
- test/attributes_test.rb
|