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.
- 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
|