gotime-cassandra_object 4.1.0 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -61,4 +61,12 @@ Cassandra Object has equivalent methods as ActiveRecord:
61
61
  ...
62
62
  end
63
63
 
64
- CQL is currently not supported
64
+ == Scoping
65
+
66
+ Some lightweight scoping features are available:
67
+
68
+ Widget.where('color' => 'red')
69
+ Widget.select(['name', 'color'])
70
+ Widget.limit(10)
71
+
72
+
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'gotime-cassandra_object'
5
- s.version = '4.1.0'
5
+ s.version = '4.2.0'
6
6
  s.description = 'Cassandra ActiveModel'
7
7
  s.summary = 'Cassandra ActiveModel'
8
8
  s.authors = ["Michael Koziarski", "gotime"]
@@ -31,9 +31,7 @@ module CassandraObject
31
31
  include Connection
32
32
  include Identity
33
33
  include Inspect
34
- include FinderMethods
35
34
  include Persistence
36
- include Batches
37
35
  include AttributeMethods
38
36
  include Validations
39
37
  include AttributeMethods::Dirty
@@ -43,6 +41,7 @@ module CassandraObject
43
41
  include Callbacks, ActiveModel::Observing
44
42
  include Timestamps
45
43
  include Savepoints
44
+ include Scoping
46
45
 
47
46
  include Serialization
48
47
 
@@ -1,37 +1,29 @@
1
1
  module CassandraObject
2
2
  class LogSubscriber < ActiveSupport::LogSubscriber
3
- def multi_get(event)
4
- name = '%s multi_get (%.1fms)' % [event.payload[:column_family], event.duration]
5
-
6
- debug " #{name} (#{event.payload[:keys].size}) #{event.payload[:keys].join(" ")}"
7
- end
8
-
9
- def remove(event)
10
- name = '%s remove (%.1fms)' % [event.payload[:column_family], event.duration]
11
-
12
- message = " #{name} #{event.payload[:key]}"
13
- message << " #{Array(event.payload[:attributes]).inspect}" if event.payload[:attributes]
14
-
15
- debug message
3
+ def initialize
4
+ super
5
+ @odd_or_even = false
16
6
  end
17
7
 
18
- def truncate(event)
19
- name = '%s truncate (%.1fms)' % [event.payload[:column_family], event.duration]
20
-
21
- debug " #{name} #{event.payload[:column_family]}"
22
- end
8
+ def cql(event)
9
+ payload = event.payload
10
+ name = '%s (%.1fms)' % [payload[:name], event.duration]
11
+ cql = payload[:cql].squeeze(' ')
23
12
 
24
- def insert(event)
25
- name = '%s insert (%.1fms)' % [event.payload[:column_family], event.duration]
13
+ if odd?
14
+ name = color(name, CYAN, true)
15
+ cql = color(cql, nil, true)
16
+ else
17
+ name = color(name, MAGENTA, true)
18
+ end
26
19
 
27
- debug " #{name} #{event.payload[:key]} #{event.payload[:attributes].inspect}"
20
+ debug " #{name} #{cql}"
28
21
  end
29
22
 
30
- def get_range(event)
31
- name = '%s get_range (%.1fms)' % [event.payload[:column_family], event.duration]
32
-
33
- debug " #{name} (#{event.payload[:count]}) '#{event.payload[:start]}' => '#{event.payload[:finish]}'"
23
+ def odd?
24
+ @odd_or_even = !@odd_or_even
34
25
  end
35
26
  end
36
27
  end
28
+
37
29
  CassandraObject::LogSubscriber.attach_to :cassandra_object
@@ -1,8 +1,6 @@
1
1
  module CassandraObject
2
- module Batches
3
- extend ActiveSupport::Concern
4
-
5
- module ClassMethods
2
+ class Scope
3
+ module Batches
6
4
  def find_each(options = {})
7
5
  find_in_batches(options) do |records|
8
6
  records.each { |record| yield record }
@@ -13,8 +11,8 @@ module CassandraObject
13
11
  batch_size = options.delete(:batch_size) || 1000
