believer 0.1.4 → 0.2

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.
@@ -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
- 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
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
@@ -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 connection.nil?
9
- connection.close
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 connection
15
- @client_connection ||= environment.create_connection(:connect_to_keyspace => true)
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
@@ -5,15 +5,17 @@ module Believer
5
5
 
6
6
  module ClassMethods
7
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}"
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].cql_column_type}"}
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 = []
@@ -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 #{@record_class.table_name}"
6
- cql << " WHERE #{@wheres.map { |wc| "#{wc.to_cql}" }.join(' AND ')}" if @wheres && @wheres.any?
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
 
@@ -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
- #def connection_configuration
29
- # {:host => '127.0.0.1'}
30
- #end
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
- @connection_configuration ||= configuration.reject {|k, v| k == :pool}
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 replication = {
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 + "config/believer.yml"
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 = {}.merge(env_config).symbolize_keys
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
@@ -1,7 +1,7 @@
1
1
  module Believer
2
2
 
3
3
  class Insert < Command
4
- include Values
4
+ include CqlHelper
5
5
 
6
6
  attr_accessor :values
7
7
 
@@ -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 ActiveRecord::Base. So if the hierarchy
38
- # looks like: Reply < Message < ActiveRecord::Base, then Message is used
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 < ActiveRecord::Base
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 < ActiveRecord::Base
55
- # class Lineitem < ActiveRecord::Base
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 < ActiveRecord::Base
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 < ActiveRecord::Base
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 < ActiveRecord::Base
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
@@ -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.present? ? @dir.to_s.upcase : 'ASC'}"
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
@@ -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