bigrecord 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +44 -0
- data/Rakefile +17 -0
- data/VERSION +1 -0
- data/doc/bigrecord_specs.rdoc +36 -0
- data/doc/getting_started.rdoc +157 -0
- data/examples/bigrecord.yml +25 -0
- data/generators/bigrecord/bigrecord_generator.rb +17 -0
- data/generators/bigrecord/templates/bigrecord.rake +47 -0
- data/generators/bigrecord_migration/bigrecord_migration_generator.rb +13 -0
- data/generators/bigrecord_migration/templates/migration.rb +9 -0
- data/generators/bigrecord_model/bigrecord_model_generator.rb +28 -0
- data/generators/bigrecord_model/templates/migration.rb +13 -0
- data/generators/bigrecord_model/templates/model.rb +7 -0
- data/generators/bigrecord_model/templates/model_spec.rb +12 -0
- data/init.rb +9 -0
- data/install.rb +22 -0
- data/lib/big_record/abstract_base.rb +1088 -0
- data/lib/big_record/action_view_extensions.rb +266 -0
- data/lib/big_record/ar_associations/association_collection.rb +194 -0
- data/lib/big_record/ar_associations/association_proxy.rb +158 -0
- data/lib/big_record/ar_associations/belongs_to_association.rb +57 -0
- data/lib/big_record/ar_associations/belongs_to_many_association.rb +57 -0
- data/lib/big_record/ar_associations/has_and_belongs_to_many_association.rb +164 -0
- data/lib/big_record/ar_associations/has_many_association.rb +191 -0
- data/lib/big_record/ar_associations/has_one_association.rb +80 -0
- data/lib/big_record/ar_associations.rb +1608 -0
- data/lib/big_record/ar_reflection.rb +223 -0
- data/lib/big_record/attribute_methods.rb +75 -0
- data/lib/big_record/base.rb +618 -0
- data/lib/big_record/br_associations/association_collection.rb +194 -0
- data/lib/big_record/br_associations/association_proxy.rb +153 -0
- data/lib/big_record/br_associations/belongs_to_association.rb +52 -0
- data/lib/big_record/br_associations/belongs_to_many_association.rb +293 -0
- data/lib/big_record/br_associations/cached_item_proxy.rb +194 -0
- data/lib/big_record/br_associations/cached_item_proxy_factory.rb +62 -0
- data/lib/big_record/br_associations/has_and_belongs_to_many_association.rb +168 -0
- data/lib/big_record/br_associations/has_one_association.rb +80 -0
- data/lib/big_record/br_associations.rb +978 -0
- data/lib/big_record/br_reflection.rb +151 -0
- data/lib/big_record/callbacks.rb +367 -0
- data/lib/big_record/connection_adapters/abstract/connection_specification.rb +279 -0
- data/lib/big_record/connection_adapters/abstract/database_statements.rb +175 -0
- data/lib/big_record/connection_adapters/abstract/quoting.rb +58 -0
- data/lib/big_record/connection_adapters/abstract_adapter.rb +190 -0
- data/lib/big_record/connection_adapters/column.rb +491 -0
- data/lib/big_record/connection_adapters/hbase_adapter.rb +432 -0
- data/lib/big_record/connection_adapters/view.rb +27 -0
- data/lib/big_record/connection_adapters.rb +10 -0
- data/lib/big_record/deletion.rb +73 -0
- data/lib/big_record/dynamic_schema.rb +92 -0
- data/lib/big_record/embedded.rb +71 -0
- data/lib/big_record/embedded_associations/association_proxy.rb +148 -0
- data/lib/big_record/family_span_columns.rb +89 -0
- data/lib/big_record/fixtures.rb +1025 -0
- data/lib/big_record/migration.rb +380 -0
- data/lib/big_record/routing_ext.rb +65 -0
- data/lib/big_record/timestamp.rb +51 -0
- data/lib/big_record/validations.rb +830 -0
- data/lib/big_record.rb +125 -0
- data/lib/bigrecord.rb +1 -0
- data/rails/init.rb +9 -0
- data/spec/connections/bigrecord.yml +13 -0
- data/spec/connections/cassandra/connection.rb +2 -0
- data/spec/connections/hbase/connection.rb +2 -0
- data/spec/debug.log +281 -0
- data/spec/integration/br_associations_spec.rb +80 -0
- data/spec/lib/animal.rb +12 -0
- data/spec/lib/book.rb +10 -0
- data/spec/lib/broken_migrations/duplicate_name/20090706182535_add_animals_table.rb +14 -0
- data/spec/lib/broken_migrations/duplicate_name/20090706193019_add_animals_table.rb +9 -0
- data/spec/lib/broken_migrations/duplicate_version/20090706190623_add_books_table.rb +9 -0
- data/spec/lib/broken_migrations/duplicate_version/20090706190623_add_companies_table.rb +9 -0
- data/spec/lib/company.rb +14 -0
- data/spec/lib/embedded/web_link.rb +12 -0
- data/spec/lib/employee.rb +33 -0
- data/spec/lib/migrations/20090706182535_add_animals_table.rb +13 -0
- data/spec/lib/migrations/20090706190623_add_books_table.rb +15 -0
- data/spec/lib/migrations/20090706193019_add_companies_table.rb +14 -0
- data/spec/lib/migrations/20090706194512_add_employees_table.rb +13 -0
- data/spec/lib/migrations/20090706195741_add_zoos_table.rb +13 -0
- data/spec/lib/novel.rb +5 -0
- data/spec/lib/zoo.rb +17 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +55 -0
- data/spec/unit/abstract_base_spec.rb +287 -0
- data/spec/unit/adapters/abstract_adapter_spec.rb +56 -0
- data/spec/unit/adapters/adapter_shared_spec.rb +51 -0
- data/spec/unit/adapters/hbase_adapter_spec.rb +15 -0
- data/spec/unit/ar_associations_spec.rb +8 -0
- data/spec/unit/base_spec.rb +6 -0
- data/spec/unit/br_associations_spec.rb +58 -0
- data/spec/unit/embedded_spec.rb +43 -0
- data/spec/unit/find_spec.rb +34 -0
- data/spec/unit/hash_helper_spec.rb +44 -0
- data/spec/unit/migration_spec.rb +144 -0
- data/spec/unit/model_spec.rb +315 -0
- data/spec/unit/validations_spec.rb +182 -0
- data/tasks/bigrecord_tasks.rake +47 -0
- data/tasks/data_store.rb +46 -0
- data/tasks/gem.rb +22 -0
- data/tasks/rdoc.rb +8 -0
- data/tasks/spec.rb +34 -0
- metadata +189 -0
@@ -0,0 +1,491 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'bigdecimal'
|
3
|
+
require 'bigdecimal/util'
|
4
|
+
|
5
|
+
module BigRecord
|
6
|
+
module ConnectionAdapters #:nodoc:
|
7
|
+
class Column
|
8
|
+
module Format
|
9
|
+
ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
|
10
|
+
ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :name, :type, :collection, :default, :alias
|
14
|
+
attr_accessor :primary
|
15
|
+
|
16
|
+
COLLECTION_SEPARATOR = "::"
|
17
|
+
|
18
|
+
def initialize(name, type, options={})
|
19
|
+
@type = type.to_sym
|
20
|
+
@collection = options[:collection]
|
21
|
+
@name = name.to_s
|
22
|
+
@alias = options[:alias] ? options[:alias].to_s : (self.class.extract_qualifier(@name) || (@name unless family?))
|
23
|
+
|
24
|
+
if options[:default]
|
25
|
+
@default = options[:default]
|
26
|
+
elsif @collection
|
27
|
+
@default = []
|
28
|
+
else
|
29
|
+
@default = (@type == :boolean) ? false : nil
|
30
|
+
end
|
31
|
+
# cache whether or not we'll need to dup the default columns to avoid clients to share
|
32
|
+
# the same reference to the default value
|
33
|
+
@must_dup_default = (!@default.nil? and (collection or (!number? and @type != :boolean)))
|
34
|
+
|
35
|
+
@primary = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# callback may be implemented by subclasses if value needs to be 'massaged' before instantiation.
|
39
|
+
def preinitialize(value)
|
40
|
+
# do something to a column value's attributes before it is instantiated.
|
41
|
+
end
|
42
|
+
|
43
|
+
def default
|
44
|
+
@must_dup_default ? @default.dup : @default
|
45
|
+
end
|
46
|
+
|
47
|
+
def text?
|
48
|
+
[:string, :text].include? type
|
49
|
+
end
|
50
|
+
|
51
|
+
def number?
|
52
|
+
[:float, :integer, :decimal].include? type
|
53
|
+
end
|
54
|
+
|
55
|
+
def primitive?
|
56
|
+
@primitive ||= ([:integer, :float, :decimal, :datetime, :date, :timestamp, :time, :text, :string, :binary, :boolean, :map, :object].include? type)
|
57
|
+
end
|
58
|
+
|
59
|
+
def family?
|
60
|
+
name =~ /:\Z/
|
61
|
+
end
|
62
|
+
|
63
|
+
def family
|
64
|
+
self.class.extract_family(self.name)
|
65
|
+
end
|
66
|
+
|
67
|
+
def qualifier
|
68
|
+
self.class.extract_qualifier(self.name)
|
69
|
+
end
|
70
|
+
|
71
|
+
def collection?
|
72
|
+
@collection
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns the Ruby class that corresponds to the abstract data type.
|
76
|
+
def klass
|
77
|
+
@klass ||=
|
78
|
+
case type
|
79
|
+
when :integer then Fixnum
|
80
|
+
when :float then Float
|
81
|
+
when :decimal then BigDecimal
|
82
|
+
when :datetime then Time
|
83
|
+
when :date then Date
|
84
|
+
when :timestamp then Time
|
85
|
+
when :time then Time
|
86
|
+
when :text, :string then String
|
87
|
+
when :binary then String
|
88
|
+
when :boolean then Object
|
89
|
+
when :map then Hash
|
90
|
+
when :object then Object
|
91
|
+
else type.to_s.constantize
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Casts value (which is a String) to an appropriate instance.
|
96
|
+
def type_cast(value)
|
97
|
+
# FIXME: this should be recursive but it doesn't work with type_cast_code()... why does
|
98
|
+
# ActiveRecord use type_cast_code ???
|
99
|
+
if collection?
|
100
|
+
return [] if value.nil?
|
101
|
+
case type
|
102
|
+
when :string then self.class.hash_to_string_collection(value)
|
103
|
+
when :text then self.class.hash_to_string_collection(value)
|
104
|
+
when :integer then self.class.hash_to_integer_collection(value)
|
105
|
+
when :float then self.class.hash_to_float_collection(value)
|
106
|
+
when :decimal then self.class.hash_to_decimal_collection(value)
|
107
|
+
when :datetime then self.class.hash_to_time_collection(value)
|
108
|
+
when :timestamp then self.class.hash_to_time_collection(value)
|
109
|
+
when :time then self.class.hash_to_dummy_time_collection(value)
|
110
|
+
when :date then self.class.hash_to_date_collection(value)
|
111
|
+
when :binary then self.class.hash_to_string_collection(value)
|
112
|
+
when :boolean then self.class.hash_to_boolean_collection(value)
|
113
|
+
when :map then value
|
114
|
+
when :object then value
|
115
|
+
else hash_to_embedded_collection(value)
|
116
|
+
end
|
117
|
+
else
|
118
|
+
casted_value =
|
119
|
+
case type
|
120
|
+
when :string then value
|
121
|
+
when :text then value
|
122
|
+
when :integer then value.to_i rescue value ? 1 : 0
|
123
|
+
when :float then value.to_f rescue value ? 1.0 : 0.0
|
124
|
+
when :decimal then self.class.value_to_decimal(value)
|
125
|
+
when :datetime then self.class.string_to_time(value)
|
126
|
+
when :timestamp then self.class.string_to_time(value)
|
127
|
+
when :time then self.class.string_to_dummy_time(value)
|
128
|
+
when :date then self.class.string_to_date(value)
|
129
|
+
when :binary then self.class.binary_to_string(value)
|
130
|
+
when :boolean then self.class.value_to_boolean(value)
|
131
|
+
when :map then value
|
132
|
+
when :object then value
|
133
|
+
else hash_to_embedded(value)
|
134
|
+
end
|
135
|
+
# Make sure that the returned value matches the current schema.
|
136
|
+
casted_value.is_a?(klass) ? casted_value : nil
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def type_cast_code(var_name)
|
141
|
+
if collection?
|
142
|
+
case type
|
143
|
+
when :string then "#{self.class.name}.hash_to_string_collection(#{var_name})"
|
144
|
+
when :text then "#{self.class.name}.hash_to_string_collection(#{var_name})"
|
145
|
+
when :integer then "#{self.class.name}.hash_to_integer_collection(#{var_name})"
|
146
|
+
when :float then "#{self.class.name}.hash_to_float_collection(#{var_name})"
|
147
|
+
when :decimal then "#{self.class.name}.hash_to_decimal_collection(#{var_name})"
|
148
|
+
when :datetime then "#{self.class.name}.hash_to_time_collection(#{var_name})"
|
149
|
+
when :timestamp then "#{self.class.name}.hash_to_time_collection(#{var_name})"
|
150
|
+
when :time then "#{self.class.name}.hash_to_dummy_time_collection(#{var_name})"
|
151
|
+
when :date then "#{self.class.name}.hash_to_date_collection(#{var_name})"
|
152
|
+
when :binary then "#{self.class.name}.hash_to_string_collection(#{var_name})"
|
153
|
+
when :boolean then "#{self.class.name}.hash_to_boolean_collection(#{var_name})"
|
154
|
+
when :map then nil
|
155
|
+
when :object then nil
|
156
|
+
else nil
|
157
|
+
end
|
158
|
+
else
|
159
|
+
case type
|
160
|
+
when :string then nil
|
161
|
+
when :text then nil
|
162
|
+
when :integer then "(#{var_name}.to_i rescue #{var_name} ? 1 : 0)"
|
163
|
+
when :float then "#{var_name}.to_f"
|
164
|
+
when :decimal then "#{self.class.name}.value_to_decimal(#{var_name})"
|
165
|
+
when :datetime then "#{self.class.name}.string_to_time(#{var_name})"
|
166
|
+
when :timestamp then "#{self.class.name}.string_to_time(#{var_name})"
|
167
|
+
when :time then "#{self.class.name}.value_to_dummy_time(#{var_name})"
|
168
|
+
when :date then "#{self.class.name}.string_to_date(#{var_name})"
|
169
|
+
when :binary then "#{self.class.name}.binary_to_string(#{var_name})"
|
170
|
+
when :boolean then "#{self.class.name}.value_to_boolean(#{var_name})"
|
171
|
+
when :map then nil
|
172
|
+
when :object then nil
|
173
|
+
else nil
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Returns the human name of the column name.
|
179
|
+
#
|
180
|
+
# ===== Examples
|
181
|
+
# Column.new('sales_stage', ...).human_name #=> 'Sales stage'
|
182
|
+
def human_name
|
183
|
+
Base.human_attribute_name(@name)
|
184
|
+
end
|
185
|
+
|
186
|
+
def hash_to_embedded_collection(hash)
|
187
|
+
hash_collection = hash.is_a?(Hash) ? self.class.hash_to_collection(hash) : hash
|
188
|
+
hash_collection_to_embedded_collection(hash_collection)
|
189
|
+
end
|
190
|
+
|
191
|
+
def hash_collection_to_embedded_collection(hash_collection)
|
192
|
+
return hash_collection unless hash_collection.is_a?(Array)
|
193
|
+
hash_collection.each_with_index do |hash, i|
|
194
|
+
hash_collection[i] = hash_to_embedded(hash) if hash.is_a?(Hash)
|
195
|
+
end
|
196
|
+
hash_collection
|
197
|
+
end
|
198
|
+
|
199
|
+
def hash_to_embedded(value)
|
200
|
+
case value
|
201
|
+
when BigRecord::Embedded then value
|
202
|
+
when Hash then self.klass.instantiate(value)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
class << self
|
207
|
+
|
208
|
+
# Extract the family from a column name
|
209
|
+
def extract_family(column_name)
|
210
|
+
return nil unless column_name
|
211
|
+
column_name =~ /\A(.*?:).*\Z/
|
212
|
+
$1
|
213
|
+
end
|
214
|
+
|
215
|
+
# Extract the qualifier from a column name
|
216
|
+
def extract_qualifier(column_name)
|
217
|
+
return nil unless column_name
|
218
|
+
column_name =~ /\A.*?:(.*)\Z/
|
219
|
+
$1
|
220
|
+
end
|
221
|
+
|
222
|
+
# Extract the collection from the hash, where the positions are the keys. Inspired
|
223
|
+
# from ActiveRecord::NestedAttributes.
|
224
|
+
#
|
225
|
+
# params = { 'member' => {
|
226
|
+
# 'name' => 'joe', 'posts_attributes' => {
|
227
|
+
# '1' => { 'title' => 'Kari, the awesome Ruby documentation browser!' },
|
228
|
+
# '2' => { 'title' => 'The egalitarian assumption of the modern citizen' },
|
229
|
+
# 'new_67890' => { 'title' => '' } # This one matches the :reject_if proc and will not be instantiated.
|
230
|
+
# }
|
231
|
+
# }}
|
232
|
+
def hash_to_collection(hash)
|
233
|
+
return hash unless hash.is_a?(Hash)
|
234
|
+
|
235
|
+
# Make sure any new records sorted by their id before they're build.
|
236
|
+
sorted_by_id = hash.sort_by { |id, _| id.is_a?(String) ? id.sub(/^new_/, '').to_i : id }
|
237
|
+
|
238
|
+
array = []
|
239
|
+
sorted_by_id.each do |id, record_attributes|
|
240
|
+
# remove blank records
|
241
|
+
next if blank_or_invalid_record?(record_attributes)
|
242
|
+
|
243
|
+
array << record_attributes
|
244
|
+
end
|
245
|
+
array
|
246
|
+
end
|
247
|
+
|
248
|
+
# Check if the given record is empty. It's recursive since it
|
249
|
+
# can be an Embedded
|
250
|
+
def blank_or_invalid_record?(record_attributes)
|
251
|
+
return true if record_attributes.blank? or !record_attributes.is_a?(Hash)
|
252
|
+
record_attributes.all? do |k, v|
|
253
|
+
v.is_a?(Hash) ? (v.empty? or blank_or_invalid_record?(v)) : v.blank?
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def extract_callstack_for_multiparameter_attributes(pairs)
|
258
|
+
attributes = { }
|
259
|
+
|
260
|
+
for pair in pairs
|
261
|
+
multiparameter_name, value = pair
|
262
|
+
attribute_name = multiparameter_name.split("(").first
|
263
|
+
attributes[attribute_name] = [] unless attributes.include?(attribute_name)
|
264
|
+
|
265
|
+
unless value.empty?
|
266
|
+
attributes[attribute_name] <<
|
267
|
+
[ find_parameter_position(multiparameter_name), type_cast_attribute_value(multiparameter_name, value) ]
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last } }
|
272
|
+
end
|
273
|
+
|
274
|
+
def parse_collection(value)
|
275
|
+
case value
|
276
|
+
when String then value.split(COLLECTION_SEPARATOR)
|
277
|
+
when Hash then value.values.first.scan(/\[(.*?)\]/).flatten
|
278
|
+
when NilClass then []
|
279
|
+
else value
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
# strings are a special case...
|
284
|
+
def hash_to_string_collection(value)
|
285
|
+
parse_collection(value).collect(&:to_s)
|
286
|
+
end
|
287
|
+
|
288
|
+
def hash_to_integer_collection(value)
|
289
|
+
parse_collection(value).collect(&:to_i)
|
290
|
+
end
|
291
|
+
|
292
|
+
def hash_to_float_collection(value)
|
293
|
+
parse_collection(value).collect(&:to_f)
|
294
|
+
end
|
295
|
+
|
296
|
+
def hash_to_decimal_collection(value)
|
297
|
+
parse_collection(value).collect{|v| value_to_decimal(v)}
|
298
|
+
end
|
299
|
+
|
300
|
+
def hash_to_date_collection(value)
|
301
|
+
parse_collection(value).collect{|v| string_to_date(v.to_s)}
|
302
|
+
end
|
303
|
+
|
304
|
+
def hash_to_time_collection(value)
|
305
|
+
parse_collection(value).collect{|v| string_to_time(v.to_s)}
|
306
|
+
end
|
307
|
+
|
308
|
+
def hash_to_dummy_time_collection(value)
|
309
|
+
parse_collection(value).collect{|v| string_to_dummy_time(v.to_s)}
|
310
|
+
end
|
311
|
+
|
312
|
+
# def hash_to_time_collection(hash)
|
313
|
+
# return []
|
314
|
+
# return hash unless hash.is_a?(Hash)
|
315
|
+
# hash_to_collection(hash).collect do |attributes|
|
316
|
+
# cleaned_attributes = {}
|
317
|
+
# debugger
|
318
|
+
# attributes.each do |k, v|
|
319
|
+
# k =~ /reflect_value(.*)/
|
320
|
+
# cleaned_attributes[$1] = v
|
321
|
+
# end
|
322
|
+
# string_to_time(cleaned_attributes)
|
323
|
+
# end
|
324
|
+
# end
|
325
|
+
#
|
326
|
+
# def hash_to_dummy_time_collection(hash)
|
327
|
+
# return []
|
328
|
+
# return hash unless hash.is_a?(Array)
|
329
|
+
# hash.split(COLLECTION_SEPARATOR).collect{|v|string_to_dummy_time(v)}
|
330
|
+
# end
|
331
|
+
#
|
332
|
+
# def hash_to_date_collection(hash)
|
333
|
+
# return []
|
334
|
+
# return hash unless hash.is_a?(Array)
|
335
|
+
# hash_to_collection(hash).collect do |attributes|
|
336
|
+
# debugger
|
337
|
+
# callstack = extract_callstack_for_multiparameter_attributes(attributes.to_a)
|
338
|
+
#
|
339
|
+
# end
|
340
|
+
# end
|
341
|
+
|
342
|
+
def hash_to_boolean_collection(value)
|
343
|
+
parse_collection(value).collect{|v| value_to_boolean(v)}
|
344
|
+
end
|
345
|
+
|
346
|
+
# Used to convert from Strings to BLOBs
|
347
|
+
def string_to_binary(value)
|
348
|
+
value
|
349
|
+
end
|
350
|
+
|
351
|
+
# Used to convert from BLOBs to Strings
|
352
|
+
def binary_to_string(value)
|
353
|
+
value
|
354
|
+
end
|
355
|
+
|
356
|
+
def string_to_date(string)
|
357
|
+
return string unless string.is_a?(String)
|
358
|
+
return nil if string.empty?
|
359
|
+
|
360
|
+
fast_string_to_date(string) || fallback_string_to_date(string)
|
361
|
+
end
|
362
|
+
|
363
|
+
def string_to_time(string)
|
364
|
+
return string unless string.is_a?(String)
|
365
|
+
return nil if string.empty?
|
366
|
+
|
367
|
+
fast_string_to_time(string) || fallback_string_to_time(string)
|
368
|
+
end
|
369
|
+
|
370
|
+
def string_to_dummy_time(string)
|
371
|
+
return string unless string.is_a?(String)
|
372
|
+
return nil if string.empty?
|
373
|
+
|
374
|
+
string_to_time "2000-01-01 #{string}"
|
375
|
+
end
|
376
|
+
|
377
|
+
# convert something to a boolean
|
378
|
+
def value_to_boolean(value)
|
379
|
+
if value == true || value == false
|
380
|
+
value
|
381
|
+
else
|
382
|
+
%w(true t 1).include?(value.to_s.downcase)
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
# convert something to a BigDecimal
|
387
|
+
def value_to_decimal(value)
|
388
|
+
if value.is_a?(BigDecimal)
|
389
|
+
value
|
390
|
+
elsif value.respond_to?(:to_d)
|
391
|
+
value.to_d
|
392
|
+
else
|
393
|
+
value.to_s.to_d
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
protected
|
398
|
+
# '0.123456' -> 123456
|
399
|
+
# '1.123456' -> 123456
|
400
|
+
def microseconds(time)
|
401
|
+
((time[:sec_fraction].to_f % 1) * 1_000_000).to_i
|
402
|
+
end
|
403
|
+
|
404
|
+
def new_date(year, mon, mday)
|
405
|
+
if year && year != 0
|
406
|
+
Date.new(year, mon, mday) rescue nil
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
def new_time(year, mon, mday, hour, min, sec, microsec)
|
411
|
+
# Treat 0000-00-00 00:00:00 as nil.
|
412
|
+
return nil if year.nil? || year == 0
|
413
|
+
|
414
|
+
Time.send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec)
|
415
|
+
# Over/underflow to DateTime
|
416
|
+
rescue ArgumentError, TypeError
|
417
|
+
zone_offset = Base.default_timezone == :local ? DateTime.local_offset : 0
|
418
|
+
DateTime.civil(year, mon, mday, hour, min, sec, zone_offset) rescue nil
|
419
|
+
end
|
420
|
+
|
421
|
+
def fast_string_to_date(string)
|
422
|
+
if string =~ Format::ISO_DATE
|
423
|
+
new_date $1.to_i, $2.to_i, $3.to_i
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
# Doesn't handle time zones.
|
428
|
+
def fast_string_to_time(string)
|
429
|
+
if string =~ Format::ISO_DATETIME
|
430
|
+
microsec = ($7.to_f * 1_000_000).to_i
|
431
|
+
new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
def fallback_string_to_date(string)
|
436
|
+
new_date *ParseDate.parsedate(string)[0..2]
|
437
|
+
end
|
438
|
+
|
439
|
+
def fallback_string_to_time(string)
|
440
|
+
time_hash = Date._parse(string)
|
441
|
+
time_hash[:sec_fraction] = microseconds(time_hash)
|
442
|
+
|
443
|
+
new_time *time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction)
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
private
|
448
|
+
def extract_limit(sql_type)
|
449
|
+
$1.to_i if sql_type =~ /\((.*)\)/
|
450
|
+
end
|
451
|
+
|
452
|
+
def extract_precision(sql_type)
|
453
|
+
$2.to_i if sql_type =~ /^(numeric|decimal|number)\((\d+)(,\d+)?\)/i
|
454
|
+
end
|
455
|
+
|
456
|
+
def extract_scale(sql_type)
|
457
|
+
case sql_type
|
458
|
+
when /^(numeric|decimal|number)\((\d+)\)/i then 0
|
459
|
+
when /^(numeric|decimal|number)\((\d+)(,(\d+))\)/i then $4.to_i
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
def simplified_type(field_type)
|
464
|
+
case field_type
|
465
|
+
when /int/i
|
466
|
+
:integer
|
467
|
+
when /float|double/i
|
468
|
+
:float
|
469
|
+
when /decimal|numeric|number/i
|
470
|
+
extract_scale(field_type) == 0 ? :integer : :decimal
|
471
|
+
when /datetime/i
|
472
|
+
:datetime
|
473
|
+
when /timestamp/i
|
474
|
+
:timestamp
|
475
|
+
when /time/i
|
476
|
+
:time
|
477
|
+
when /date/i
|
478
|
+
:date
|
479
|
+
when /clob/i, /text/i
|
480
|
+
:text
|
481
|
+
when /blob/i, /binary/i
|
482
|
+
:binary
|
483
|
+
when /char/i, /string/i
|
484
|
+
:string
|
485
|
+
when /boolean/i
|
486
|
+
:boolean
|
487
|
+
end
|
488
|
+
end
|
489
|
+
end
|
490
|
+
end
|
491
|
+
end
|