activerecord 3.2.22.5 → 4.2.11.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (236) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1632 -609
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +37 -41
  5. data/examples/performance.rb +31 -19
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +56 -42
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -36
  10. data/lib/active_record/associations/association.rb +73 -55
  11. data/lib/active_record/associations/association_scope.rb +143 -82
  12. data/lib/active_record/associations/belongs_to_association.rb +65 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
  14. data/lib/active_record/associations/builder/association.rb +125 -31
  15. data/lib/active_record/associations/builder/belongs_to.rb +89 -61
  16. data/lib/active_record/associations/builder/collection_association.rb +69 -49
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +113 -42
  18. data/lib/active_record/associations/builder/has_many.rb +8 -64
  19. data/lib/active_record/associations/builder/has_one.rb +12 -51
  20. data/lib/active_record/associations/builder/singular_association.rb +23 -17
  21. data/lib/active_record/associations/collection_association.rb +251 -177
  22. data/lib/active_record/associations/collection_proxy.rb +963 -63
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +113 -22
  25. data/lib/active_record/associations/has_many_through_association.rb +99 -39
  26. data/lib/active_record/associations/has_one_association.rb +43 -20
  27. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  28. data/lib/active_record/associations/join_dependency/join_association.rb +76 -107
  29. data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
  31. data/lib/active_record/associations/join_dependency.rb +230 -156
  32. data/lib/active_record/associations/preloader/association.rb +96 -55
  33. data/lib/active_record/associations/preloader/collection_association.rb +3 -3
  34. data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
  35. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +62 -33
  38. data/lib/active_record/associations/preloader.rb +101 -79
  39. data/lib/active_record/associations/singular_association.rb +29 -13
  40. data/lib/active_record/associations/through_association.rb +30 -16
  41. data/lib/active_record/associations.rb +463 -345
  42. data/lib/active_record/attribute.rb +163 -0
  43. data/lib/active_record/attribute_assignment.rb +142 -151
  44. data/lib/active_record/attribute_decorators.rb +66 -0
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
  46. data/lib/active_record/attribute_methods/dirty.rb +137 -57
  47. data/lib/active_record/attribute_methods/primary_key.rb +50 -36
  48. data/lib/active_record/attribute_methods/query.rb +5 -4
  49. data/lib/active_record/attribute_methods/read.rb +73 -106
  50. data/lib/active_record/attribute_methods/serialization.rb +44 -94
  51. data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -45
  52. data/lib/active_record/attribute_methods/write.rb +57 -44
  53. data/lib/active_record/attribute_methods.rb +301 -141
  54. data/lib/active_record/attribute_set/builder.rb +106 -0
  55. data/lib/active_record/attribute_set.rb +81 -0
  56. data/lib/active_record/attributes.rb +147 -0
  57. data/lib/active_record/autosave_association.rb +246 -217
  58. data/lib/active_record/base.rb +70 -474
  59. data/lib/active_record/callbacks.rb +66 -28
  60. data/lib/active_record/coders/json.rb +13 -0
  61. data/lib/active_record/coders/yaml_column.rb +18 -21
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +396 -219
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -164
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +29 -24
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -55
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +261 -169
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +707 -259
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +298 -89
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +466 -196
  75. data/lib/active_record/connection_adapters/column.rb +31 -245
  76. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +45 -57
  78. data/lib/active_record/connection_adapters/mysql_adapter.rb +180 -123
  79. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  80. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  81. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +596 -0
  112. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  113. data/lib/active_record/connection_adapters/postgresql_adapter.rb +430 -999
  114. data/lib/active_record/connection_adapters/schema_cache.rb +52 -27
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +579 -22
  116. data/lib/active_record/connection_handling.rb +132 -0
  117. data/lib/active_record/core.rb +579 -0
  118. data/lib/active_record/counter_cache.rb +157 -105
  119. data/lib/active_record/dynamic_matchers.rb +119 -63
  120. data/lib/active_record/enum.rb +197 -0
  121. data/lib/active_record/errors.rb +94 -36
  122. data/lib/active_record/explain.rb +15 -63
  123. data/lib/active_record/explain_registry.rb +30 -0
  124. data/lib/active_record/explain_subscriber.rb +9 -5
  125. data/lib/active_record/fixture_set/file.rb +56 -0
  126. data/lib/active_record/fixtures.rb +302 -215
  127. data/lib/active_record/gem_version.rb +15 -0
  128. data/lib/active_record/inheritance.rb +143 -70
  129. data/lib/active_record/integration.rb +65 -12
  130. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  131. data/lib/active_record/locale/en.yml +8 -1
  132. data/lib/active_record/locking/optimistic.rb +73 -52
  133. data/lib/active_record/locking/pessimistic.rb +5 -5
  134. data/lib/active_record/log_subscriber.rb +24 -21
  135. data/lib/active_record/migration/command_recorder.rb +124 -32
  136. data/lib/active_record/migration/join_table.rb +15 -0
  137. data/lib/active_record/migration.rb +511 -213
  138. data/lib/active_record/model_schema.rb +91 -117
  139. data/lib/active_record/nested_attributes.rb +184 -130
  140. data/lib/active_record/no_touching.rb +52 -0
  141. data/lib/active_record/null_relation.rb +81 -0
  142. data/lib/active_record/persistence.rb +276 -117
  143. data/lib/active_record/query_cache.rb +19 -37
  144. data/lib/active_record/querying.rb +28 -18
  145. data/lib/active_record/railtie.rb +73 -40
  146. data/lib/active_record/railties/console_sandbox.rb +3 -4
  147. data/lib/active_record/railties/controller_runtime.rb +4 -3
  148. data/lib/active_record/railties/databases.rake +141 -416
  149. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  150. data/lib/active_record/readonly_attributes.rb +1 -4
  151. data/lib/active_record/reflection.rb +513 -154
  152. data/lib/active_record/relation/batches.rb +91 -43
  153. data/lib/active_record/relation/calculations.rb +199 -161
  154. data/lib/active_record/relation/delegation.rb +116 -25
  155. data/lib/active_record/relation/finder_methods.rb +362 -248
  156. data/lib/active_record/relation/merger.rb +193 -0
  157. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  158. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  159. data/lib/active_record/relation/predicate_builder.rb +135 -43
  160. data/lib/active_record/relation/query_methods.rb +928 -167
  161. data/lib/active_record/relation/spawn_methods.rb +48 -149
  162. data/lib/active_record/relation.rb +352 -207
  163. data/lib/active_record/result.rb +101 -10
  164. data/lib/active_record/runtime_registry.rb +22 -0
  165. data/lib/active_record/sanitization.rb +56 -59
  166. data/lib/active_record/schema.rb +19 -13
  167. data/lib/active_record/schema_dumper.rb +106 -63
  168. data/lib/active_record/schema_migration.rb +53 -0
  169. data/lib/active_record/scoping/default.rb +50 -57
  170. data/lib/active_record/scoping/named.rb +73 -109
  171. data/lib/active_record/scoping.rb +58 -123
  172. data/lib/active_record/serialization.rb +6 -2
  173. data/lib/active_record/serializers/xml_serializer.rb +12 -22
  174. data/lib/active_record/statement_cache.rb +111 -0
  175. data/lib/active_record/store.rb +168 -15
  176. data/lib/active_record/tasks/database_tasks.rb +299 -0
  177. data/lib/active_record/tasks/mysql_database_tasks.rb +159 -0
  178. data/lib/active_record/tasks/postgresql_database_tasks.rb +101 -0
  179. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  180. data/lib/active_record/timestamp.rb +23 -16
  181. data/lib/active_record/transactions.rb +125 -79
  182. data/lib/active_record/type/big_integer.rb +13 -0
  183. data/lib/active_record/type/binary.rb +50 -0
  184. data/lib/active_record/type/boolean.rb +31 -0
  185. data/lib/active_record/type/date.rb +50 -0
  186. data/lib/active_record/type/date_time.rb +54 -0
  187. data/lib/active_record/type/decimal.rb +64 -0
  188. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  189. data/lib/active_record/type/decorator.rb +14 -0
  190. data/lib/active_record/type/float.rb +19 -0
  191. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  192. data/lib/active_record/type/integer.rb +59 -0
  193. data/lib/active_record/type/mutable.rb +16 -0
  194. data/lib/active_record/type/numeric.rb +36 -0
  195. data/lib/active_record/type/serialized.rb +62 -0
  196. data/lib/active_record/type/string.rb +40 -0
  197. data/lib/active_record/type/text.rb +11 -0
  198. data/lib/active_record/type/time.rb +26 -0
  199. data/lib/active_record/type/time_value.rb +38 -0
  200. data/lib/active_record/type/type_map.rb +64 -0
  201. data/lib/active_record/type/unsigned_integer.rb +15 -0
  202. data/lib/active_record/type/value.rb +110 -0
  203. data/lib/active_record/type.rb +23 -0
  204. data/lib/active_record/validations/associated.rb +24 -16
  205. data/lib/active_record/validations/presence.rb +67 -0
  206. data/lib/active_record/validations/uniqueness.rb +123 -64
  207. data/lib/active_record/validations.rb +36 -29
  208. data/lib/active_record/version.rb +5 -7
  209. data/lib/active_record.rb +66 -46
  210. data/lib/rails/generators/active_record/migration/migration_generator.rb +53 -8
  211. data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +5 -1
  212. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  213. data/lib/rails/generators/active_record/migration.rb +11 -8
  214. data/lib/rails/generators/active_record/model/model_generator.rb +9 -4
  215. data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
  216. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  217. data/lib/rails/generators/active_record.rb +3 -11
  218. metadata +101 -45
  219. data/examples/associations.png +0 -0
  220. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  221. data/lib/active_record/associations/join_helper.rb +0 -55
  222. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  223. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  224. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  225. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  226. data/lib/active_record/dynamic_finder_match.rb +0 -68
  227. data/lib/active_record/dynamic_scope_match.rb +0 -23
  228. data/lib/active_record/fixtures/file.rb +0 -65
  229. data/lib/active_record/identity_map.rb +0 -162
  230. data/lib/active_record/observer.rb +0 -121
  231. data/lib/active_record/session_store.rb +0 -360
  232. data/lib/active_record/test_case.rb +0 -73
  233. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  234. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  235. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  236. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -13,97 +13,36 @@ module ActiveRecord