14
12
  start_key = nil
15
13
 
16
- statement = "select * from #{column_family} limit #{batch_size + 1}"
17
- records = instantiate_from_cql statement
14
+ scope = limit(batch_size + 1)
15
+ records = scope.to_a
18
16
 
19
17
  while records.any?
20
18
  if records.size > batch_size
@@ -26,8 +24,7 @@ module CassandraObject
26
24
  yield records
27
25
  break if next_record.nil?
28
26
 
29
- statement = "SELECT * FROM #{column_family} WHERE KEY >= ? LIMIT #{batch_size + 1}"
30
- records = instantiate_from_cql statement, next_record.id
27
+ records = scope.where("KEY >= #{next_record.id}").to_a
31
28
  end
32
29
  end
33
30
  end
@@ -0,0 +1,47 @@
1
+ module CassandraObject
2
+ class Scope
3
+ module FinderMethods
4
+ def find(ids)
5
+ if ids.is_a?(Array)
6
+ find_some(ids)
7
+ else
8
+ find_one(ids)
9
+ end
10
+ end
11
+
12
+ def find_by_id(ids)
13
+ find(ids)
14
+ rescue CassandraObject::RecordNotFound
15
+ nil
16
+ end
17
+
18
+ def all
19
+ to_a
20
+ end
21
+
22
+ def first
23
+ limit(1).to_a.first
24
+ end
25
+
26
+ private
27
+ def find_one(id)
28
+ if id.blank?
29
+ raise CassandraObject::RecordNotFound, "Couldn't find #{self.name} with key #{id.inspect}"
30
+ elsif record = where('KEY' => id).first
31
+ record
32
+ else
33
+ raise CassandraObject::RecordNotFound
34
+ end
35
+ end
36
+
37
+ def find_some(ids)
38
+ ids = ids.flatten
39
+ return [] if ids.empty?
40
+
41
+ ids = ids.compact.map(&:to_s).uniq
42
+
43
+ where("KEY in (#{ids * ','})").to_a
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,79 @@
1
+ module CassandraObject
2
+ class Scope
3
+ module QueryMethods
4
+ def select!(value)
5
+ self.select_values += Array.wrap(value)
6
+ self
7
+ end
8
+
9
+ def select(value)
10
+ clone.select! value
11
+ end
12
+
13
+ def where!(*values)
14
+ self.where_values += values.flatten
15
+ self
16
+ end
17
+
18
+ def where(*values)
19
+ clone.where! values
20
+ end
21
+
22
+ def limit!(value)
23
+ self.limit_value = value
24
+ self
25
+ end
26
+
27
+ def limit(value)
28
+ clone.limit! value
29
+ end
30
+
31
+ def to_a
32
+ statement = [
33
+ "select #{select_string} from #{klass.column_family}",
34
+ where_string,
35
+ limit_string
36
+ ].delete_if(&:blank?) * ' '
37
+
38
+ instantiate_from_cql(statement)
39
+ end
40
+
41
+ private
42
+ def select_string
43
+ if select_values.any?
44
+ select_values * ','
45
+ else
46
+ '*'
47
+ end
48
+ end
49
+
50
+ def where_string
51
+ if where_values.any?
52
+ wheres = []
53
+
54
+ where_values.map do |where_value|
55
+ if where_value.is_a?(String)
56
+ wheres << where_value
57
+ elsif where_value.is_a?(Hash)
58
+ where_value.each do |column, value|
59
+ wheres << "#{column} = #{value}"
60
+ end
61
+ end
62
+ end
63
+
64
+ "WHERE #{wheres * ' AND '}"
65
+ else
66
+ ''
67
+ end
68
+ end
69
+
70
+ def limit_string
71
+ if limit_value
72
+ "limit #{limit_value}"
73
+ else
74
+ ""
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,48 @@
1
+ require 'cassandra_object/scope/batches'
2
+ require 'cassandra_object/scope/finder_methods'
3
+ require 'cassandra_object/scope/query_methods'
4
+
5
+ module CassandraObject
6
+ class Scope
7
+ include Batches, FinderMethods, QueryMethods
8
+
9
+ attr_accessor :klass
10
+ attr_accessor :limit_value, :select_values, :where_values
11
+
12
+ def initialize(klass)
13
+ @klass = klass
14
+
15
+ @limit_value = nil
16
+ @select_values = []
17
+ @where_values = []
18
+ end
19
+
20
+ private
21
+ def method_missing(method_name, *args, &block)
22
+ if klass.respond_to?(method_name)
23
+ klass.send(method_name, *args, &block)
24
+ elsif Array.method_defined?(method_name)
25
+ to_a.send(method_name, *args, &block)
26
+ else
27
+ super
28
+ end
29
+ end
30
+
31
+ def instantiate_from_cql(cql_string, *args)
32
+ results = []
33
+ klass.execute_cql(cql_string, *args).fetch do |cql_row|
34
+ results << instantiate_cql_row(cql_row)
35
+ end
36
+ results.compact!
37
+ results
38
+ end
39
+
40
+ def instantiate_cql_row(cql_row)
41
+ attributes = cql_row.to_hash
42
+ key = attributes.delete('KEY')
43
+ if attributes.any?
44
+ instantiate(key, attributes)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,19 @@
1
+ module CassandraObject
2
+ module Scoping
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ singleton_class.class_eval do
7
+ delegate :find, :find_by_id, :first, :all, to: :scope
8
+ delegate :find_each, :find_in_batches, to: :scope
9
+ delegate :select, :where, to: :scope
10
+ end
11
+ end
12
+
13
+ module ClassMethods
14
+ def scope
15
+ Scope.new(self)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -15,10 +15,10 @@ module CassandraObject
15
15
  autoload :Identity
