virsandra 0.5.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/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
|