believer 0.1.4 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +168 -53
- data/lib/believer.rb +29 -26
- data/lib/believer/base.rb +2 -0
- data/lib/believer/column.rb +65 -0
- data/lib/believer/columns.rb +3 -62
- data/lib/believer/command.rb +14 -16
- data/lib/believer/connection.rb +25 -6
- data/lib/believer/cql_helper.rb +20 -0
- data/lib/believer/ddl.rb +7 -5
- data/lib/believer/delete.rb +4 -2
- data/lib/believer/environment.rb +43 -13
- data/lib/believer/environment/merb_env.rb +14 -0
- data/lib/believer/environment/rails_env.rb +3 -4
- data/lib/believer/insert.rb +1 -1
- data/lib/believer/model_schema.rb +10 -44
- data/lib/believer/order_by.rb +8 -1
- data/lib/believer/persistence.rb +6 -1
- data/lib/believer/query.rb +3 -3
- data/lib/believer/relation.rb +90 -0
- data/lib/believer/scoped_command.rb +11 -4
- data/lib/believer/test/test_run_life_cycle.rb +49 -0
- data/lib/believer/values.rb +2 -11
- data/lib/believer/version.rb +1 -1
- data/lib/believer/where_clause.rb +1 -1
- data/spec/believer/delete_spec.rb +2 -2
- data/spec/believer/environment_spec.rb +32 -0
- data/spec/believer/insert_spec.rb +2 -2
- data/spec/believer/query_spec.rb +14 -14
- data/spec/believer/relation_spec.rb +33 -0
- data/spec/spec_helper.rb +6 -20
- data/spec/support/setup_database.rb +1 -1
- data/spec/support/test_classes.rb +34 -21
- metadata +27 -7
- data/lib/believer/owner.rb +0 -48
- data/lib/believer/primary_key.rb +0 -5
- data/lib/believer/test/rspec/test_run_life_cycle.rb +0 -51
- data/spec/believer/environment/rails_env_spec.rb +0 -0
data/lib/believer/command.rb
CHANGED
@@ -18,23 +18,21 @@ module Believer
|
|
18
18
|
{:record_class => @record_class}
|
19
19
|
end
|
20
20
|
|
21
|
-
def connection
|
22
|
-
@record_class.connection
|
23
|
-
end
|
24
|
-
|
25
21
|
def execute(name = 'cql.cql_record')
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
22
|
+
@record_class.connection_pool.with do |connection|
|
23
|
+
cql = to_cql
|
24
|
+
begin
|
25
|
+
start = Time.now
|
26
|
+
puts "Executing #{cql}"
|
27
|
+
res = connection.execute(cql)
|
28
|
+
#Rails.logger.debug "#{name} #{sprintf "%.3f", (Time.now - start)*1000.0} ms: #{cql}"
|
29
|
+
return res
|
30
|
+
rescue Cql::Protocol::DecodingError => e
|
31
|
+
# Decoding errors tend to #$%# up the connection, resulting in no more activity, so a reconnect is performed here.
|
32
|
+
# This is a known issue in cql-rb, and will be fixed in version 1.10
|
33
|
+
@record_class.reset_connection(connection)
|
34
|
+
raise e
|
35
|
+
end
|
38
36
|
end
|
39
37
|
|
40
38
|
end
|
data/lib/believer/connection.rb
CHANGED
@@ -1,18 +1,37 @@
|
|
1
|
+
require 'connection_pool'
|
2
|
+
|
1
3
|
module Believer
|
2
4
|
module Connection
|
3
5
|
extend ::ActiveSupport::Concern
|
4
6
|
|
5
7
|
module ClassMethods
|
6
8
|
|
7
|
-
def reset_connection
|
8
|
-
unless
|
9
|
-
|
10
|
-
@client_connection = nil
|
9
|
+
def reset_connection(conn)
|
10
|
+
unless conn.nil?
|
11
|
+
conn.close
|
11
12
|
end
|
12
13
|
end
|
13
14
|
|
14
|
-
def
|
15
|
-
|
15
|
+
def connection_pool
|
16
|
+
Believer::Connection::Pool.instance.connection(environment)
|
17
|
+
#@client_connection ||= environment.create_connection(:connect_to_keyspace => true)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
class Pool
|
23
|
+
include ::Singleton
|
24
|
+
|
25
|
+
# Retrieve a connection from the pool
|
26
|
+
# @param environment [Believer::Environment::BaseEnv] the environment with the connection configuration
|
27
|
+
def connection(environment)
|
28
|
+
unless @connection_pool
|
29
|
+
pool_config = environment.connection_pool_configuration
|
30
|
+
@connection_pool ||= ::ConnectionPool.new(pool_config) do
|
31
|
+
environment.create_connection(:connect_to_keyspace => true)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
@connection_pool
|
16
35
|
end
|
17
36
|
|
18
37
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Believer
|
2
|
+
|
3
|
+
# Contains various methods for dealing with CQL statements
|
4
|
+
module CqlHelper
|
5
|
+
|
6
|
+
CQL_TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S%z'
|
7
|
+
|
8
|
+
# Converts a value to a CQL literal
|
9
|
+
# @param value [Object] the value to convert
|
10
|
+
def to_cql_literal(value)
|
11
|
+
return 'NULL' if value.nil?
|
12
|
+
return "'#{value}'" if value.is_a?(String)
|
13
|
+
return "#{value}" if value.is_a?(Numeric)
|
14
|
+
return "'#{value.strftime(CQL_TIMESTAMP_FORMAT)}'" if value.is_a?(Time) || value.is_a?(DateTime)
|
15
|
+
#return "#{value.to_i * 1000}" if value.is_a?(Time) || value.is_a?(DateTime)
|
16
|
+
return nil
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
data/lib/believer/ddl.rb
CHANGED
@@ -5,15 +5,17 @@ module Believer
|
|
5
5
|
|
6
6
|
module ClassMethods
|
7
7
|
def create_table
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
connection_pool.with do |connection|
|
9
|
+
cql = create_table_cql
|
10
|
+
puts "Creating table #{table_name} using CQL:\n#{cql}"
|
11
|
+
connection.execute(cql)
|
12
|
+
puts "Created table #{table_name}"
|
13
|
+
end
|
12
14
|
end
|
13
15
|
|
14
16
|
def create_table_cql
|
15
17
|
s = "CREATE TABLE #{table_name} (\n"
|
16
|
-
col_statement_parts = columns.keys.map {|col| "#{col} #{columns[col].
|
18
|
+
col_statement_parts = columns.keys.map {|col| "#{col} #{columns[col].cql_type}"}
|
17
19
|
s << col_statement_parts.join(",\n")
|
18
20
|
|
19
21
|
keys = []
|
data/lib/believer/delete.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
module Believer
|
2
|
+
# Creates CQL DELETE statements
|
2
3
|
class Delete < ScopedCommand
|
3
4
|
|
5
|
+
# Creates the CQL
|
4
6
|
def to_cql
|
5
|
-
cql = "DELETE FROM #{
|
6
|
-
cql << " WHERE #{
|
7
|
+
cql = "DELETE FROM #{record_class.table_name}"
|
8
|
+
cql << " WHERE #{wheres.map { |wc| "#{wc.to_cql}" }.join(' AND ')}" if wheres.any?
|
7
9
|
cql
|
8
10
|
end
|
9
11
|
|
data/lib/believer/environment.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#require "believer/environment/rails_env"
|
2
|
+
#require "believer/environment/merb_env"
|
2
3
|
|
3
4
|
module Believer
|
4
5
|
module Environment
|
@@ -7,12 +8,13 @@ module Believer
|
|
7
8
|
module ClassMethods
|
8
9
|
|
9
10
|
def environment
|
10
|
-
|
11
11
|
if @environment.nil?
|
12
12
|
if self.superclass.respond_to?(:environment)
|
13
13
|
@environment = self.superclass.environment
|
14
14
|
elsif defined?(::Rails)
|
15
15
|
@environment = ::Believer::Environment::RailsEnv.new
|
16
|
+
elsif defined?(::Merb)
|
17
|
+
@environment = ::Believer::Environment::MerbEnv.new
|
16
18
|
end
|
17
19
|
end
|
18
20
|
@environment
|
@@ -25,29 +27,41 @@ module Believer
|
|
25
27
|
end
|
26
28
|
|
27
29
|
class BaseEnv
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
|
30
|
+
# Default pool configuration
|
31
|
+
DEFAULT_POOL_CONFIG = {
|
32
|
+
:size => 1,
|
33
|
+
:timeout => 10
|
34
|
+
}
|
35
|
+
|
36
|
+
# Creates a new environment using the provided configuration
|
37
|
+
# @param config [Hash] the configuration
|
32
38
|
def initialize(config = nil)
|
33
39
|
@configuration = config.dup unless config.nil?
|
34
40
|
end
|
35
41
|
|
42
|
+
# Returns the configuration. This configuration hash should contain the cql-rb client connection parameters.
|
43
|
+
# Optionally the connection_pool configuraton can be included in a :pool node.
|
36
44
|
def configuration
|
37
45
|
@configuration ||= load_configuration
|
38
46
|
end
|
39
47
|
|
48
|
+
# Sets the configuration
|
40
49
|
def configuration=(config)
|
41
50
|
@configuration = config
|
42
51
|
end
|
43
52
|
|
44
53
|
def connection_configuration
|
45
|
-
|
54
|
+
configuration.reject {|k, v| k == :pool}
|
55
|
+
end
|
56
|
+
|
57
|
+
# The connection_pool configuration, which should be a :pool node in the configuration.
|
58
|
+
def connection_pool_configuration
|
59
|
+
DEFAULT_POOL_CONFIG.merge(configuration[:pool].is_a?(Hash) ? configuration[:pool].symbolize_keys! : {})
|
46
60
|
end
|
47
61
|
|
62
|
+
# Creates a new connection
|
48
63
|
def create_connection(options = {})
|
49
64
|
cc = connection_configuration
|
50
|
-
|
51
65
|
if options[:connect_to_keyspace] && cc[:keyspace]
|
52
66
|
connection = Cql::Client.connect(cc)
|
53
67
|
connection.use(cc[:keyspace])
|
@@ -57,16 +71,32 @@ module Believer
|
|
57
71
|
connection
|
58
72
|
end
|
59
73
|
|
60
|
-
def create_keyspace(connection = nil)
|
61
|
-
conn = connection || create_connection
|
74
|
+
def create_keyspace(properties = {}, connection = nil)
|
75
|
+
conn = connection || create_connection(:connect_to_keyspace => false)
|
76
|
+
|
77
|
+
default_properties = {:replication => {:class => 'SimpleStrategy', :replication_factor => 1}}
|
78
|
+
ks_props = default_properties.merge(properties)
|
79
|
+
|
80
|
+
ks_props_s = ks_props.keys.map {|k|
|
81
|
+
v = ks_props[k]
|
82
|
+
v_s = nil
|
83
|
+
if v.is_a?(Hash)
|
84
|
+
v_s = v.to_json.gsub(/\"/) {|m| "'"}
|
85
|
+
elsif v.is_a?(String)
|
86
|
+
v_s = "'#{v}'"
|
87
|
+
else
|
88
|
+
v_s = v.to_s
|
89
|
+
end
|
90
|
+
"#{k} = #{v_s}"
|
91
|
+
}.join("\nAND ")
|
92
|
+
|
62
93
|
ks_def = <<-KS_DEF
|
63
94
|
CREATE KEYSPACE #{connection_configuration[:keyspace]}
|
64
|
-
WITH
|
65
|
-
'class': 'SimpleStrategy',
|
66
|
-
'replication_factor': 1
|
67
|
-
}
|
95
|
+
WITH #{ks_props_s}
|
68
96
|
KS_DEF
|
69
97
|
|
98
|
+
puts ks_def
|
99
|
+
|
70
100
|
conn.execute(ks_def)
|
71
101
|
end
|
72
102
|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
module Believer
|
3
|
+
module Environment
|
4
|
+
class MerbEnv < Believer::Environment::BaseEnv
|
5
|
+
|
6
|
+
def load_configuration
|
7
|
+
config_file = File.join(Merb.root, 'config', 'believer.yml')
|
8
|
+
config = HashWithIndifferentAccess.new(YAML::load(File.open(config_file.to_s)))
|
9
|
+
env_config = config[Merb.environment]
|
10
|
+
env_config
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -4,11 +4,10 @@ module Believer
|
|
4
4
|
class RailsEnv < Believer::Environment::BaseEnv
|
5
5
|
|
6
6
|
def load_configuration
|
7
|
-
config_file = Rails.root
|
8
|
-
config = YAML::load(File.open(config_file.to_s))
|
7
|
+
config_file = File.join(Rails.root, 'config', 'believer.yml')
|
8
|
+
config = HashWithIndifferentAccess.new(YAML::load(File.open(config_file.to_s)))
|
9
9
|
env_config = config[Rails.env]
|
10
|
-
env_config =
|
11
|
-
env_config[:logger] = Rails.logger
|
10
|
+
env_config[:logger] = Rails.logger unless Rails.logger.nil?
|
12
11
|
env_config
|
13
12
|
end
|
14
13
|
end
|
data/lib/believer/insert.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
module Believer
|
2
|
+
|
3
|
+
# Mostly *borrowed* from ActiveRecord::ModelSchema ;)
|
2
4
|
module ModelSchema
|
3
5
|
extend ActiveSupport::Concern
|
4
6
|
|
@@ -34,8 +36,8 @@ module Believer
|
|
34
36
|
|
35
37
|
module ClassMethods
|
36
38
|
# Guesses the table name (in forced lower-case) based on the name of the class in the
|
37
|
-
# inheritance hierarchy descending directly from
|
38
|
-
# looks like: Reply < Message <
|
39
|
+
# inheritance hierarchy descending directly from Believer::Base. So if the hierarchy
|
40
|
+
# looks like: Reply < Message < Believer::Base, then Message is used
|
39
41
|
# to guess the table name even when called on Reply. The rules used to do the guess
|
40
42
|
# are handled by the Inflector class in Active Support, which knows almost all common
|
41
43
|
# English inflections. You can add new inflections in config/initializers/inflections.rb.
|
@@ -45,14 +47,14 @@ module Believer
|
|
45
47
|
#
|
46
48
|
# ==== Examples
|
47
49
|
#
|
48
|
-
# class Invoice <
|
50
|
+
# class Invoice < Believer::Base
|
49
51
|
# end
|
50
52
|
#
|
51
53
|
# file class table_name
|
52
54
|
# invoice.rb Invoice invoices
|
53
55
|
#
|
54
|
-
# class Invoice <
|
55
|
-
# class Lineitem <
|
56
|
+
# class Invoice < Believer::Base
|
57
|
+
# class Lineitem < Believer::Base
|
56
58
|
# end
|
57
59
|
# end
|
58
60
|
#
|
@@ -60,7 +62,7 @@ module Believer
|
|
60
62
|
# invoice.rb Invoice::Lineitem invoice_lineitems
|
61
63
|
#
|
62
64
|
# module Invoice
|
63
|
-
# class Lineitem <
|
65
|
+
# class Lineitem < Believer::Base
|
64
66
|
# end
|
65
67
|
# end
|
66
68
|
#
|
@@ -74,7 +76,7 @@ module Believer
|
|
74
76
|
#
|
75
77
|
# You can also set your own table name explicitly:
|
76
78
|
#
|
77
|
-
# class Mouse <
|
79
|
+
# class Mouse < Believer::Base
|
78
80
|
# self.table_name = "mice"
|
79
81
|
# end
|
80
82
|
#
|
@@ -82,7 +84,7 @@ module Believer
|
|
82
84
|
# own computation. (Possibly using <tt>super</tt> to manipulate the default
|
83
85
|
# table name.) Example:
|
84
86
|
#
|
85
|
-
# class Post <
|
87
|
+
# class Post < Believer::Base
|
86
88
|
# def self.table_name
|
87
89
|
# "special_" + super
|
88
90
|
# end
|
@@ -93,10 +95,6 @@ module Believer
|
|
93
95
|
@table_name
|
94
96
|
end
|
95
97
|
|
96
|
-
def original_table_name #:nodoc:
|
97
|
-
deprecated_original_property_getter :table_name
|
98
|
-
end
|
99
|
-
|
100
98
|
# Sets the table name explicitly. Example:
|
101
99
|
#
|
102
100
|
# class Project < ActiveRecord::Base
|
@@ -111,11 +109,6 @@ module Believer
|
|
111
109
|
@quoted_table_name = nil
|
112
110
|
end
|
113
111
|
|
114
|
-
def set_table_name(value = nil, &block) #:nodoc:
|
115
|
-
deprecated_property_setter :table_name, value, block
|
116
|
-
@quoted_table_name = nil
|
117
|
-
end
|
118
|
-
|
119
112
|
# Returns a quoted version of the table name, used to construct SQL statements.
|
120
113
|
def quoted_table_name
|
121
114
|
@quoted_table_name ||= "`#{table_name}`"
|
@@ -144,33 +137,6 @@ module Believer
|
|
144
137
|
"#{full_table_name_prefix}#{undecorated_table_name(name)}#{table_name_suffix}"
|
145
138
|
end
|
146
139
|
|
147
|
-
def deprecated_property_setter(property, value, block)
|
148
|
-
if block
|
149
|
-
ActiveSupport::Deprecation.warn(
|
150
|
-
"Calling set_#{property} is deprecated. If you need to lazily evaluate " \
|
151
|
-
"the #{property}, define your own `self.#{property}` class method. You can use `super` " \
|
152
|
-
"to get the default #{property} where you would have called `original_#{property}`."
|
153
|
-
)
|
154
|
-
|
155
|
-
define_attr_method property, value, false, &block
|
156
|
-
else
|
157
|
-
ActiveSupport::Deprecation.warn(
|
158
|
-
"Calling set_#{property} is deprecated. Please use `self.#{property} = 'the_name'` instead."
|
159
|
-
)
|
160
|
-
|
161
|
-
define_attr_method property, value, false
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
def deprecated_original_property_getter(property)
|
166
|
-
ActiveSupport::Deprecation.warn("original_#{property} is deprecated. Define self.#{property} and call super instead.")
|
167
|
-
|
168
|
-
if !instance_variable_defined?("@original_#{property}") && respond_to?("reset_#{property}")
|
169
|
-
send("reset_#{property}")
|
170
|
-
else
|
171
|
-
instance_variable_get("@original_#{property}")
|
172
|
-
end
|
173
|
-
end
|
174
140
|
end
|
175
141
|
end
|
176
142
|
end
|
data/lib/believer/order_by.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
module Believer
|
2
|
+
|
3
|
+
# Encapsulates the CQL ORDER BY clause
|
2
4
|
class OrderBy
|
5
|
+
|
3
6
|
attr_reader :field, :dir
|
4
7
|
|
8
|
+
# @param field [Symbol] the field to order by
|
9
|
+
# @param dir [Symbol] the order direction. Can be :asc or :desc. Default is :asc
|
5
10
|
def initialize(field, dir = :asc)
|
6
11
|
raise "Invalid field: #{field}" unless field.is_a?(Symbol) || field.is_a?(String)
|
7
12
|
raise "Direction must be one of (:asc|:desc): #{dir}" unless dir == :asc || dir == :desc
|
@@ -9,10 +14,12 @@ module Believer
|
|
9
14
|
@dir = dir
|
10
15
|
end
|
11
16
|
|
17
|
+
# Creates the CQL ORDER BY clause
|
12
18
|
def to_cql
|
13
|
-
"ORDER BY #{@field} #{@dir.
|
19
|
+
"ORDER BY #{@field} #{@dir.to_s.upcase}"
|
14
20
|
end
|
15
21
|
|
22
|
+
# Inverts the direction of the order
|
16
23
|
def inverse
|
17
24
|
OrderBy.new(@field, @dir == :asc ? :desc : :asc)
|
18
25
|
end
|
data/lib/believer/persistence.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
module Believer
|
2
2
|
|
3
|
+
# Defines persistence functionality for a class
|
3
4
|
module Persistence
|
4
|
-
|
5
5
|
extend ::ActiveSupport::Concern
|
6
6
|
|
7
7
|
module ClassMethods
|
8
8
|
|
9
|
+
# Creates 1 or more new instances, and persists them to the database.
|
10
|
+
# An optional block can be provided which is called for each created model.
|
11
|
+
#
|
12
|
+
# @param attributes [Enumerable] the attributes. If this is an array, it is assumed multiple models should be created
|
9
13
|
def create(attributes = nil, &block)
|
10
14
|
if attributes.is_a?(Array)
|
11
15
|
attributes.collect { |attr| create(attr, &block) }
|
@@ -23,6 +27,7 @@ module Believer
|
|
23
27
|
Insert.new(:record_class => self.class, :values => self).execute
|
24
28
|
end
|
25
29
|
|
30
|
+
# Destroys the model.
|
26
31
|
def destroy
|
27
32
|
Delete.new(:record_class => self.class).where(key_values).execute
|
28
33
|
end
|