13
13
  ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
14
14
  end
15
15
 
16
- attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale
17
- attr_accessor :primary, :coder
16
+ attr_reader :name, :cast_type, :null, :sql_type, :default, :default_function
18
17
 
19
- alias :encoded? :coder
18
+ delegate :type, :precision, :scale, :limit, :klass, :accessor,
19
+ :text?, :number?, :binary?, :changed?,
20
+ :type_cast_from_user, :type_cast_from_database, :type_cast_for_database,
21
+ :type_cast_for_schema,
22
+ to: :cast_type
20
23
 
21
24
  # Instantiates a new column in the table.
22
25
  #
23
26
  # +name+ is the column's name, such as <tt>supplier_id</tt> in <tt>supplier_id int(11)</tt>.
24
27
  # +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
28
+ # +cast_type+ is the object used for type casting and type information.
25
29
  # +sql_type+ is used to extract the column's length, if necessary. For example +60+ in
26
30
  # <tt>company_name varchar(60)</tt>.
27
31
  # It will be mapped to one of the standard Rails SQL types in the <tt>type</tt> attribute.
28
32
  # +null+ determines if this column allows +NULL+ values.
29
- def initialize(name, default, sql_type = nil, null = true)
30
- @name = name
31
- @sql_type = sql_type
32
- @null = null
33
- @limit = extract_limit(sql_type)
34
- @precision = extract_precision(sql_type)
35
- @scale = extract_scale(sql_type)
36
- @type = simplified_type(sql_type)
37
- @default = extract_default(default)
38
- @primary = nil
39
- @coder = nil
40
- end
41
-
42
- # Returns +true+ if the column is either of type string or text.
43
- def text?
44
- type == :string || type == :text
45
- end
46
-
47
- # Returns +true+ if the column is either of type integer, float or decimal.
48
- def number?
49
- type == :integer || type == :float || type == :decimal
33
+ def initialize(name, default, cast_type, sql_type = nil, null = true)
34
+ @name = name.freeze
35
+ @cast_type = cast_type
36
+ @sql_type = sql_type
37
+ @null = null
38
+ @default = default
39
+ @default_function = nil
50
40
  end
