cql-model 0.3.0
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/.ruby-version +1 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +56 -0
- data/README.md +24 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/cql-model.gemspec +85 -0
- data/cql_model.rconf +11 -0
- data/lib/cql/model.rb +49 -0
- data/lib/cql/model/class_methods.rb +178 -0
- data/lib/cql/model/instance_methods.rb +50 -0
- data/lib/cql/model/query.rb +107 -0
- data/lib/cql/model/query/comparison_expression.rb +126 -0
- data/lib/cql/model/query/expression.rb +5 -0
- data/lib/cql/model/query/insert_statement.rb +51 -0
- data/lib/cql/model/query/mutation_statement.rb +48 -0
- data/lib/cql/model/query/select_statement.rb +143 -0
- data/lib/cql/model/query/statement.rb +44 -0
- data/lib/cql/model/query/update_expression.rb +104 -0
- data/lib/cql/model/query/update_statement.rb +91 -0
- metadata +227 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
module Cql::Model::Query
|
2
|
+
|
3
|
+
class Statement
|
4
|
+
|
5
|
+
# Initialize instance variables common to all statements
|
6
|
+
#
|
7
|
+
# @param [Class] klass Model class
|
8
|
+
# @param [Cql::Client] client used to connect to Cassandra
|
9
|
+
def initialize(klass, client)
|
10
|
+
@klass = klass
|
11
|
+
@client = client || klass.cql_client
|
12
|
+
@consistency = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
# Build a string representation of this CQL statement, suitable for execution by a CQL client.
|
16
|
+
# @return [String]
|
17
|
+
def to_s
|
18
|
+
raise NotImplementedError, "Subclass responsibility"
|
19
|
+
end
|
20
|
+
|
21
|
+
# Execute this CQL statement. Return value and parameters vary for each derived class.
|
22
|
+
# @see SelectStatement#execute
|
23
|
+
# @see InsertStatement#execute
|
24
|
+
# @see UpdateStatement#execute
|
25
|
+
def execute
|
26
|
+
raise NotImplementedError, "Subclass responsibility"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Specify consistency level to use when executing statemnt
|
30
|
+
# See http://www.datastax.com/docs/1.0/dml/data_consistency
|
31
|
+
# Defaults to :local_quorum
|
32
|
+
#
|
33
|
+
# @param [String] consistency One of 'ANY', 'ONE', 'QUORUM', 'LOCAL_QUORUM', 'EACH_QUORUM', 'ALL' as of Cassandra 1.0
|
34
|
+
# @return [String] consistency value
|
35
|
+
def consistency(consist)
|
36
|
+
raise ArgumentError, "Cannot specify USING CONSISTENCY twice" unless @consistency.nil?
|
37
|
+
@consistency = consist
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
alias using_consistency consistency
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Cql::Model::Query
|
4
|
+
# @TODO docs
|
5
|
+
class UpdateExpression < Expression
|
6
|
+
# Operators allowed in an update lambda
|
7
|
+
OPERATORS = {
|
8
|
+
:+ => '+',
|
9
|
+
:- => '-',
|
10
|
+
:[]= => true # special treatment in #__build__
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
# @TODO docs
|
14
|
+
def initialize(&block)
|
15
|
+
@left = nil
|
16
|
+
@operator = nil
|
17
|
+
@right = nil
|
18
|
+
|
19
|
+
instance_exec(&block) if block
|
20
|
+
end
|
21
|
+
|
22
|
+
# @TODO docs
|
23
|
+
def to_s
|
24
|
+
__build__
|
25
|
+
end
|
26
|
+
|
27
|
+
# @TODO docs
|
28
|
+
def inspect
|
29
|
+
__build__
|
30
|
+
end
|
31
|
+
|
32
|
+
# This is where the magic happens. Ensure all of our operators are overloaded so they call
|
33
|
+
# #apply and contribute to the CQL expression that will be built.
|
34
|
+
OPERATORS.keys.each do |op|
|
35
|
+
define_method(op) do |*args|
|
36
|
+
__apply__(op, args)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# @TODO docs
|
41
|
+
def method_missing(token, *args)
|
42
|
+
__apply__(token, args)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# @TODO docs
|
48
|
+
def __apply__(token, args)
|
49
|
+
if @left.nil?
|
50
|
+
if args.empty?
|
51
|
+
# A well-behaved CQL identifier (column name that is a valid Ruby method name)
|
52
|
+
@left = token
|
53
|
+
elsif args.length == 1
|
54
|
+
# A CQL typecast (column name that is an integer, float, etc and must be wrapped in a decorator)
|
55
|
+
@left = args.first
|
56
|
+
else
|
57
|
+
::Kernel.raise ::Cql::Model::SyntaxError.new(
|
58
|
+
"Unacceptable token '#{token}'; expected a CQL identifier or typecast")
|
59
|
+
end
|
60
|
+
elsif @operator.nil?
|
61
|
+
# Looking for an operator + right operand
|
62
|
+
if OPERATORS.keys.include?(token)
|
63
|
+
@operator = token
|
64
|
+
|
65
|
+
if (token == :[]=)
|
66
|
+
@right = args # the right-hand argument of []= is a (key, value) pair
|
67
|
+
else
|
68
|
+
@right = args.first
|
69
|
+
end
|
70
|
+
else
|
71
|
+
::Kernel.raise ::Cql::Model::SyntaxError.new(
|
72
|
+
"Unacceptable token '#{token}'; expected a CQL-compatible operator")
|
73
|
+
end
|
74
|
+
else
|
75
|
+
::Kernel.raise ::Cql::Model::SyntaxError.new(
|
76
|
+
"Unacceptable token '#{token}'; the expression is " +
|
77
|
+
"already complete")
|
78
|
+
end
|
79
|
+
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
83
|
+
# @TODO docs
|
84
|
+
def __build__
|
85
|
+
if @left.nil? || @operator.nil? || @right.nil?
|
86
|
+
::Kernel.raise ::Cql::Model::SyntaxError.new(
|
87
|
+
"Cannot build a CQL expression; the Ruby expression is incomplete " +
|
88
|
+
"(#{@left.inspect}, #{@operator.inspect}, #{@right.inspect})")
|
89
|
+
else
|
90
|
+
left = ::Cql::Model::Query.cql_identifier(@left)
|
91
|
+
case @operator
|
92
|
+
when :[]=
|
93
|
+
key = ::Cql::Model::Query.cql_value(@right[0], context=:update)
|
94
|
+
val = ::Cql::Model::Query.cql_value(@right[1], context=:update)
|
95
|
+
"#{left}[#{key}] = #{val}"
|
96
|
+
else
|
97
|
+
op = OPERATORS[@operator]
|
98
|
+
right = ::Cql::Model::Query.cql_value(@right, context=:update)
|
99
|
+
"#{left} #{op} #{right}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Cql::Model::Query
|
2
|
+
|
3
|
+
# UPDATE statement DSL
|
4
|
+
# << An UPDATE writes one or more columns to a record in a Cassandra column family. No results are returned.
|
5
|
+
# Row/column records are created if they do not exist, or overwritten if they do exist >>
|
6
|
+
# (from http://www.datastax.com/docs/1.1/references/cql/UPDATE)
|
7
|
+
#
|
8
|
+
# Note: user a hash with a single key :value to update counter columns using the existing counter value:
|
9
|
+
# update(:id => 12, :counter => { :value => 'counter + 1' })
|
10
|
+
#
|
11
|
+
# E.g.:
|
12
|
+
# Model.update(:id => '123', :col => 'value', :counter => { :value => 'counter + 2' })
|
13
|
+
# Model.update(:id => ['123', '456'], :col => 'value')
|
14
|
+
# Model.update(:id => '123', :col => 'value').ttl(3600)
|
15
|
+
# Model.update(:id => '123', :col => 'value').timestamp(1366057256324)
|
16
|
+
# Model.update(:id => '123', :col => 'value').timestamp('2013-04-15 13:21:48')
|
17
|
+
# Model.update(:id => '123', :col => 'value').consistency('ONE')
|
18
|
+
# Model.update(:id => ['123', '456'], :col => 'value', :counter => 'counter + 2').ttl(3600).timestamp(1366057256324).consistency('ONE')
|
19
|
+
#
|
20
|
+
# Can also be used on Model instances, e.g.:
|
21
|
+
# @model.update(:col => 'value', :counter => 'counter + 2')
|
22
|
+
# @model.update_all_by('name', :col => 'value') # 'name' must be part of the table composite key
|
23
|
+
class UpdateStatement < MutationStatement
|
24
|
+
def initialize(klass, client=nil)
|
25
|
+
super(klass, client)
|
26
|
+
@where = []
|
27
|
+
end
|
28
|
+
|
29
|
+
# Create or append to the WHERE clause for this statement. The block that you pass will define the constraint
|
30
|
+
# and any where() parameters will be forwarded to the block as yield parameters. This allows late binding of
|
31
|
+
# variables in the WHERE clause, e.g. for prepared statements.
|
32
|
+
# TODO examples
|
33
|
+
# @see Expression
|
34
|
+
def where(*params, &block)
|
35
|
+
@where << ComparisonExpression.new(*params, &block)
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
alias and where
|
40
|
+
|
41
|
+
# DSL for setting UPDATE values
|
42
|
+
#
|
43
|
+
# @param [Hash] values Hash of column values or column update expression indexed by column name
|
44
|
+
def update(values)
|
45
|
+
raise ArgumentError, "Cannot specify UPDATE values twice" unless @values.nil?
|
46
|
+
@values = values
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [String] a CQL UPDATE statement with suitable constraints and options
|
51
|
+
def to_s
|
52
|
+
s = "UPDATE #{@klass.table_name}"
|
53
|
+
|
54
|
+
options = []
|
55
|
+
options << "CONSISTENCY #{@consistency || @klass.write_consistency}"
|
56
|
+
options << "TIMESTAMP #{@timestamp}" unless @timestamp.nil?
|
57
|
+
options << "TTL #{@ttl}" unless @ttl.nil?
|
58
|
+
s << " USING #{options.join(' AND ')}"
|
59
|
+
|
60
|
+
if @values.respond_to?(:map)
|
61
|
+
if @values.respond_to?(:each_pair)
|
62
|
+
# List of column names and values (or lambdas containing list/set/counter operations)
|
63
|
+
pairs = @values.map do |n, v|
|
64
|
+
if v.respond_to?(:call)
|
65
|
+
"#{n} = #{UpdateExpression.new(&v).to_s}"
|
66
|
+
else
|
67
|
+
"#{n} = #{::Cql::Model::Query.cql_value(v)}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
s << " SET #{pairs.join(', ')}"
|
71
|
+
elsif @values.all? { |v| v.respond_to?(:call) }
|
72
|
+
# Array of hash assignments
|
73
|
+
assigns = @values.map { |v| "#{UpdateExpression.new(&v).to_s}" }
|
74
|
+
s << " SET #{assigns.join(', ')}"
|
75
|
+
end
|
76
|
+
elsif @values.respond_to?(:call)
|
77
|
+
# Simple hash assignment
|
78
|
+
assign = UpdateExpression.new(&@values).to_s
|
79
|
+
s << " SET #{assign}"
|
80
|
+
end
|
81
|
+
|
82
|
+
unless @where.empty?
|
83
|
+
s << " WHERE " << @where.map { |w| w.to_s }.join(' AND ')
|
84
|
+
end
|
85
|
+
|
86
|
+
s << ';'
|
87
|
+
|
88
|
+
s
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
metadata
ADDED
@@ -0,0 +1,227 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cql-model
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Tony Spataro
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-04-25 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: cql-rb
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.0.0.pre4
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.0.0.pre4
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0.9'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0.9'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: cucumber
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '1.0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: jeweler
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 1.8.3
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 1.8.3
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: debugger
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: rdoc
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 2.4.2
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 2.4.2
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: flexmock
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ~>
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0.8'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ~>
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0.8'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: syntax
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ~>
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: 1.0.0
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ~>
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 1.0.0
|
158
|
+
- !ruby/object:Gem::Dependency
|
159
|
+
name: nokogiri
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ~>
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '1.5'
|
166
|
+
type: :development
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ~>
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '1.5'
|
174
|
+
description: Lightweight, performant OOP wrapper for Cassandra tables; inspired by
|
175
|
+
DataMapper.
|
176
|
+
email: gemspec@tracker.xeger.net
|
177
|
+
executables: []
|
178
|
+
extensions: []
|
179
|
+
extra_rdoc_files:
|
180
|
+
- README.md
|
181
|
+
files:
|
182
|
+
- .ruby-version
|
183
|
+
- Gemfile
|
184
|
+
- Gemfile.lock
|
185
|
+
- README.md
|
186
|
+
- Rakefile
|
187
|
+
- VERSION
|
188
|
+
- cql-model.gemspec
|
189
|
+
- cql_model.rconf
|
190
|
+
- lib/cql/model.rb
|
191
|
+
- lib/cql/model/class_methods.rb
|
192
|
+
- lib/cql/model/instance_methods.rb
|
193
|
+
- lib/cql/model/query.rb
|
194
|
+
- lib/cql/model/query/comparison_expression.rb
|
195
|
+
- lib/cql/model/query/expression.rb
|
196
|
+
- lib/cql/model/query/insert_statement.rb
|
197
|
+
- lib/cql/model/query/mutation_statement.rb
|
198
|
+
- lib/cql/model/query/select_statement.rb
|
199
|
+
- lib/cql/model/query/statement.rb
|
200
|
+
- lib/cql/model/query/update_expression.rb
|
201
|
+
- lib/cql/model/query/update_statement.rb
|
202
|
+
homepage: https://github.com/xeger/cql-model
|
203
|
+
licenses:
|
204
|
+
- MIT
|
205
|
+
post_install_message:
|
206
|
+
rdoc_options: []
|
207
|
+
require_paths:
|
208
|
+
- lib
|
209
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
210
|
+
none: false
|
211
|
+
requirements:
|
212
|
+
- - ! '>='
|
213
|
+
- !ruby/object:Gem::Version
|
214
|
+
version: 1.9.0
|
215
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
216
|
+
none: false
|
217
|
+
requirements:
|
218
|
+
- - ! '>='
|
219
|
+
- !ruby/object:Gem::Version
|
220
|
+
version: '0'
|
221
|
+
requirements: []
|
222
|
+
rubyforge_project:
|
223
|
+
rubygems_version: 1.8.23
|
224
|
+
signing_key:
|
225
|
+
specification_version: 3
|
226
|
+
summary: Cassandra CQL model.
|
227
|
+
test_files: []
|