believer 0.1.4 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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