51
41
 
52
42
  def has_default?
53
43
  !default.nil?
54
44
  end
55
45
 
56
- # Returns the Ruby class that corresponds to the abstract data type.
57
- def klass
58
- case type
59
- when :integer then Fixnum
60
- when :float then Float
61
- when :decimal then BigDecimal
62
- when :datetime, :timestamp, :time then Time
63
- when :date then Date
64
- when :text, :string, :binary then String
65
- when :boolean then Object
66
- end
67
- end
68
-
69
- # Casts value (which is a String) to an appropriate instance.
70
- def type_cast(value)
71
- return nil if value.nil?
72
- return coder.load(value) if encoded?
73
-
74
- klass = self.class
75
-
76
- case type
77
- when :string, :text then value
78
- when :integer then klass.value_to_integer(value)
79
- when :float then value.to_f
80
- when :decimal then klass.value_to_decimal(value)
81
- when :datetime, :timestamp then klass.string_to_time(value)
82
- when :time then klass.string_to_dummy_time(value)
83
- when :date then klass.string_to_date(value)
84
- when :binary then klass.binary_to_string(value)
85
- when :boolean then klass.value_to_boolean(value)
86
- else value
87
- end
88
- end
89
-
90
- def type_cast_code(var_name)
91
- klass = self.class.name
92
-
93
- case type
94
- when :string, :text then var_name
95
- when :integer then "#{klass}.value_to_integer(#{var_name})"
96
- when :float then "#{var_name}.to_f"
97
- when :decimal then "#{klass}.value_to_decimal(#{var_name})"
98
- when :datetime, :timestamp then "#{klass}.string_to_time(#{var_name})"
99
- when :time then "#{klass}.string_to_dummy_time(#{var_name})"
100
- when :date then "#{klass}.string_to_date(#{var_name})"
101
- when :binary then "#{klass}.binary_to_string(#{var_name})"
102
- when :boolean then "#{klass}.value_to_boolean(#{var_name})"
103
- else var_name
104
- end
105
- end
106
-
107
46
  # Returns the human name of the column name.