16
16
  autoload :Inspect
17
17
  autoload :Serialization
18
- autoload :Batches
19
- autoload :FinderMethods
20
18
  autoload :Savepoints
21
19
  autoload :Schema
20
+ autoload :Scope
21
+ autoload :Scoping
22
22
  autoload :Timestamps
23
23
  autoload :Type
24
24
 
@@ -0,0 +1,6 @@
1
+ require "test_helper"
2
+ require "active_support/log_subscriber/test_helper"
3
+
4
+ class CassandraObject::LogSubscriberTest < CassandraObject::TestCase
5
+ include ActiveSupport::LogSubscriber::TestHelper
6
+ end
@@ -0,0 +1,12 @@
1
+ require 'test_helper'
2
+
3
+ class CassandraObject::QueryMethodsTest < CassandraObject::TestCase
4
+ test "select" do
5
+ issue = Issue.create title: 'foo', description: 'bar'
6
+
7
+ issue = Issue.select(:title).find(issue.id)
8
+
9
+ assert_equal 'foo', issue.title
10
+ assert_nil issue.description
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class CassandraObject::ScopingTest < CassandraObject::TestCase
4
+ test "scope" do
5
+ assert_kind_of CassandraObject::Scope, Issue.scope
6
+ end
7
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gotime-cassandra_object
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0
4
+ version: 4.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-08-13 00:00:00.000000000 Z
13
+ date: 2012-08-14 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activemodel
@@ -98,7 +98,6 @@ files:
98
98
  - lib/cassandra_object/attribute_methods/primary_key.rb
99
99
  - lib/cassandra_object/attribute_methods/typecasting.rb
100
100
  - lib/cassandra_object/base.rb
101
- - lib/cassandra_object/batches.rb
102
101
  - lib/cassandra_object/belongs_to.rb
103
102
  - lib/cassandra_object/belongs_to/association.rb
104
103
  - lib/cassandra_object/belongs_to/builder.rb
@@ -106,7 +105,6 @@ files:
106
105
  - lib/cassandra_object/callbacks.rb
107
106
  - lib/cassandra_object/connection.rb
108
107
  - lib/cassandra_object/errors.rb
