sessionm-cassandra_object 4.0.1 → 4.0.2
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.
- 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
|