bigrecord 0.0.5

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.
Files changed (104) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +44 -0
  3. data/Rakefile +17 -0
  4. data/VERSION +1 -0
  5. data/doc/bigrecord_specs.rdoc +36 -0
  6. data/doc/getting_started.rdoc +157 -0
  7. data/examples/bigrecord.yml +25 -0
  8. data/generators/bigrecord/bigrecord_generator.rb +17 -0
  9. data/generators/bigrecord/templates/bigrecord.rake +47 -0
  10. data/generators/bigrecord_migration/bigrecord_migration_generator.rb +13 -0
  11. data/generators/bigrecord_migration/templates/migration.rb +9 -0
  12. data/generators/bigrecord_model/bigrecord_model_generator.rb +28 -0
  13. data/generators/bigrecord_model/templates/migration.rb +13 -0
  14. data/generators/bigrecord_model/templates/model.rb +7 -0
  15. data/generators/bigrecord_model/templates/model_spec.rb +12 -0
  16. data/init.rb +9 -0
  17. data/install.rb +22 -0
  18. data/lib/big_record/abstract_base.rb +1088 -0
  19. data/lib/big_record/action_view_extensions.rb +266 -0
  20. data/lib/big_record/ar_associations/association_collection.rb +194 -0
  21. data/lib/big_record/ar_associations/association_proxy.rb +158 -0
  22. data/lib/big_record/ar_associations/belongs_to_association.rb +57 -0
  23. data/lib/big_record/ar_associations/belongs_to_many_association.rb +57 -0
  24. data/lib/big_record/ar_associations/has_and_belongs_to_many_association.rb +164 -0
  25. data/lib/big_record/ar_associations/has_many_association.rb +191 -0
  26. data/lib/big_record/ar_associations/has_one_association.rb +80 -0
  27. data/lib/big_record/ar_associations.rb +1608 -0
  28. data/lib/big_record/ar_reflection.rb +223 -0
  29. data/lib/big_record/attribute_methods.rb +75 -0
  30. data/lib/big_record/base.rb +618 -0
  31. data/lib/big_record/br_associations/association_collection.rb +194 -0
  32. data/lib/big_record/br_associations/association_proxy.rb +153 -0
  33. data/lib/big_record/br_associations/belongs_to_association.rb +52 -0
  34. data/lib/big_record/br_associations/belongs_to_many_association.rb +293 -0
  35. data/lib/big_record/br_associations/cached_item_proxy.rb +194 -0
  36. data/lib/big_record/br_associations/cached_item_proxy_factory.rb +62 -0
  37. data/lib/big_record/br_associations/has_and_belongs_to_many_association.rb +168 -0
  38. data/lib/big_record/br_associations/has_one_association.rb +80 -0
  39. data/lib/big_record/br_associations.rb +978 -0
  40. data/lib/big_record/br_reflection.rb +151 -0
  41. data/lib/big_record/callbacks.rb +367 -0
  42. data/lib/big_record/connection_adapters/abstract/connection_specification.rb +279 -0
  43. data/lib/big_record/connection_adapters/abstract/database_statements.rb +175 -0
  44. data/lib/big_record/connection_adapters/abstract/quoting.rb +58 -0
  45. data/lib/big_record/connection_adapters/abstract_adapter.rb +190 -0
  46. data/lib/big_record/connection_adapters/column.rb +491 -0
  47. data/lib/big_record/connection_adapters/hbase_adapter.rb +432 -0
  48. data/lib/big_record/connection_adapters/view.rb +27 -0
  49. data/lib/big_record/connection_adapters.rb +10 -0
  50. data/lib/big_record/deletion.rb +73 -0
  51. data/lib/big_record/dynamic_schema.rb +92 -0
  52. data/lib/big_record/embedded.rb +71 -0
  53. data/lib/big_record/embedded_associations/association_proxy.rb +148 -0
  54. data/lib/big_record/family_span_columns.rb +89 -0
  55. data/lib/big_record/fixtures.rb +1025 -0
  56. data/lib/big_record/migration.rb +380 -0
  57. data/lib/big_record/routing_ext.rb +65 -0
  58. data/lib/big_record/timestamp.rb +51 -0
  59. data/lib/big_record/validations.rb +830 -0
  60. data/lib/big_record.rb +125 -0
  61. data/lib/bigrecord.rb +1 -0
  62. data/rails/init.rb +9 -0
  63. data/spec/connections/bigrecord.yml +13 -0
  64. data/spec/connections/cassandra/connection.rb +2 -0
  65. data/spec/connections/hbase/connection.rb +2 -0
  66. data/spec/debug.log +281 -0
  67. data/spec/integration/br_associations_spec.rb +80 -0
  68. data/spec/lib/animal.rb +12 -0
  69. data/spec/lib/book.rb +10 -0
  70. data/spec/lib/broken_migrations/duplicate_name/20090706182535_add_animals_table.rb +14 -0
  71. data/spec/lib/broken_migrations/duplicate_name/20090706193019_add_animals_table.rb +9 -0
  72. data/spec/lib/broken_migrations/duplicate_version/20090706190623_add_books_table.rb +9 -0
  73. data/spec/lib/broken_migrations/duplicate_version/20090706190623_add_companies_table.rb +9 -0
  74. data/spec/lib/company.rb +14 -0
  75. data/spec/lib/embedded/web_link.rb +12 -0
  76. data/spec/lib/employee.rb +33 -0
  77. data/spec/lib/migrations/20090706182535_add_animals_table.rb +13 -0
  78. data/spec/lib/migrations/20090706190623_add_books_table.rb +15 -0
  79. data/spec/lib/migrations/20090706193019_add_companies_table.rb +14 -0
  80. data/spec/lib/migrations/20090706194512_add_employees_table.rb +13 -0
  81. data/spec/lib/migrations/20090706195741_add_zoos_table.rb +13 -0
  82. data/spec/lib/novel.rb +5 -0
  83. data/spec/lib/zoo.rb +17 -0
  84. data/spec/spec.opts +4 -0
  85. data/spec/spec_helper.rb +55 -0
  86. data/spec/unit/abstract_base_spec.rb +287 -0
  87. data/spec/unit/adapters/abstract_adapter_spec.rb +56 -0
  88. data/spec/unit/adapters/adapter_shared_spec.rb +51 -0
  89. data/spec/unit/adapters/hbase_adapter_spec.rb +15 -0
  90. data/spec/unit/ar_associations_spec.rb +8 -0
  91. data/spec/unit/base_spec.rb +6 -0
  92. data/spec/unit/br_associations_spec.rb +58 -0
  93. data/spec/unit/embedded_spec.rb +43 -0
  94. data/spec/unit/find_spec.rb +34 -0
  95. data/spec/unit/hash_helper_spec.rb +44 -0
  96. data/spec/unit/migration_spec.rb +144 -0
  97. data/spec/unit/model_spec.rb +315 -0
  98. data/spec/unit/validations_spec.rb +182 -0
  99. data/tasks/bigrecord_tasks.rake +47 -0
  100. data/tasks/data_store.rb +46 -0
  101. data/tasks/gem.rb +22 -0
  102. data/tasks/rdoc.rb +8 -0
  103. data/tasks/spec.rb +34 -0
  104. metadata +189 -0