108
47
  #
109
48
  # ===== Examples
@@ -112,184 +51,31 @@ module ActiveRecord
112
51
  Base.human_attribute_name(@name)
113
52
  end
114
53
 
115
- def extract_default(default)
116
- type_cast(default)
54
+ def with_type(type)
55
+ dup.tap do |clone|
56
+ clone.instance_variable_set('@cast_type', type)
57
+ end
117
58
  end
118
59
 
119
- # Used to convert from Strings to BLOBs
120
- def string_to_binary(value)
121
- self.class.string_to_binary(value)
60
+ def ==(other)
61
+ other.name == name &&
62
+ other.default == default &&
63
+ other.cast_type == cast_type &&
64
+ other.sql_type == sql_type &&
65
+ other.null == null &&
66
+ other.default_function == default_function
122
67
  end
68
+ alias :eql? :==
123
69
 
124
- class << self
125
- # Used to convert from Strings to BLOBs
126
- def string_to_binary(value)
127
- value
128
- end
129
-
130
- # Used to convert from BLOBs to Strings
131
- def binary_to_string(value)
132
- value
133
- end
134
-
135
- def string_to_date(string)
136
- return string unless string.is_a?(String)
137
- return nil if string.empty?
138
-
139
- fast_string_to_date(string) || fallback_string_to_date(string)
140
- end
141
-
142
- def string_to_time(string)
143
- return string unless string.is_a?(String)
144
- return nil if string.empty?
145
-
146
- fast_string_to_time(string) || fallback_string_to_time(string)
147
- end
148
-
149
- def string_to_dummy_time(string)
150
- return string unless string.is_a?(String)
151
- return nil if string.empty?
152
-
153
- dummy_time_string = "2000-01-01 #{string}"
154
-
155
- fast_string_to_time(dummy_time_string) || begin
156
- time_hash = Date._parse(dummy_time_string)
157
- return nil if time_hash[:hour].nil?
158
- new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction))
159
- end
160
- end
161
-
162
- # convert something to a boolean
163
- def value_to_boolean(value)
164
- if value.is_a?(String) && value.blank?
165
- nil
166
- else
167
- TRUE_VALUES.include?(value)
168
- end
169
- end
170
-
171
- # Used to convert values to integer.
172
- # handle the case when an integer column is used to store boolean values
173
- def value_to_integer(value)
174
- case value
175
- when TrueClass, FalseClass
176
- value ? 1 : 0
177
- else
178
- value.to_i rescue nil
179
- end
180
- end
181
-
182
- # convert something to a BigDecimal
183
- def value_to_decimal(value)
184
- # Using .class is faster than .is_a? and
185
- # subclasses of BigDecimal will be handled
186
- # in the else clause
187
- if value.class == BigDecimal
188
- value
189
- elsif value.respond_to?(:to_d)
190
- value.to_d
191
- else
192
- value.to_s.to_d
193
- end
194
- end
195
-
196
- protected
197
- # '0.123456' -> 123456
198
- # '1.123456' -> 123456
199
- def microseconds(time)
200
- time[:sec_fraction] ? (time[:sec_fraction] * 1_000_000).to_i : 0
201
- end
202
-
203
- def new_date(year, mon, mday)
204
- if year && year != 0
205
- Date.new(year, mon, mday) rescue nil
206
- end
207
- end
208
-
209
- def new_time(year, mon, mday, hour, min, sec, microsec)
210
- # Treat 0000-00-00 00:00:00 as nil.
211
- return nil if year.nil? || (year == 0 && mon == 0 && mday == 0)
212
-
213
- Time.time_with_datetime_fallback(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
214
- end
215
-
216
- def fast_string_to_date(string)
217
- if string =~ Format::ISO_DATE
218
- new_date $1.to_i, $2.to_i, $3.to_i
219
- end
220
- end
221
-
222
- if RUBY_VERSION >= '1.9'
223
- # Doesn't handle time zones.
224
- def fast_string_to_time(string)
225
- if string =~ Format::ISO_DATETIME
226
- microsec = ($7.to_r * 1_000_000).to_i
227
- new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
228
- end
229
- end
230
- else
231
- def fast_string_to_time(string)
232
- if string =~ Format::ISO_DATETIME
233
- microsec = ($7.to_f * 1_000_000).round.to_i
234
- new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
235
- end
236
- end
237
- end
238
-
239
- def fallback_string_to_date(string)
240
- new_date(*::Date._parse(string, false).values_at(:year, :mon, :mday))
241
- end
242
-
243
- def fallback_string_to_time(string)
244
- time_hash = Date._parse(string)
245
- time_hash[:sec_fraction] = microseconds(time_hash)
246
-
247
- new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction))
248
- end
70
+ def hash
71
+ attributes_for_hash.hash
249
72
  end
