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.
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