believer 0.1
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/README.md +2 -0
- data/lib/believer.rb +42 -0
- data/lib/believer/base.rb +45 -0
- data/lib/believer/batch.rb +37 -0
- data/lib/believer/batch_delete.rb +27 -0
- data/lib/believer/callbacks.rb +276 -0
- data/lib/believer/columns.rb +166 -0
- data/lib/believer/command.rb +44 -0
- data/lib/believer/connection.rb +22 -0
- data/lib/believer/ddl.rb +36 -0
- data/lib/believer/delete.rb +11 -0
- data/lib/believer/empty_result.rb +32 -0
- data/lib/believer/environment.rb +51 -0
- data/lib/believer/environment/rails_environment.rb +19 -0
- data/lib/believer/insert.rb +18 -0
- data/lib/believer/limit.rb +20 -0
- data/lib/believer/model_schema.rb +176 -0
- data/lib/believer/observer.rb +36 -0
- data/lib/believer/order_by.rb +22 -0
- data/lib/believer/owner.rb +48 -0
- data/lib/believer/persistence.rb +31 -0
- data/lib/believer/primary_key.rb +5 -0
- data/lib/believer/query.rb +140 -0
- data/lib/believer/querying.rb +6 -0
- data/lib/believer/scoped_command.rb +19 -0
- data/lib/believer/scoping.rb +16 -0
- data/lib/believer/test/rspec/test_run_life_cycle.rb +51 -0
- data/lib/believer/values.rb +40 -0
- data/lib/believer/version.rb +5 -0
- data/lib/believer/where_clause.rb +40 -0
- data/spec/believer/base_spec.rb +6 -0
- data/spec/believer/callback_spec.rb +49 -0
- data/spec/believer/delete_spec.rb +12 -0
- data/spec/believer/insert_spec.rb +9 -0
- data/spec/believer/limit_spec.rb +7 -0
- data/spec/believer/order_by_spec.rb +14 -0
- data/spec/believer/query_spec.rb +31 -0
- data/spec/believer/time_series_spec.rb +18 -0
- data/spec/believer/where_spec.rb +28 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/support/setup_database.rb +20 -0
- data/spec/support/test_classes.rb +60 -0
- metadata +180 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
module Believer
|
2
|
+
class Command
|
3
|
+
|
4
|
+
attr_accessor :record_class
|
5
|
+
|
6
|
+
def initialize(attrs = {})
|
7
|
+
attrs.each do |name, value|
|
8
|
+
send("#{name}=", value)
|
9
|
+
end if attrs.present?
|
10
|
+
#@instrumenter = ActiveSupport::Notifications.instrumenter
|
11
|
+
end
|
12
|
+
|
13
|
+
def clone
|
14
|
+
self.class.new(query_attributes)
|
15
|
+
end
|
16
|
+
|
17
|
+
def query_attributes
|
18
|
+
{:record_class => @record_class}
|
19
|
+
end
|
20
|
+
|
21
|
+
def connection
|
22
|
+
@record_class.connection
|
23
|
+
end
|
24
|
+
|
25
|
+
def execute(name = 'cql.cql_record')
|
26
|
+
cql = to_cql
|
27
|
+
begin
|
28
|
+
start = Time.now
|
29
|
+
puts "Executing #{cql}"
|
30
|
+
res = connection.execute(cql)
|
31
|
+
#Rails.logger.debug "#{name} #{sprintf "%.3f", (Time.now - start)*1000.0} ms: #{cql}"
|
32
|
+
return res
|
33
|
+
rescue Cql::Protocol::DecodingError => e
|
34
|
+
# Decoding errors tend to #$%# up the connection, resulting in no more activity, so a reconnect is performed here.
|
35
|
+
# This is a known issue in cql-rb, and will be fixed in version 1.10
|
36
|
+
@record_class.reset_connection
|
37
|
+
raise e
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Believer
|
2
|
+
module Connection
|
3
|
+
extend ::ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
def reset_connection
|
8
|
+
unless connection.nil?
|
9
|
+
connection.close
|
10
|
+
@client_connection = nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def connection
|
15
|
+
@client_connection ||= environment.create_connection(:connect_to_keyspace => true)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/lib/believer/ddl.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Believer
|
2
|
+
|
3
|
+
module DDL
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def create_table
|
8
|
+
cql = create_table_cql
|
9
|
+
puts "Creating table #{table_name} using CQL:\n#{cql}"
|
10
|
+
connection.execute(cql)
|
11
|
+
puts "Created table #{table_name}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_table_cql
|
15
|
+
s = "CREATE TABLE #{table_name} (\n"
|
16
|
+
col_statement_parts = columns.keys.map {|col| "#{col} #{columns[col].cql_column_type}"}
|
17
|
+
s << col_statement_parts.join(",\n")
|
18
|
+
|
19
|
+
keys = []
|
20
|
+
get_primary_key.each do |key_part|
|
21
|
+
if key_part.is_a?(Enumerable)
|
22
|
+
keys << "(#{key_part.join(',')})"
|
23
|
+
else
|
24
|
+
keys << key_part
|
25
|
+
end
|
26
|
+
end
|
27
|
+
s << ",\n"
|
28
|
+
s << "PRIMARY KEY (#{keys.join(',')})"
|
29
|
+
s << "\n)"
|
30
|
+
s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Believer
|
2
|
+
|
3
|
+
class EmptyResult
|
4
|
+
DATA_METHODS = {
|
5
|
+
:execute => [],
|
6
|
+
:destroy_all => nil,
|
7
|
+
:delete_all => 0,
|
8
|
+
:to_a => [],
|
9
|
+
:size => 0,
|
10
|
+
:count => 0,
|
11
|
+
:each => nil,
|
12
|
+
:first => nil,
|
13
|
+
:last => nil,
|
14
|
+
:any? => false
|
15
|
+
}
|
16
|
+
QUERY_METHODS = [:select, :where, :order, :limit]
|
17
|
+
|
18
|
+
DATA_METHODS.each do |method_name, return_val|
|
19
|
+
define_method(method_name) do |*|
|
20
|
+
return_val
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
QUERY_METHODS.each do |method_name|
|
25
|
+
define_method(method_name) do |*|
|
26
|
+
self
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Believer
|
2
|
+
module Environment
|
3
|
+
extend ::ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
def environment
|
8
|
+
if @environment.nil?
|
9
|
+
if defined?(Rails)
|
10
|
+
@environment = RailsEnvironment.new
|
11
|
+
end
|
12
|
+
end
|
13
|
+
@environment
|
14
|
+
end
|
15
|
+
|
16
|
+
def environment=(env)
|
17
|
+
@environment = env
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
class Base
|
23
|
+
def create_connection(options = {})
|
24
|
+
cc = connection_configuration
|
25
|
+
|
26
|
+
if options[:connect_to_keyspace] && cc[:keyspace]
|
27
|
+
connection = Cql::Client.connect(cc)
|
28
|
+
connection.use(cc[:keyspace])
|
29
|
+
else
|
30
|
+
connection = Cql::Client.connect(cc.delete_if {|k,v|k==:keyspace})
|
31
|
+
end
|
32
|
+
connection
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_keyspace(connection = nil)
|
36
|
+
conn = connection || create_connection
|
37
|
+
ks_def = <<-KSDEF
|
38
|
+
CREATE KEYSPACE #{connection_configuration[:keyspace]}
|
39
|
+
WITH replication = {
|
40
|
+
'class': 'SimpleStrategy',
|
41
|
+
'replication_factor': 1
|
42
|
+
}
|
43
|
+
KSDEF
|
44
|
+
|
45
|
+
conn.execute(ks_def)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
module Believer
|
3
|
+
module Environment
|
4
|
+
class RailsEnvironment < Base
|
5
|
+
|
6
|
+
def connection_configuration
|
7
|
+
unless @connection_configuration
|
8
|
+
config_file = Rails.root + "config/cql.yml"
|
9
|
+
#puts "Using CQL connection config file: #{config_file}"
|
10
|
+
config = YAML::load(File.open(config_file.to_s))
|
11
|
+
env_config = config[Rails.env]
|
12
|
+
@connection_configuration = env_config.symbolize_keys
|
13
|
+
@connection_configuration[:logger] = Rails.logger
|
14
|
+
end
|
15
|
+
@connection_configuration
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Believer
|
2
|
+
|
3
|
+
class Insert < Command
|
4
|
+
include Values
|
5
|
+
|
6
|
+
attr_accessor :values
|
7
|
+
|
8
|
+
def values=(v)
|
9
|
+
@values = v.is_a?(Base) ? v.attributes : v.to_hash
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_cql
|
13
|
+
attrs = @values.keys
|
14
|
+
"INSERT INTO #{@record_class.table_name} (#{attrs.join(', ')}) VALUES (#{attrs.map {|a| to_cql_literal(@values[a]) }.join(', ')})"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
module Believer
|
2
|
+
module ModelSchema
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
##
|
7
|
+
# :singleton-method:
|
8
|
+
# Accessor for the name of the prefix string to prepend to every table name. So if set
|
9
|
+
# to "basecamp_", all table names will be named like "basecamp_projects", "basecamp_people",
|
10
|
+
# etc. This is a convenient way of creating a namespace for tables in a shared database.
|
11
|
+
# By default, the prefix is the empty string.
|
12
|
+
#
|
13
|
+
# If you are organising your models within modules you can add a prefix to the models within
|
14
|
+
# a namespace by defining a singleton method in the parent module called table_name_prefix which
|
15
|
+
# returns your chosen prefix.
|
16
|
+
class_attribute :table_name_prefix, :instance_writer => false
|
17
|
+
self.table_name_prefix = ""
|
18
|
+
|
19
|
+
##
|
20
|
+
# :singleton-method:
|
21
|
+
# Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
|
22
|
+
# "people_basecamp"). By default, the suffix is the empty string.
|
23
|
+
class_attribute :table_name_suffix, :instance_writer => false
|
24
|
+
self.table_name_suffix = ""
|
25
|
+
|
26
|
+
##
|
27
|
+
# :singleton-method:
|
28
|
+
# Indicates whether table names should be the pluralized versions of the corresponding class names.
|
29
|
+
# If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
|
30
|
+
# See table_name for the full rules on table/class naming. This is true, by default.
|
31
|
+
class_attribute :pluralize_table_names, :instance_writer => false
|
32
|
+
self.pluralize_table_names = true
|
33
|
+
end
|
34
|
+
|
35
|
+
module ClassMethods
|
36
|
+
# Guesses the table name (in forced lower-case) based on the name of the class in the
|
37
|
+
# inheritance hierarchy descending directly from ActiveRecord::Base. So if the hierarchy
|
38
|
+
# looks like: Reply < Message < ActiveRecord::Base, then Message is used
|
39
|
+
# to guess the table name even when called on Reply. The rules used to do the guess
|
40
|
+
# are handled by the Inflector class in Active Support, which knows almost all common
|
41
|
+
# English inflections. You can add new inflections in config/initializers/inflections.rb.
|
42
|
+
#
|
43
|
+
# Nested classes are given table names prefixed by the singular form of
|
44
|
+
# the parent's table name. Enclosing modules are not considered.
|
45
|
+
#
|
46
|
+
# ==== Examples
|
47
|
+
#
|
48
|
+
# class Invoice < ActiveRecord::Base
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# file class table_name
|
52
|
+
# invoice.rb Invoice invoices
|
53
|
+
#
|
54
|
+
# class Invoice < ActiveRecord::Base
|
55
|
+
# class Lineitem < ActiveRecord::Base
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# file class table_name
|
60
|
+
# invoice.rb Invoice::Lineitem invoice_lineitems
|
61
|
+
#
|
62
|
+
# module Invoice
|
63
|
+
# class Lineitem < ActiveRecord::Base
|
64
|
+
# end
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# file class table_name
|
68
|
+
# invoice/lineitem.rb Invoice::Lineitem lineitems
|
69
|
+
#
|
70
|
+
# Additionally, the class-level +table_name_prefix+ is prepended and the
|
71
|
+
# +table_name_suffix+ is appended. So if you have "myapp_" as a prefix,
|
72
|
+
# the table name guess for an Invoice class becomes "myapp_invoices".
|
73
|
+
# Invoice::Lineitem becomes "myapp_invoice_lineitems".
|
74
|
+
#
|
75
|
+
# You can also set your own table name explicitly:
|
76
|
+
#
|
77
|
+
# class Mouse < ActiveRecord::Base
|
78
|
+
# self.table_name = "mice"
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# Alternatively, you can override the table_name method to define your
|
82
|
+
# own computation. (Possibly using <tt>super</tt> to manipulate the default
|
83
|
+
# table name.) Example:
|
84
|
+
#
|
85
|
+
# class Post < ActiveRecord::Base
|
86
|
+
# def self.table_name
|
87
|
+
# "special_" + super
|
88
|
+
# end
|
89
|
+
# end
|
90
|
+
# Post.table_name # => "special_posts"
|
91
|
+
def table_name
|
92
|
+
reset_table_name unless defined?(@table_name)
|
93
|
+
@table_name
|
94
|
+
end
|
95
|
+
|
96
|
+
def original_table_name #:nodoc:
|
97
|
+
deprecated_original_property_getter :table_name
|
98
|
+
end
|
99
|
+
|
100
|
+
# Sets the table name explicitly. Example:
|
101
|
+
#
|
102
|
+
# class Project < ActiveRecord::Base
|
103
|
+
# self.table_name = "project"
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# You can also just define your own <tt>self.table_name</tt> method; see
|
107
|
+
# the documentation for ActiveRecord::Base#table_name.
|
108
|
+
def table_name=(value)
|
109
|
+
@original_table_name = @table_name if defined?(@table_name)
|
110
|
+
@table_name = value && value.to_s
|
111
|
+
@quoted_table_name = nil
|
112
|
+
end
|
113
|
+
|
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
|
+
# Returns a quoted version of the table name, used to construct SQL statements.
|
120
|
+
def quoted_table_name
|
121
|
+
@quoted_table_name ||= "`#{table_name}`"
|
122
|
+
end
|
123
|
+
|
124
|
+
# Computes the table name, (re)sets it internally, and returns it.
|
125
|
+
def reset_table_name #:nodoc:
|
126
|
+
self.table_name = compute_table_name
|
127
|
+
end
|
128
|
+
|
129
|
+
def full_table_name_prefix #:nodoc:
|
130
|
+
(parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
# Guesses the table name, but does not decorate it with prefix and suffix information.
|
136
|
+
def undecorated_table_name(class_name = base_class.name)
|
137
|
+
table_name = class_name.to_s.demodulize.underscore
|
138
|
+
table_name = table_name.pluralize if pluralize_table_names
|
139
|
+
table_name
|
140
|
+
end
|
141
|
+
|
142
|
+
# Computes and returns a table name according to default conventions.
|
143
|
+
def compute_table_name
|
144
|
+
"#{full_table_name_prefix}#{undecorated_table_name(name)}#{table_name_suffix}"
|
145
|
+
end
|
146
|
+
|
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
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'active_model/observing'
|
2
|
+
|
3
|
+
module Believer
|
4
|
+
|
5
|
+
class Observer < ::ActiveModel::Observer
|
6
|
+
|
7
|
+
protected
|
8
|
+
|
9
|
+
def observed_classes
|
10
|
+
klasses = super
|
11
|
+
klasses + klasses.map { |klass| klass.descendants }.flatten
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_observer!(klass)
|
15
|
+
super
|
16
|
+
define_callbacks klass
|
17
|
+
end
|
18
|
+
|
19
|
+
def define_callbacks(klass)
|
20
|
+
observer = self
|
21
|
+
observer_name = observer.class.name.underscore.gsub('/', '__')
|
22
|
+
|
23
|
+
Believer::Callbacks::CALLBACKS.each do |callback|
|
24
|
+
next unless respond_to?(callback)
|
25
|
+
callback_meth = :"_notify_#{observer_name}_for_#{callback}"
|
26
|
+
unless klass.respond_to?(callback_meth)
|
27
|
+
klass.send(:define_method, callback_meth) do |&block|
|
28
|
+
observer.update(callback, self, &block)
|
29
|
+
end
|
30
|
+
klass.send(callback, callback_meth)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|