109
- - lib/cassandra_object/finder_methods.rb
110
108
  - lib/cassandra_object/identity.rb
111
109
  - lib/cassandra_object/inspect.rb
112
110
  - lib/cassandra_object/log_subscriber.rb
@@ -114,6 +112,11 @@ files:
114
112
  - lib/cassandra_object/railtie.rb
115
113
  - lib/cassandra_object/savepoints.rb
116
114
  - lib/cassandra_object/schema.rb
115
+ - lib/cassandra_object/scope.rb
116
+ - lib/cassandra_object/scope/batches.rb
117
+ - lib/cassandra_object/scope/finder_methods.rb
118
+ - lib/cassandra_object/scope/query_methods.rb
119
+ - lib/cassandra_object/scoping.rb
117
120
  - lib/cassandra_object/serialization.rb
118
121
  - lib/cassandra_object/tasks/ks.rake
119
122
  - lib/cassandra_object/timestamps.rb
@@ -142,15 +145,18 @@ files:
142
145
  - test/unit/attribute_methods/typecasting_test.rb
143
146
  - test/unit/attribute_methods_test.rb
144
147
  - test/unit/base_test.rb
145
- - test/unit/batches_test.rb
146
148
  - test/unit/belongs_to_test.rb
147
149
  - test/unit/callbacks_test.rb
148
150
  - test/unit/connection_test.rb
149
- - test/unit/finder_methods_test.rb
150
151
  - test/unit/identity_test.rb
151
152
  - test/unit/inspect_test.rb
153
+ - test/unit/log_subscriber_test.rb
152
154
  - test/unit/persistence_test.rb
153
155
  - test/unit/savepoints_test.rb
156
+ - test/unit/scope/batches_test.rb
157
+ - test/unit/scope/finder_methods_test.rb
158
+ - test/unit/scope/query_methods_test.rb
159
+ - test/unit/scoping_test.rb
154
160
  - test/unit/timestamps_test.rb
155
161
  - test/unit/types/array_type_test.rb
156
162
  - test/unit/types/base_type_test.rb
@@ -1,68 +0,0 @@
1
- module CassandraObject
2
- module FinderMethods
3
- extend ActiveSupport::Concern
4
-
5
- module ClassMethods
6
- def find(ids)
7
- if ids.is_a?(Array)
8
- find_some(ids)
9
- else
10
- find_one(ids)
11
- end
12
- end
13
-
14
- def find_by_id(ids)
15
- find(ids)
16
- rescue CassandraObject::RecordNotFound
17
- nil
18
- end
19
-
20
- def all
21
- instantiate_from_cql "select * from #{column_family}"
22
- end
23
-
24
- def first(options = {})
25
- instantiate_from_cql("select * from #{column_family} limit 1").first
26
- end
27
-
28
- private
29
-
30
- def instantiate_from_cql(cql_string, *args)
31
- results = []
32
- execute_cql(cql_string, *args).fetch do |cql_row|
33
- results << instantiate_cql_row(cql_row)
34
- end
35
- results.compact!
36
- results
37
- end
38
-
39
- def instantiate_cql_row(cql_row)
40
- attributes = cql_row.to_hash
41
- key = attributes.delete('KEY')
42
- if attributes.any?
43
- instantiate(key, attributes)
44
- end
45
- end
46
-
47
- def find_one(id)
48
- if id.blank?
49
- raise CassandraObject::RecordNotFound, "Couldn't find #{self.name} with key #{id.inspect}"
50
- elsif record = instantiate_from_cql("select * from #{column_family} where KEY = ? limit 1", id).first
51
- record
52
- else
53
- raise CassandraObject::RecordNotFound
54
- end
55
- end
56
-
57
- def find_some(ids)
58
- ids = ids.flatten
59
- return [] if ids.empty?
60
-
61
- ids = ids.compact.map(&:to_s).uniq
62
-
63
- statement = "select * from #{column_family} where KEY in (#{Array.new(ids.size, '?') * ','})"
64
- instantiate_from_cql statement, *ids
65
- end
66
- end
67
- end
68
- end