@@ -0,0 +1,279 @@
1
+ require 'set'
2
+
3
+ module BigRecord
4
+ class Base
5
+ class ConnectionSpecification #:nodoc:
6
+ attr_reader :config, :adapter_method
7
+ def initialize (config, adapter_method)
8
+ @config, @adapter_method = config, adapter_method
9
+ end
10
+ end
11
+
12
+ # Check for activity after at least +verification_timeout+ seconds.
13
+ # Defaults to 0 (always check.)
14
+ cattr_accessor :verification_timeout, :instance_writer => false
15
+ @@verification_timeout = 0
16
+
17
+ # The class -> [adapter_method, config] map
18
+ @@defined_connections = {}
19
+
20
+ # The class -> thread id -> adapter cache. (class -> adapter if not allow_concurrency)
21
+ @@active_connections = {}
22
+
23
+ class << self
24
+ # Retrieve the connection cache.
25
+ def thread_safe_active_connections #:nodoc:
26
+ @@active_connections[Thread.current.object_id] ||= {}
27
+ end
28
+
29
+ def single_threaded_active_connections #:nodoc:
30
+ @@active_connections
31
+ end
32
+
33
+ # pick up the right active_connection method from @@allow_concurrency
34
+ if @@allow_concurrency
35
+ alias_method :active_connections, :thread_safe_active_connections
36
+ else
37
+ alias_method :active_connections, :single_threaded_active_connections
38
+ end
39
+
40
+ # set concurrency support flag (not thread safe, like most of the methods in this file)
41
+ def allow_concurrency=(threaded) #:nodoc:
42
+ logger.debug "allow_concurrency=#{threaded}" if logger
43
+ return if @@allow_concurrency == threaded
44
+ clear_all_cached_connections!
45
+ @@allow_concurrency = threaded
46
+ method_prefix = threaded ? "thread_safe" : "single_threaded"
47
+ sing = (class << self; self; end)
48
+ [:active_connections, :scoped_methods].each do |method|
49
+ sing.send(:alias_method, method, "#{method_prefix}_#{method}")
50
+ end
51
+ log_connections if logger
52
+ end
53
+
54
+ def active_connection_name #:nodoc:
55
+ @active_connection_name ||=
56
+ if active_connections[name] || @@defined_connections[name]
57
+ name
58
+ elsif self == BigRecord::Base
59
+ nil
60
+ else
61
+ superclass.active_connection_name
62
+ end
63
+ end
64
+
65
+ def clear_active_connection_name #:nodoc:
66
+ @active_connection_name = nil
67
+ subclasses.each { |klass| klass.clear_active_connection_name }
68
+ end
69
+
70
+ # Returns the connection currently associated with the class. This can
71
+ # also be used to "borrow" the connection to do database work unrelated
72
+ # to any of the specific Active Records.
73
+ def connection
74
+ if @active_connection_name && (conn = active_connections[@active_connection_name])
75
+ conn
76
+ else
77
+ # retrieve_connection sets the cache key.
78
+ conn = retrieve_connection
79
+ active_connections[@active_connection_name] = conn
80
+ end
81
+ end
82
+
83
+ # Clears the cache which maps classes to connections.
84
+ def clear_active_connections!
85
+ clear_cache!(@@active_connections) do |name, conn|
86
+ conn.disconnect!
87
+ end
88
+ end
89
+
90
+ # Clears the cache which maps classes
91
+ def clear_reloadable_connections!
92
+ @@active_connections.each do |name, conn|
93
+ if conn.requires_reloading?
94
+ conn.disconnect!
95
+ @@active_connections.delete(name)
96
+ end
97
+ end
98
+ end
99
+
100
+ # Verify active connections.
101
+ def verify_active_connections! #:nodoc:
102
+ if @@allow_concurrency
103
+ remove_stale_cached_threads!(@@active_connections) do |name, conn|
104
+ conn.disconnect!
105
+ end
106
+ end
107
+
108
+ active_connections.each_value do |connection|
109
+ connection.verify!(@@verification_timeout)
110
+ end
111
+ end
112
+
113
+ private
114
+ def clear_cache!(cache, thread_id = nil, &block)
115
+ if cache
116
+ if @@allow_concurrency
117
+ thread_id ||= Thread.current.object_id
118
+ thread_cache, cache = cache, cache[thread_id]
119
+ return unless cache
120
+ end
121
+
122
+ cache.each(&block) if block_given?
123
+ cache.clear
124
+ end
125
+ ensure
126
+ if thread_cache && @@allow_concurrency
127
+ thread_cache.delete(thread_id)
128
+ end
129
+ end
130
+
131
+ # Remove stale threads from the cache.
132
+ def remove_stale_cached_threads!(cache, &block)
133
+ stale = Set.new(cache.keys)
134
+
135
+ Thread.list.each do |thread|
136
+ stale.delete(thread.object_id) if thread.alive?
137
+ end
138
+
139
+ stale.each do |thread_id|
140
+ clear_cache!(cache, thread_id, &block)
141
+ end
142
+ end
143
+
144
+ def clear_all_cached_connections!
145
+ if @@allow_concurrency
146
+ @@active_connections.each_value do |connection_hash_for_thread|
147
+ connection_hash_for_thread.each_value {|conn| conn.disconnect! }
148
+ connection_hash_for_thread.clear
149
+ end
150
+ else
151
+ @@active_connections.each_value {|conn| conn.disconnect! }
152
+ end
153
+ @@active_connections.clear
154
+ end
155
+ end
156
+
157
+ # Returns the connection currently associated with the class. This can
158
+ # also be used to "borrow" the connection to do database work that isn't
159
+ # easily done without going straight to SQL.
160
+ def connection
161
+ self.class.connection
162
+ end
163
+
164
+ # Establishes the connection to the database. Accepts a hash as input where
165
+ # the :adapter key must be specified with the name of a database adapter (in lower-case)
166
+ # example for regular databases (MySQL, Postgresql, etc):
167
+ #
168
+ # ActiveRecord::Base.establish_connection(
169
+ # :adapter => "mysql",
170
+ # :host => "localhost",
171
+ # :username => "myuser",
172
+ # :password => "mypass",
173
+ # :database => "somedatabase"
174
+ # )
175
+ #
176
+ # Example for SQLite database:
177
+ #
178
+ # ActiveRecord::Base.establish_connection(
179
+ # :adapter => "sqlite",
180
+ # :database => "path/to/dbfile"
181
+ # )
182
+ #
183
+ # Also accepts keys as strings (for parsing from yaml for example):
184
+ # ActiveRecord::Base.establish_connection(
185
+ # "adapter" => "sqlite",
186
+ # "database" => "path/to/dbfile"
187
+ # )
188
+ #
189
+ # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
190
+ # may be returned on an error.
191
+ def self.establish_connection(spec = nil)
192
+ case spec
193
+ when nil
194
+ raise AdapterNotSpecified unless defined? RAILS_ENV
195
+ establish_connection(RAILS_ENV)
196
+ when ConnectionSpecification
197
+ clear_active_connection_name
198
+ @active_connection_name = name
199
+ @@defined_connections[name] = spec
200
+ when Symbol, String
201
+ if configuration = configurations[spec.to_s]
202
+ establish_connection(configuration)
203
+ else
204
+ raise AdapterNotSpecified, "#{spec} database is not configured"
205
+ end
206
+ else
207
+ spec = spec.symbolize_keys
208
+ unless spec.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end
209
+ adapter_method = "#{spec[:adapter]}_connection"
210
+ unless respond_to?(adapter_method) then raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter" end
211
+ remove_connection
212
+ establish_connection(ConnectionSpecification.new(spec, adapter_method))
213
+ end
214
+ end
215
+
216
+ # Locate the connection of the nearest super class. This can be an
217
+ # active or defined connections: if it is the latter, it will be
218
+ # opened and set as the active connection for the class it was defined
219
+ # for (not necessarily the current class).
220
+ def self.retrieve_connection #:nodoc:
221
+ # Name is nil if establish_connection hasn't been called for
222
+ # some class along the inheritance chain up to AR::Base yet.
223
+ if name = active_connection_name
224
+ if conn = active_connections[name]
225
+ # Verify the connection.
226
+ conn.verify!(@@verification_timeout)
227
+ elsif spec = @@defined_connections[name]
228
+ # Activate this connection specification.
229
+ klass = name.constantize
230
+ klass.connection = spec
231
+ conn = active_connections[name]
232
+ end
233
+ end
234
+
235
+ conn or raise ConnectionNotEstablished
236
+ end
237
+
238
+ # Returns true if a connection that's accessible to this class have already been opened.
239
+ def self.connected?
240
+ active_connections[active_connection_name] ? true : false
241
+ end
242
+
243
+ # Remove the connection for this class. This will close the active
244
+ # connection and the defined connection (if they exist). The result
245
+ # can be used as argument for establish_connection, for easy
246
+ # re-establishing of the connection.
247
+ def self.remove_connection(klass=self)
248
+ spec = @@defined_connections[klass.name]
249
+ konn = active_connections[klass.name]
250
+ @@defined_connections.delete_if { |key, value| value == spec }
251
+ active_connections.delete_if { |key, value| value == konn }
252
+ konn.disconnect! if konn
253
+ spec.config if spec
254
+ end
255
+
256
+ # Set the connection for the class.
257
+ def self.connection=(spec) #:nodoc:
258
+ if spec.kind_of?(BigRecord::ConnectionAdapters::AbstractAdapter)
259
+ active_connections[name] = spec
260
+ elsif spec.kind_of?(ConnectionSpecification)
261
+ config = spec.config.reverse_merge(:allow_concurrency => @@allow_concurrency)
262
+ self.connection = self.send(spec.adapter_method, config)
263
+ elsif spec.nil?
264
+ raise ConnectionNotEstablished
265
+ else
266
+ establish_connection spec
267
+ end
268
+ end
269
+
270
+ # connection state logging
271
+ def self.log_connections #:nodoc:
272
+ if logger
273
+ logger.info "Defined connections: #{@@defined_connections.inspect}"
274
+ logger.info "Active connections: #{active_connections.inspect}"
275
+ logger.info "Active connection name: #{@active_connection_name}"
276
+ end
277
+ end
278
+ end
279
+ end
@@ -0,0 +1,175 @@
1
+ module BigRecord
2
+ module ConnectionAdapters # :nodoc:
3
+ module DatabaseStatements
4
+ # # Returns an array of record hashes with the column names as keys and
5
+ # # column values as values.
6
+ # def select_all(sql, name = nil)
7
+ # select(sql, name)
8
+ # end
9
+ #
10
+ # # Returns a record hash with the column names as keys and column values
11
+ # # as values.
12
+ # def select_one(sql, name = nil)
13
+ # result = select_all(sql, name)
14
+ # result.first if result
15
+ # end
16
+ #
17
+ # # Returns a single value from a record
18
+ # def select_value(sql, name = nil)
19
+ # if result = select_one(sql, name)
20
+ # result.values.first
21
+ # end
22
+ # end
23
+ #
24
+ # # Returns an array of the values of the first column in a select:
25
+ # # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
26
+ # def select_values(sql, name = nil)
27
+ # result = select_rows(sql, name)
28
+ # result.map { |v| v[0] }
29
+ # end
30
+ #
31
+ # # Returns an array of arrays containing the field values.
32
+ # # Order is the same as that returned by #columns.
33
+ # def select_rows(sql, name = nil)
34
+ # raise NotImplementedError, "select_rows is an abstract method"
35
+ # end
36
+ #
37
+ # # Executes the SQL statement in the context of this connection.
38
+ # def execute(sql, name = nil)
39
+ # raise NotImplementedError, "execute is an abstract method"
40
+ # end
41
+ #
42
+ # # Returns the last auto-generated ID from the affected table.
43
+ # def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
44
+ # insert_sql(sql, name, pk, id_value, sequence_name)
45
+ # end
46
+ #
47
+ # # Executes the update statement and returns the number of rows affected.
48
+ # def update(sql, name = nil)
49
+ # update_sql(sql, name)
50
+ # end
51
+ #
52
+ # # Executes the delete statement and returns the number of rows affected.
53
+ # def delete(sql, name = nil)
54
+ # delete_sql(sql, name)
55
+ # end
56
+ #
57
+ # # Wrap a block in a transaction. Returns result of block.
58
+ # def transaction(start_db_transaction = true)
59
+ # transaction_open = false
60
+ # begin
61
+ # if block_given?
62
+ # if start_db_transaction
63
+ # begin_db_transaction
64
+ # transaction_open = true
65
+ # end
66
+ # yield
67
+ # end
68
+ # rescue Exception => database_transaction_rollback
69
+ # if transaction_open
70
+ # transaction_open = false
71
+ # rollback_db_transaction
72
+ # end
73
+ # raise unless database_transaction_rollback.is_a? ActiveRecord::Rollback
74
+ # end
75
+ # ensure
76
+ # if transaction_open
77
+ # begin
78
+ # commit_db_transaction
79
+ # rescue Exception => database_transaction_rollback
80
+ # rollback_db_transaction
81
+ # raise
82
+ # end
83
+ # end
84
+ # end
85
+ #
86
+ # # Begins the transaction (and turns off auto-committing).
87
+ # def begin_db_transaction() end
88
+ #
89
+ # # Commits the transaction (and turns on auto-committing).
90
+ # def commit_db_transaction() end
91
+ #
92
+ # # Rolls back the transaction (and turns on auto-committing). Must be
93
+ # # done if the transaction block raises an exception or returns false.
94
+ # def rollback_db_transaction() end
95
+ #
96
+ # # Alias for #add_limit_offset!.
97
+ # def add_limit!(sql, options)
98
+ # add_limit_offset!(sql, options) if options
99
+ # end
100
+ #
101
+ # # Appends +LIMIT+ and +OFFSET+ options to an SQL statement.
102
+ # # This method *modifies* the +sql+ parameter.
103
+ # # ===== Examples
104
+ # # add_limit_offset!('SELECT * FROM suppliers', {:limit => 10, :offset => 50})
105
+ # # generates
106
+ # # SELECT * FROM suppliers LIMIT 10 OFFSET 50
107
+ # def add_limit_offset!(sql, options)
108
+ # if limit = options[:limit]
109
+ # sql << " LIMIT #{limit}"
110
+ # if offset = options[:offset]
111
+ # sql << " OFFSET #{offset}"
112
+ # end
113
+ # end
114
+ # end
115
+ #
116
+ # # Appends a locking clause to an SQL statement.
117
+ # # This method *modifies* the +sql+ parameter.
118
+ # # # SELECT * FROM suppliers FOR UPDATE
119
+ # # add_lock! 'SELECT * FROM suppliers', :lock => true
120
+ # # add_lock! 'SELECT * FROM suppliers', :lock => ' FOR UPDATE'
121
+ # def add_lock!(sql, options)
122
+ # case lock = options[:lock]
123
+ # when true; sql << ' FOR UPDATE'
124
+ # when String; sql << " #{lock}"
125
+ # end
126
+ # end
127
+ #
128
+ # def default_sequence_name(table, column)
129
+ # nil
130
+ # end
131
+ #
132
+ # # Set the sequence to the max value of the table's column.
133
+ # def reset_sequence!(table, column, sequence = nil)
134
+ # # Do nothing by default. Implement for PostgreSQL, Oracle, ...
135
+ # end
136
+
137
+ # Inserts the given fixture into the table. Overridden in adapters that require
138
+ # something beyond a simple insert (eg. Oracle).
139
+ def insert_fixture(fixture, table_name)
140
+ # execute "INSERT INTO #{quote_table_name(table_name)} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert'
141
+ attributes = fixture.to_hash.dup
142
+ id = attributes.delete("id")
143
+ raise ArgumentError, "the id is missing" unless id
144
+ update(table_name, id, attributes, Time.now.to_bigrecord_timestamp)
145
+ end
146
+
147
+ # def empty_insert_statement(table_name)
148
+ # "INSERT INTO #{quote_table_name(table_name)} VALUES(DEFAULT)"
149
+ # end
150
+ #
151
+ # protected
152
+ # # Returns an array of record hashes with the column names as keys and
153
+ # # column values as values.
154
+ # def select(sql, name = nil)
155
+ # raise NotImplementedError, "select is an abstract method"
156
+ # end
157
+ #
158
+ # # Returns the last auto-generated ID from the affected table.
159
+ # def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
160
+ # execute(sql, name)
161
+ # id_value
162
+ # end
163
+ #
164
+ # # Executes the update statement and returns the number of rows affected.
165
+ # def update_sql(sql, name = nil)
166
+ # execute(sql, name)
167
+ # end
168
+ #
169
+ # # Executes the delete statement and returns the number of rows affected.
170
+ # def delete_sql(sql, name = nil)
171
+ # update_sql(sql, name)
172
+ # end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,58 @@
1
+ module BigRecord
2
+ module ConnectionAdapters # :nodoc:
3
+ module Quoting
4
+ # Quotes the column value to help prevent
5
+ # {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
6
+ def quote(value, column = nil)
7
+ # records are quoted as their primary key
8
+ return value.quoted_id if value.respond_to?(:quoted_id)
9
+
10
+ case value
11
+ when String, ActiveSupport::Multibyte::Chars
12
+ value = value.to_s
13
+ if column && column.type == :binary && column.class.respond_to?(:string_to_binary)
14
+ "'#{quote_string(column.class.string_to_binary(value))}'" # ' (for ruby-mode)
15
+ elsif column && [:integer, :float].include?(column.type)
16
+ value = column.type == :integer ? value.to_i : value.to_f
17
+ value.to_s
18
+ else
19
+ "'#{quote_string(value)}'" # ' (for ruby-mode)
20
+ end
21
+ when NilClass then "NULL"
22
+ when TrueClass then (column && column.type == :integer ? '1' : quoted_true)
23
+ when FalseClass then (column && column.type == :integer ? '0' : quoted_false)
24
+ when Float, Fixnum, Bignum then value.to_s
25
+ # BigDecimals need to be output in a non-normalized form and quoted.
26
+ when BigDecimal then value.to_s('F')
27
+ when Date then "'#{value.to_s}'"
28
+ when Time, DateTime then "'#{quoted_date(value)}'"
29
+ else "'#{quote_string(value.to_yaml)}'"
30
+ end
31
+ end
32
+
33
+ # Quotes a string, escaping any ' (single quote) and \ (backslash)
34
+ # characters.
35
+ def quote_string(s)
36
+ s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
37
+ end
38
+
39
+ # Returns a quoted form of the column name. This is highly adapter
40
+ # specific.
41
+ def quote_column_name(name)
42
+ name
43
+ end
44
+
45
+ def quoted_true
46
+ "'t'"
47
+ end
48
+
49
+ def quoted_false
50
+ "'f'"
51
+ end
52
+
53
+ def quoted_date(value)
54
+ value.strftime("%Y-%m-%d %H:%M:%S")
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,190 @@
1
+ require 'benchmark'
2
+ require 'date'
3
+ require 'bigdecimal'
4
+ require 'bigdecimal/util'
5
+
6
+ module BigRecord
7
+ module ConnectionAdapters # :nodoc:
8
+ # All the concrete database adapters follow the interface laid down in this class.
9
+ # You can use this interface directly by borrowing the database connection from the Base with
10
+ # Base.connection.
11
+ #
12
+ # Most of the methods in the adapter are useful during migrations. Most
13
+ # notably, SchemaStatements#create_table, SchemaStatements#drop_table,
14
+ # SchemaStatements#add_index, SchemaStatements#remove_index,
15
+ # SchemaStatements#add_column, SchemaStatements#change_column and
16
+ # SchemaStatements#remove_column are very useful.
17
+ class AbstractAdapter
18
+ include Quoting, DatabaseStatements#, SchemaStatements
19
+ @@row_even = true
20
+
21
+ def initialize(connection, logger = nil) #:nodoc:
22
+ @connection, @logger = connection, logger
23
+ @runtime = 0
24
+ @last_verification = 0
25
+ end
26
+
27
+ # Returns the human-readable name of the adapter. Use mixed case - one
28
+ # can always use downcase if needed.
29
+ def adapter_name
30
+ 'Abstract'
31
+ end
32
+
33
+ # Does this adapter support migrations? Backend specific, as the
34
+ # abstract adapter always returns +false+.
35
+ def supports_migrations?
36
+ false
37
+ end
38
+
39
+ # Does this adapter support using DISTINCT within COUNT? This is +true+
40
+ # for all adapters except sqlite.
41
+ def supports_count_distinct?
42
+ false
43
+ end
44
+
45
+ def supports_ddl_transactions?
46
+ false
47
+ end
48
+
49
+ # Should primary key values be selected from their corresponding
50
+ # sequence before the insert statement? If true, next_sequence_value
51
+ # is called before each insert to set the record's primary key.
52
+ # This is false for all adapters but Firebird.
53
+ def prefetch_primary_key?(table_name = nil)
54
+ false
55
+ end
56
+
57
+ def reset_runtime #:nodoc:
58
+ rt, @runtime = @runtime, 0
59
+ rt
60
+ end
61
+
62
+ # QUOTING ==================================================
63
+
64
+ # Override to return the quoted table name if the database needs it
65
+ def quote_table_name(name)
66
+ name
67
+ end
68
+
69
+ # REFERENTIAL INTEGRITY ====================================
70
+
71
+ # Override to turn off referential integrity while executing +&block+
72
+ def disable_referential_integrity(&block)
73
+ yield
74
+ end
75
+
76
+ # CONNECTION MANAGEMENT ====================================
77
+
78
+ # Is this connection active and ready to perform queries?
79
+ def active?
80
+ @active != false
81
+ end
82
+
83
+ # Close this connection and open a new one in its place.
84
+ def reconnect!
85
+ @active = true
86
+ end
87
+
88
+ # Close this connection
89
+ def disconnect!
90
+ @active = false
91
+ end
92
+
93
+ # Returns true if its safe to reload the connection between requests for development mode.
94
+ # This is not the case for Ruby/MySQL and it's not necessary for any adapters except SQLite.
95
+ def requires_reloading?
96
+ false
97
+ end
98
+
99
+ # Lazily verify this connection, calling +active?+ only if it hasn't
100
+ # been called for +timeout+ seconds.
101
+ def verify!(timeout)
102
+ now = Time.now.to_i
103
+ if (now - @last_verification) > timeout
104
+ reconnect! unless active?
105
+ @last_verification = now
106
+ end
107
+ end
108
+
109
+ # Provides access to the underlying database connection. Useful for
110
+ # when you need to call a proprietary method such as postgresql's lo_*
111
+ # methods
112
+ def raw_connection
113
+ @connection
114
+ end
115
+
116
+ # DATABASE STATEMENTS ======================================
117
+
118
+ def update_raw(table_name, row, values, timestamp)
119
+ raise NotImplementedError
120
+ end
121
+
122
+ def update(table_name, row, values, timestamp)
123
+ raise NotImplementedError
124
+ end
125
+
126
+ def get_raw(table_name, row, column, options={})
127
+ raise NotImplementedError
128
+ end
129
+
130
+ def get(table_name, row, column, options={})
131
+ raise NotImplementedError
132
+ end
133
+
134
+ def get_columns_raw(table_name, row, columns, options={})
135
+ raise NotImplementedError
136
+ end
137
+
138
+ def get_columns(table_name, row, columns, options={})
139
+ raise NotImplementedError
140
+ end
141
+
142
+ def get_consecutive_rows_raw(table_name, start_row, limit, columns, stop_row = nil)
143
+ raise NotImplementedError
144
+ end
145
+
146
+ def get_consecutive_rows(table_name, start_row, limit, columns, stop_row = nil)
147
+ raise NotImplementedError
148
+ end
149
+
150
+ def delete(table_name, row)
151
+ raise NotImplementedError
152
+ end
153
+
154
+ def delete_all(table_name)
155
+ raise NotImplementedError
156
+ end
157
+
158
+ # SCHEMA STATEMENTS ========================================
159
+
160
+ def table_exists?(table_name)
161
+ raise NotImplementedError
162
+ end
163
+
164
+ def create_table(table_name, column_families)
165
+ raise NotImplementedError
166
+ end
167
+
168
+ def drop_table(table_name)
169
+ raise NotImplementedError
170
+ end
171
+
172
+ end # class AbstractAdapter
173
+ end # module ConnectionAdapters
174
+ end # module BigRecord
175
+
176
+
177
+ # Open the time class to add logic for the hbase timestamp
178
+ class Time
179
+ # Return this time is the hbase timestamp format, i.e. a 'long'. The 4 high bytes contain
180
+ # the number of seconds since epoch and the 4 low bytes contain the microseconds. That
181
+ # format is an arbitrary one and could have been something else.
182
+ def to_bigrecord_timestamp
183
+ (self.to_i << 32) + self.usec
184
+ end
185
+
186
+ def self.from_bigrecord_timestamp(timestamp)
187
+ Time.at(timestamp >> 32, timestamp & 0xFFFFFFFF)
188
+ end
189
+
190
+ end