virsandra 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/LICENSE.txt +22 -0
  2. data/README.md +93 -0
  3. data/Rakefile +18 -0
  4. data/lib/virsandra.rb +89 -0
  5. data/lib/virsandra/configuration.rb +61 -0
  6. data/lib/virsandra/connection.rb +39 -0
  7. data/lib/virsandra/cql_value.rb +38 -0
  8. data/lib/virsandra/errors.rb +3 -0
  9. data/lib/virsandra/model.rb +86 -0
  10. data/lib/virsandra/model_query.rb +52 -0
  11. data/lib/virsandra/queries/add_query.rb +25 -0
  12. data/lib/virsandra/queries/alter_query.rb +34 -0
  13. data/lib/virsandra/queries/delete_query.rb +25 -0
  14. data/lib/virsandra/queries/insert_query.rb +34 -0
  15. data/lib/virsandra/queries/limit_query.rb +17 -0
  16. data/lib/virsandra/queries/order_query.rb +36 -0
  17. data/lib/virsandra/queries/select_query.rb +72 -0
  18. data/lib/virsandra/queries/table_query.rb +13 -0
  19. data/lib/virsandra/queries/values_query.rb +41 -0
  20. data/lib/virsandra/queries/where_query.rb +69 -0
  21. data/lib/virsandra/query.rb +87 -0
  22. data/lib/virsandra/version.rb +3 -0
  23. data/spec/feature_helper.rb +62 -0
  24. data/spec/integration/virsandra_spec.rb +13 -0
  25. data/spec/lib/virsandra/configuration_spec.rb +66 -0
  26. data/spec/lib/virsandra/connection_spec.rb +47 -0
  27. data/spec/lib/virsandra/cql_value_spec.rb +25 -0
  28. data/spec/lib/virsandra/model_query_spec.rb +58 -0
  29. data/spec/lib/virsandra/model_spec.rb +173 -0
  30. data/spec/lib/virsandra/queries/add_query_spec.rb +26 -0
  31. data/spec/lib/virsandra/queries/alter_query_spec.rb +35 -0
  32. data/spec/lib/virsandra/queries/delete_query_spec.rb +34 -0
  33. data/spec/lib/virsandra/queries/insert_query_spec.rb +36 -0
  34. data/spec/lib/virsandra/queries/limit_query_spec.rb +20 -0
  35. data/spec/lib/virsandra/queries/order_query_spec.rb +33 -0
  36. data/spec/lib/virsandra/queries/select_query_spec.rb +108 -0
  37. data/spec/lib/virsandra/queries/table_query_spec.rb +13 -0
  38. data/spec/lib/virsandra/queries/values_query_spec.rb +41 -0
  39. data/spec/lib/virsandra/queries/where_query_spec.rb +76 -0
  40. data/spec/lib/virsandra/query_spec.rb +117 -0
  41. data/spec/lib/virsandra_spec.rb +108 -0
  42. data/spec/spec_helper.rb +19 -0
  43. 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,13 @@
1
+ module Virsandra
2
+ class TableQuery < Query
3
+ def initialize(keyword, table_name)
4
+ @table_name, @keyword = table_name, keyword
5
+ end
6
+
7
+ private
8
+
9
+ def raw_query
10
+ [@table_name.to_s.empty? ? "" : @keyword, @table_name]
11
+ end
12
+ end
13
+ 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