250
73
 
251
74
  private
252
- def extract_limit(sql_type)
253
- $1.to_i if sql_type =~ /\((.*)\)/
254
- end
255
75
 
256
- def extract_precision(sql_type)
257
- $2.to_i if sql_type =~ /^(numeric|decimal|number)\((\d+)(,\d+)?\)/i
258
- end
259
-
260
- def extract_scale(sql_type)
261
- case sql_type
262
- when /^(numeric|decimal|number)\((\d+)\)/i then 0
263
- when /^(numeric|decimal|number)\((\d+)(,(\d+))\)/i then $4.to_i
264
- end
265
- end
266
-
267
- def simplified_type(field_type)
268
- case field_type
269
- when /int/i
270
- :integer
271
- when /float|double/i
272
- :float
273
- when /decimal|numeric|number/i
274
- extract_scale(field_type) == 0 ? :integer : :decimal
275
- when /datetime/i
276
- :datetime
277
- when /timestamp/i
278
- :timestamp
279
- when /time/i
280
- :time
281
- when /date/i
282
- :date
283
- when /clob/i, /text/i
284
- :text
285
- when /blob/i, /binary/i
286
- :binary
287
- when /char/i, /string/i
288
- :string
289
- when /boolean/i
290
- :boolean
291
- end
292
- end
76
+ def attributes_for_hash
77
+ [self.class, name, default, cast_type, sql_type, null, default_function]
78
+ end
293
79
  end
294
80
  end
295
81
  # :startdoc:
