believer 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/README.md +2 -0
  2. data/lib/believer.rb +42 -0
  3. data/lib/believer/base.rb +45 -0
  4. data/lib/believer/batch.rb +37 -0
  5. data/lib/believer/batch_delete.rb +27 -0
  6. data/lib/believer/callbacks.rb +276 -0
  7. data/lib/believer/columns.rb +166 -0
  8. data/lib/believer/command.rb +44 -0
  9. data/lib/believer/connection.rb +22 -0
  10. data/lib/believer/ddl.rb +36 -0
  11. data/lib/believer/delete.rb +11 -0
  12. data/lib/believer/empty_result.rb +32 -0
  13. data/lib/believer/environment.rb +51 -0
  14. data/lib/believer/environment/rails_environment.rb +19 -0
  15. data/lib/believer/insert.rb +18 -0
  16. data/lib/believer/limit.rb +20 -0
  17. data/lib/believer/model_schema.rb +176 -0
  18. data/lib/believer/observer.rb +36 -0
  19. data/lib/believer/order_by.rb +22 -0
  20. data/lib/believer/owner.rb +48 -0
  21. data/lib/believer/persistence.rb +31 -0
  22. data/lib/believer/primary_key.rb +5 -0
  23. data/lib/believer/query.rb +140 -0
  24. data/lib/believer/querying.rb +6 -0
  25. data/lib/believer/scoped_command.rb +19 -0
  26. data/lib/believer/scoping.rb +16 -0
  27. data/lib/believer/test/rspec/test_run_life_cycle.rb +51 -0
  28. data/lib/believer/values.rb +40 -0
  29. data/lib/believer/version.rb +5 -0
  30. data/lib/believer/where_clause.rb +40 -0
  31. data/spec/believer/base_spec.rb +6 -0
  32. data/spec/believer/callback_spec.rb +49 -0
  33. data/spec/believer/delete_spec.rb +12 -0
  34. data/spec/believer/insert_spec.rb +9 -0
  35. data/spec/believer/limit_spec.rb +7 -0
  36. data/spec/believer/order_by_spec.rb +14 -0
  37. data/spec/believer/query_spec.rb +31 -0
  38. data/spec/believer/time_series_spec.rb +18 -0
  39. data/spec/believer/where_spec.rb +28 -0
  40. data/spec/spec_helper.rb +37 -0
  41. data/spec/support/setup_database.rb +20 -0
  42. data/spec/support/test_classes.rb +60 -0
  43. 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
@@ -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,11 @@
1
+ module Believer
2
+ class Delete < ScopedCommand
3
+
4
+ def to_cql
5
+ cql = "DELETE FROM #{@record_class.table_name}"
6
+ cql << " WHERE #{@wheres.map { |wc| "#{wc.to_cql}" }.join(' AND ')}" if @wheres && @wheres.any?
7
+ cql
8
+ end
9
+
10
+ end
11
+ 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,20 @@
1
+ module Believer
2
+
3
+ class Limit
4
+ attr_reader :size
5
+
6
+ def initialize(l)
7
+ @size = l.to_i
8
+ end
9
+
10
+ def to_cql
11
+ "LIMIT #{@size}"
12
+ end
13
+
14
+ def to_i
15
+ @size
16
+ end
17
+
18
+ end
19
+
20
+ 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