virsandra 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +22 -0
- data/README.md +93 -0
- data/Rakefile +18 -0
- data/lib/virsandra.rb +89 -0
- data/lib/virsandra/configuration.rb +61 -0
- data/lib/virsandra/connection.rb +39 -0
- data/lib/virsandra/cql_value.rb +38 -0
- data/lib/virsandra/errors.rb +3 -0
- data/lib/virsandra/model.rb +86 -0
- data/lib/virsandra/model_query.rb +52 -0
- data/lib/virsandra/queries/add_query.rb +25 -0
- data/lib/virsandra/queries/alter_query.rb +34 -0
- data/lib/virsandra/queries/delete_query.rb +25 -0
- data/lib/virsandra/queries/insert_query.rb +34 -0
- data/lib/virsandra/queries/limit_query.rb +17 -0
- data/lib/virsandra/queries/order_query.rb +36 -0
- data/lib/virsandra/queries/select_query.rb +72 -0
- data/lib/virsandra/queries/table_query.rb +13 -0
- data/lib/virsandra/queries/values_query.rb +41 -0
- data/lib/virsandra/queries/where_query.rb +69 -0
- data/lib/virsandra/query.rb +87 -0
- data/lib/virsandra/version.rb +3 -0
- data/spec/feature_helper.rb +62 -0
- data/spec/integration/virsandra_spec.rb +13 -0
- data/spec/lib/virsandra/configuration_spec.rb +66 -0
- data/spec/lib/virsandra/connection_spec.rb +47 -0
- data/spec/lib/virsandra/cql_value_spec.rb +25 -0
- data/spec/lib/virsandra/model_query_spec.rb +58 -0
- data/spec/lib/virsandra/model_spec.rb +173 -0
- data/spec/lib/virsandra/queries/add_query_spec.rb +26 -0
- data/spec/lib/virsandra/queries/alter_query_spec.rb +35 -0
- data/spec/lib/virsandra/queries/delete_query_spec.rb +34 -0
- data/spec/lib/virsandra/queries/insert_query_spec.rb +36 -0
- data/spec/lib/virsandra/queries/limit_query_spec.rb +20 -0
- data/spec/lib/virsandra/queries/order_query_spec.rb +33 -0
- data/spec/lib/virsandra/queries/select_query_spec.rb +108 -0
- data/spec/lib/virsandra/queries/table_query_spec.rb +13 -0
- data/spec/lib/virsandra/queries/values_query_spec.rb +41 -0
- data/spec/lib/virsandra/queries/where_query_spec.rb +76 -0
- data/spec/lib/virsandra/query_spec.rb +117 -0
- data/spec/lib/virsandra_spec.rb +108 -0
- data/spec/spec_helper.rb +19 -0
- metadata +207 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
module Virsandra
|
2
|
+
class ModelQuery
|
3
|
+
|
4
|
+
def initialize(model)
|
5
|
+
@model = model
|
6
|
+
end
|
7
|
+
|
8
|
+
def find_by_key
|
9
|
+
return {} unless @model.valid?
|
10
|
+
query = Query.select.from(@model.table).where(@model.key)
|
11
|
+
query.fetch
|
12
|
+
end
|
13
|
+
|
14
|
+
def save
|
15
|
+
query = Query.insert.into(@model.table).values(@model.attributes)
|
16
|
+
query.fetch
|
17
|
+
end
|
18
|
+
|
19
|
+
def delete
|
20
|
+
query = Query.delete.from(@model.table).where(@model.key)
|
21
|
+
query.fetch
|
22
|
+
end
|
23
|
+
|
24
|
+
def where(params)
|
25
|
+
query = Query.select.from(@model.table)
|
26
|
+
|
27
|
+
unless params.empty?
|
28
|
+
raise ArgumentError.new("Invalid search terms") unless valid_search_params?(params)
|
29
|
+
query.where(params)
|
30
|
+
end
|
31
|
+
|
32
|
+
query_enumerator(query)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def valid_search_params?(params)
|
38
|
+
params.keys.all? { |k| @model.column_names.include?(k.to_sym) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def query_enumerator(query)
|
42
|
+
Enumerator.new do |yielder|
|
43
|
+
rows = query.execute
|
44
|
+
rows.each do |row|
|
45
|
+
record = @model.new(row.to_hash)
|
46
|
+
yielder.yield record
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Virsandra
|
2
|
+
class AddQuery < Query
|
3
|
+
def initialize(column_name, column_type = nil)
|
4
|
+
@column_name = column_name
|
5
|
+
@column_type = column_type.to_s.empty? ? "varchar" : column_type
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
validate
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def raw_query
|
16
|
+
["ADD", @column_name, @column_type]
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate
|
20
|
+
if @column_name.to_s.empty?
|
21
|
+
raise InvalidQuery.new("You must specify column name")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Virsandra
|
2
|
+
class AlterQuery < Query
|
3
|
+
def initialize(skip_validation = false)
|
4
|
+
@skip_validation = skip_validation
|
5
|
+
end
|
6
|
+
|
7
|
+
def table(table_name)
|
8
|
+
@table = TableQuery.new("TABLE", table_name)
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def add(column_name, column_type = nil)
|
13
|
+
@add = AddQuery.new(column_name, column_type)
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
validate
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def raw_query
|
25
|
+
["ALTER", @table, @add]
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate
|
29
|
+
if !@skip_validation && @table.to_s.empty?
|
30
|
+
raise InvalidQuery.new("You must set the table")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Virsandra
|
2
|
+
class DeleteQuery < Query
|
3
|
+
|
4
|
+
def from(table)
|
5
|
+
@from = TableQuery.new("FROM" ,table)
|
6
|
+
self
|
7
|
+
end
|
8
|
+
alias_method :table, :from
|
9
|
+
|
10
|
+
def where(clause)
|
11
|
+
unless @where
|
12
|
+
@where = WhereQuery.new(clause)
|
13
|
+
else
|
14
|
+
@where += WhereQuery.new(clause)
|
15
|
+
end
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def raw_query
|
22
|
+
["DELETE", @from.to_s, @where.to_s]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Virsandra
|
2
|
+
class InsertQuery < Query
|
3
|
+
|
4
|
+
def into(table_name)
|
5
|
+
@into = TableQuery.new("INTO", table_name)
|
6
|
+
self
|
7
|
+
end
|
8
|
+
|
9
|
+
def values(values)
|
10
|
+
@values = ValuesQuery.new(values)
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
validate
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def raw_query
|
22
|
+
["INSERT", @into, @values]
|
23
|
+
end
|
24
|
+
|
25
|
+
def validate
|
26
|
+
if @into.to_s.empty?
|
27
|
+
raise InvalidQuery.new("You must set into")
|
28
|
+
end
|
29
|
+
if @values.to_s.empty?
|
30
|
+
raise InvalidQuery.new("You must set values")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Virsandra
|
2
|
+
class LimitQuery < Query
|
3
|
+
def initialize(number)
|
4
|
+
@number = number
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def raw_query
|
10
|
+
if @number.to_i > 0
|
11
|
+
["LIMIT", @number]
|
12
|
+
else
|
13
|
+
raise InvalidQuery.new("Limit must be positive number")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Virsandra
|
2
|
+
class OrderQuery < Query
|
3
|
+
VALID_ORDERS = ["ASC", "DESC"]
|
4
|
+
|
5
|
+
def initialize(columns)
|
6
|
+
@columns = columns
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def raw_query
|
12
|
+
[@columns.nil? ? "" : "ORDER BY" ,columns_as_string]
|
13
|
+
end
|
14
|
+
|
15
|
+
def columns_as_string
|
16
|
+
if @columns.respond_to?(:each_pair)
|
17
|
+
result = []
|
18
|
+
@columns.each_pair do |column_name, order|
|
19
|
+
result << column_as_string(column_name, order)
|
20
|
+
end
|
21
|
+
result.join(", ")
|
22
|
+
else
|
23
|
+
@columns.to_s
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def column_as_string(column_name, order)
|
28
|
+
normalized_order = order.to_s.upcase
|
29
|
+
if VALID_ORDERS.include?(normalized_order)
|
30
|
+
"#{column_name} #{normalized_order}"
|
31
|
+
else
|
32
|
+
raise InvalidQuery.new("Unknown order #{order}")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Virsandra
|
2
|
+
class SelectQuery < Query
|
3
|
+
def initialize(columns = nil, skip_validation = false)
|
4
|
+
@columns = columns
|
5
|
+
@skip_validation = skip_validation
|
6
|
+
end
|
7
|
+
|
8
|
+
def from(table)
|
9
|
+
@from = TableQuery.new("FROM", table)
|
10
|
+
self
|
11
|
+
end
|
12
|
+
|
13
|
+
def where(clause)
|
14
|
+
unless @where
|
15
|
+
@where = WhereQuery.new(clause)
|
16
|
+
else
|
17
|
+
@where += WhereQuery.new(clause)
|
18
|
+
end
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def order(columns)
|
23
|
+
@order = OrderQuery.new(columns)
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def limit(number)
|
28
|
+
@limit = LimitQuery.new(number)
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def allow_filtering!
|
33
|
+
@allow_filtering = "ALLOW FILTERING"
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def deny_filtering!
|
38
|
+
@allow_filtering = nil
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def reset
|
43
|
+
@where, @order, @limit = nil
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_s
|
47
|
+
validate
|
48
|
+
super
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def raw_query
|
54
|
+
["SELECT", columns_as_string, @from, @where, @order, @limit, @allow_filtering]
|
55
|
+
end
|
56
|
+
|
57
|
+
def columns_as_string
|
58
|
+
converted_columns = if @columns.respond_to?(:join)
|
59
|
+
@columns.join(", ")
|
60
|
+
else
|
61
|
+
@columns.to_s
|
62
|
+
end
|
63
|
+
converted_columns.empty? ? "*" : converted_columns
|
64
|
+
end
|
65
|
+
|
66
|
+
def validate
|
67
|
+
if !@skip_validation && @from.to_s.empty?
|
68
|
+
raise InvalidQuery.new("You must set from")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Virsandra
|
2
|
+
class ValuesQuery < Query
|
3
|
+
def initialize(values)
|
4
|
+
@values = values
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def raw_query
|
10
|
+
[values_to_string]
|
11
|
+
end
|
12
|
+
|
13
|
+
def values_to_string
|
14
|
+
if @values.respond_to?(:each_pair)
|
15
|
+
if values_str = values_as_string and !values_str.empty?
|
16
|
+
"(#{columns_as_string}) VALUES (#{values_str})"
|
17
|
+
else
|
18
|
+
""
|
19
|
+
end
|
20
|
+
else
|
21
|
+
""
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def columns_as_string
|
26
|
+
columns = []
|
27
|
+
@values.each_pair do |key, value|
|
28
|
+
columns << key unless value.nil?
|
29
|
+
end
|
30
|
+
columns.join(", ")
|
31
|
+
end
|
32
|
+
|
33
|
+
def values_as_string
|
34
|
+
values = []
|
35
|
+
@values.each_pair do |key, value|
|
36
|
+
values << CQLValue.convert(value) unless value.nil?
|
37
|
+
end
|
38
|
+
values.join(", ")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Virsandra
|
2
|
+
class WhereQuery < Query
|
3
|
+
OPERATOR_MAPPING = {
|
4
|
+
:lt => "<",
|
5
|
+
:gt => ">",
|
6
|
+
:eq => "=",
|
7
|
+
:lt_or_eq => "<=",
|
8
|
+
:gt_or_eq => ">=",
|
9
|
+
:in => "IN"
|
10
|
+
}
|
11
|
+
|
12
|
+
def initialize(clause)
|
13
|
+
@clause = clause
|
14
|
+
end
|
15
|
+
|
16
|
+
def +(where_query)
|
17
|
+
if where_query.is_a?(self.class)
|
18
|
+
@clause = [to_s, where_query.to_s.gsub(/^WHERE\s+/, "")].join(" AND ")
|
19
|
+
else
|
20
|
+
raise InvalidQuery.new("WhereQuery can be mereged only with other WhereQuery")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
alias_method :merge, :+
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def raw_query
|
28
|
+
["WHERE", clause_as_string]
|
29
|
+
end
|
30
|
+
|
31
|
+
def clause_as_string
|
32
|
+
if @clause.respond_to?(:each_pair)
|
33
|
+
result = []
|
34
|
+
@clause.each_pair do |field_name, value|
|
35
|
+
result << "#{field_name} #{operator_for(value)} #{convert_value(value)}"
|
36
|
+
end
|
37
|
+
result.join(" AND ")
|
38
|
+
else
|
39
|
+
@clause.to_s
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def operator_for(value)
|
44
|
+
if value.respond_to?(:keys)
|
45
|
+
OPERATOR_MAPPING[value.keys.first.to_sym]
|
46
|
+
elsif value.respond_to?(:to_a)
|
47
|
+
OPERATOR_MAPPING[:in]
|
48
|
+
else
|
49
|
+
OPERATOR_MAPPING[:eq]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def convert_value(value)
|
54
|
+
if value.respond_to?(:values)
|
55
|
+
attribute_value = value.values.first
|
56
|
+
if value.keys.first == :in
|
57
|
+
convert_value(Array(attribute_value))
|
58
|
+
else
|
59
|
+
convert_value(attribute_value)
|
60
|
+
end
|
61
|
+
elsif value.respond_to?(:to_a)
|
62
|
+
converted_values = value.to_a.map{|value| CQLValue.convert(value)}
|
63
|
+
"(#{converted_values.join(", ")})"
|
64
|
+
else
|
65
|
+
CQLValue.convert(value)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Virsandra
|
2
|
+
class InvalidQuery < Exception; end
|
3
|
+
|
4
|
+
class Query
|
5
|
+
|
6
|
+
attr_reader :row
|
7
|
+
attr_accessor :table, :statement
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
def select(*fields)
|
12
|
+
SelectQuery.new(fields)
|
13
|
+
end
|
14
|
+
|
15
|
+
def insert
|
16
|
+
InsertQuery.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def delete
|
20
|
+
DeleteQuery.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def alter
|
24
|
+
AlterQuery.new
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
raw_query.map{|query_part| query_part.to_s }.reject(&:empty?).join(" ")
|
30
|
+
end
|
31
|
+
|
32
|
+
def from *args
|
33
|
+
raise InvalidQuery.new("From, table or into clause not defined for #{self.class}")
|
34
|
+
end
|
35
|
+
alias_method :table, :from
|
36
|
+
alias_method :into, :from
|
37
|
+
|
38
|
+
def where *args
|
39
|
+
raise InvalidQuery.new("Where clause not defined for #{self.class}")
|
40
|
+
end
|
41
|
+
|
42
|
+
def order *args
|
43
|
+
raise InvalidQuery.new("Order clause not defined for #{self.class}")
|
44
|
+
end
|
45
|
+
|
46
|
+
def limit *args
|
47
|
+
raise InvalidQuery.new("Limit clause not defined for #{self.class}")
|
48
|
+
end
|
49
|
+
|
50
|
+
def add *args
|
51
|
+
raise InvalidQuery.new("Add clause not defined for #{self.class}")
|
52
|
+
end
|
53
|
+
|
54
|
+
def values *args
|
55
|
+
raise InvalidQuery.new("Values clause not defined for #{self.class}")
|
56
|
+
end
|
57
|
+
|
58
|
+
def execute
|
59
|
+
|
60
|
+
@row = Virsandra.execute(self.to_s)
|
61
|
+
end
|
62
|
+
|
63
|
+
def fetch(statement = nil)
|
64
|
+
@raw_query = statement if statement
|
65
|
+
execute
|
66
|
+
fetch_with_symbolized_keys
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def raw_query
|
72
|
+
[@raw_query]
|
73
|
+
end
|
74
|
+
|
75
|
+
def fetch_with_symbolized_keys
|
76
|
+
row_hash = @row && @row.first
|
77
|
+
return {} unless row_hash
|
78
|
+
|
79
|
+
Hash[row_hash.map{|(k,v)| [k.to_sym,v]}]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
Dir[File.expand_path('../queries/*_query.rb', __FILE__)].each do |path|
|
86
|
+
require path
|
87
|
+
end
|