@@ -0,0 +1,275 @@
1
+ require 'uri'
2
+ require 'active_support/core_ext/string/filters'
3
+
4
+ module ActiveRecord
5
+ module ConnectionAdapters
6
+ class ConnectionSpecification #:nodoc:
7
+ attr_reader :config, :adapter_method
8
+
9
+ def initialize(config, adapter_method)
10
+ @config, @adapter_method = config, adapter_method
11
+ end
12
+
13
+ def initialize_dup(original)
14
+ @config = original.config.dup
15
+ end
16
+
17
+ # Expands a connection string into a hash.
18
+ class ConnectionUrlResolver # :nodoc:
19
+
20
+ # == Example
21
+ #
22
+ # url = "postgresql://foo:bar@localhost:9000/foo_test?pool=5&timeout=3000"
23
+ # ConnectionUrlResolver.new(url).to_hash
24
+ # # => {
25
+ # "adapter" => "postgresql",
26
+ # "host" => "localhost",
27
+ # "port" => 9000,
28
+ # "database" => "foo_test",
29
+ # "username" => "foo",
30
+ # "password" => "bar",
31
+ # "pool" => "5",
32
+ # "timeout" => "3000"
33
+ # }
34
+ def initialize(url)
35
+ raise "Database URL cannot be empty" if url.blank?
36
+ @uri = uri_parser.parse(url)
37
+ @adapter = @uri.scheme.tr('-', '_')
38
+ @adapter = "postgresql" if @adapter == "postgres"
39
+
40
+ if @uri.opaque
41
+ @uri.opaque, @query = @uri.opaque.split('?', 2)
42
+ else
43
+ @query = @uri.query
44
+ end
45
+ end
46
+
47
+ # Converts the given URL to a full connection hash.
48
+ def to_hash
49
+ config = raw_config.reject { |_,value| value.blank? }
50
+ config.map { |key,value| config[key] = uri_parser.unescape(value) if value.is_a? String }
51
+ config
52
+ end
53
+
54
+ private
55
+
56
+ def uri
57
+ @uri
58
+ end
59
+
60
+ def uri_parser
61
+ @uri_parser ||= URI::Parser.new
62
+ end
63
+
64
+ # Converts the query parameters of the URI into a hash.
65
+ #
66
+ # "localhost?pool=5&reaping_frequency=2"
67
+ # # => { "pool" => "5", "reaping_frequency" => "2" }
68
+ #
69
+ # returns empty hash if no query present.
70
+ #
71
+ # "localhost"
72
+ # # => {}
73
+ def query_hash
74
+ Hash[(@query || '').split("&").map { |pair| pair.split("=") }]
75
+ end
76
+
77
+ def raw_config
78
+ if uri.opaque
79
+ query_hash.merge({
80
+ "adapter" => @adapter,
81
+ "database" => uri.opaque })
82
+ else
83
+ query_hash.merge({
84
+ "adapter" => @adapter,
85
+ "username" => uri.user,
86
+ "password" => uri.password,
87
+ "port" => uri.port,
88
+ "database" => database_from_path,
89
+ "host" => uri.hostname })
90
+ end
91
+ end
92
+
93
+ # Returns name of the database.
94
+ def database_from_path
95
+ if @adapter == 'sqlite3'
96
+ # 'sqlite3:/foo' is absolute, because that makes sense. The
97
+ # corresponding relative version, 'sqlite3:foo', is handled
98
+ # elsewhere, as an "opaque".
99
+
100
+ uri.path
101
+ else
102
+ # Only SQLite uses a filename as the "database" name; for
103
+ # anything else, a leading slash would be silly.
104
+
105
+ uri.path.sub(%r{^/}, "")
106
+ end
107
+ end
108
+ end
109
+
110
+ ##
111
+ # Builds a ConnectionSpecification from user input.
112
+ class Resolver # :nodoc:
113
+ attr_reader :configurations
114
+
115
+ # Accepts a hash two layers deep, keys on the first layer represent
116
+ # environments such as "production". Keys must be strings.
117
+ def initialize(configurations)
118
+ @configurations = configurations
119
+ end
120
+
121
+ # Returns a hash with database connection information.
122
+ #
123
+ # == Examples
124
+ #
125
+ # Full hash Configuration.
126
+ #
127
+ # configurations = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
128
+ # Resolver.new(configurations).resolve(:production)
129
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3"}
130
+ #
131
+ # Initialized with URL configuration strings.
132
+ #
133
+ # configurations = { "production" => "postgresql://localhost/foo" }
134
+ # Resolver.new(configurations).resolve(:production)
135
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
136
+ #
137
+ def resolve(config)
138
+ if config
139
+ resolve_connection config
140
+ elsif env = ActiveRecord::ConnectionHandling::RAILS_ENV.call
141
+ resolve_symbol_connection env.to_sym
142
+ else
143
+ raise AdapterNotSpecified
144
+ end
145
+ end
146
+
147
+ # Expands each key in @configurations hash into fully resolved hash
148
+ def resolve_all
149
+ config = configurations.dup
150
+ config.each do |key, value|
151
+ config[key] = resolve(value) if value
152
+ end
153
+ config
154
+ end
155
+
156
+ # Returns an instance of ConnectionSpecification for a given adapter.
157
+ # Accepts a hash one layer deep that contains all connection information.
158
+ #
159
+ # == Example
160
+ #
161
+ # config = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
162
+ # spec = Resolver.new(config).spec(:production)
163
+ # spec.adapter_method
164
+ # # => "sqlite3_connection"
165
+ # spec.config
166
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" }
167
+ #
168
+ def spec(config)
169
+ spec = resolve(config).symbolize_keys
170
+
171
+ raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter)
172
+
173
+ path_to_adapter = "active_record/connection_adapters/#{spec[:adapter]}_adapter"
174
+ begin
175
+ require path_to_adapter
176
+ rescue Gem::LoadError => e
177
+ raise Gem::LoadError, "Specified '#{spec[:adapter]}' for database adapter, but the gem is not loaded. Add `gem '#{e.name}'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord)."
178
+ rescue LoadError => e
179
+ raise LoadError, "Could not load '#{path_to_adapter}'. Make sure that the adapter in config/database.yml is valid. If you use an adapter other than 'mysql', 'mysql2', 'postgresql' or 'sqlite3' add the necessary adapter gem to the Gemfile.", e.backtrace
180
+ end
181
+
182
+ adapter_method = "#{spec[:adapter]}_connection"
183
+ ConnectionSpecification.new(spec, adapter_method)
184
+ end
185
+
186
+ private
187
+
188
+ # Returns fully resolved connection, accepts hash, string or symbol.
189
+ # Always returns a hash.
190
+ #
191
+ # == Examples
192
+ #
193
+ # Symbol representing current environment.
194
+ #
195
+ # Resolver.new("production" => {}).resolve_connection(:production)
196
+ # # => {}
197
+ #
198
+ # One layer deep hash of connection values.
199
+ #
200
+ # Resolver.new({}).resolve_connection("adapter" => "sqlite3")
201
+ # # => { "adapter" => "sqlite3" }
202
+ #
203
+ # Connection URL.
204
+ #
205
+ # Resolver.new({}).resolve_connection("postgresql://localhost/foo")
206
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
207
+ #
208
+ def resolve_connection(spec)
209
+ case spec
210
+ when Symbol
211
+ resolve_symbol_connection spec
212
+ when String
213
+ resolve_string_connection spec
214
+ when Hash
215
+ resolve_hash_connection spec
216
+ end
217
+ end
218
+
219
+ def resolve_string_connection(spec)
220
+ # Rails has historically accepted a string to mean either
221
+ # an environment key or a URL spec, so we have deprecated
222
+ # this ambiguous behaviour and in the future this function
223
+ # can be removed in favor of resolve_url_connection.
224
+ if configurations.key?(spec) || spec !~ /:/
225
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
226
+ Passing a string to ActiveRecord::Base.establish_connection for a
227
+ configuration lookup is deprecated, please pass a symbol
228
+ (#{spec.to_sym.inspect}) instead.
229
+ MSG
230
+
231
+ resolve_symbol_connection(spec)
232
+ else
233
+ resolve_url_connection(spec)
234
+ end
235
+ end
236
+
237
+ # Takes the environment such as +:production+ or +:development+.
238
+ # This requires that the @configurations was initialized with a key that
239
+ # matches.
240
+ #
241
+ # Resolver.new("production" => {}).resolve_symbol_connection(:production)
242
+ # # => {}
243
+ #
244
+ def resolve_symbol_connection(spec)
245
+ if config = configurations[spec.to_s]
246
+ resolve_connection(config)
247
+ else
248
+ raise(AdapterNotSpecified, "'#{spec}' database is not configured. Available: #{configurations.keys.inspect}")
249
+ end
250
+ end
251
+
252
+ # Accepts a hash. Expands the "url" key that contains a
253
+ # URL database connection to a full connection
254
+ # hash and merges with the rest of the hash.
255
+ # Connection details inside of the "url" key win any merge conflicts
256
+ def resolve_hash_connection(spec)
257
+ if spec["url"] && spec["url"] !~ /^jdbc:/
258
+ connection_hash = resolve_url_connection(spec.delete("url"))
259
+ spec.merge!(connection_hash)
260
+ end
261
+ spec
262
+ end
263
+
264
+ # Takes a connection URL.
265
+ #
266
+ # Resolver.new({}).resolve_url_connection("postgresql://localhost/foo")
267
+ # # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
268
+ #
269
+ def resolve_url_connection(url)
270
+ ConnectionUrlResolver.new(url).to_hash
271
+ end
272
+ end
273
+ end
274
+ end
275
+ end