activerecord 3.1.10 → 4.2.11

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.

Potentially problematic release.


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

Files changed (237) hide show
  1. checksums.yaml +6 -6
  2. data/CHANGELOG.md +1837 -338
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +39 -43
  5. data/examples/performance.rb +51 -20
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +57 -43
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -39
  10. data/lib/active_record/associations/association.rb +71 -85
  11. data/lib/active_record/associations/association_scope.rb +138 -89
  12. data/lib/active_record/associations/belongs_to_association.rb +65 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -3
  14. data/lib/active_record/associations/builder/association.rb +125 -29
  15. data/lib/active_record/associations/builder/belongs_to.rb +91 -60
  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 -52
  20. data/lib/active_record/associations/builder/singular_association.rb +22 -29
  21. data/lib/active_record/associations/collection_association.rb +294 -187
  22. data/lib/active_record/associations/collection_proxy.rb +961 -94
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +118 -23
  25. data/lib/active_record/associations/has_many_through_association.rb +115 -45
  26. data/lib/active_record/associations/has_one_association.rb +57 -24
  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 -102
  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 +61 -32
  38. data/lib/active_record/associations/preloader.rb +113 -87
  39. data/lib/active_record/associations/singular_association.rb +29 -13
  40. data/lib/active_record/associations/through_association.rb +37 -19
  41. data/lib/active_record/associations.rb +505 -371
  42. data/lib/active_record/attribute.rb +163 -0
  43. data/lib/active_record/attribute_assignment.rb +212 -0
  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 +141 -51
  47. data/lib/active_record/attribute_methods/primary_key.rb +87 -36
  48. data/lib/active_record/attribute_methods/query.rb +5 -4
  49. data/lib/active_record/attribute_methods/read.rb +74 -117
  50. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  51. data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -47
  52. data/lib/active_record/attribute_methods/write.rb +60 -21
  53. data/lib/active_record/attribute_methods.rb +409 -48
  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 +279 -232
  58. data/lib/active_record/base.rb +84 -1969
  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 +422 -243
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +170 -194
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -19
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -57
  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 +273 -170
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +731 -254
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +339 -95
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +946 -0
  75. data/lib/active_record/connection_adapters/column.rb +33 -221
  76. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +140 -602
  78. data/lib/active_record/connection_adapters/mysql_adapter.rb +254 -756
  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 +445 -902
  114. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +578 -25
  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 +159 -102
  119. data/lib/active_record/dynamic_matchers.rb +140 -0
  120. data/lib/active_record/enum.rb +197 -0
  121. data/lib/active_record/errors.rb +102 -34
  122. data/lib/active_record/explain.rb +38 -0
  123. data/lib/active_record/explain_registry.rb +30 -0
  124. data/lib/active_record/explain_subscriber.rb +29 -0
  125. data/lib/active_record/fixture_set/file.rb +56 -0
  126. data/lib/active_record/fixtures.rb +318 -260
  127. data/lib/active_record/gem_version.rb +15 -0
  128. data/lib/active_record/inheritance.rb +247 -0
  129. data/lib/active_record/integration.rb +113 -0
  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 +80 -52
  133. data/lib/active_record/locking/pessimistic.rb +27 -5
  134. data/lib/active_record/log_subscriber.rb +25 -18
  135. data/lib/active_record/migration/command_recorder.rb +130 -38
  136. data/lib/active_record/migration/join_table.rb +15 -0
  137. data/lib/active_record/migration.rb +532 -201
  138. data/lib/active_record/model_schema.rb +342 -0
  139. data/lib/active_record/nested_attributes.rb +229 -139
  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 +304 -99
  143. data/lib/active_record/query_cache.rb +25 -43
  144. data/lib/active_record/querying.rb +68 -0
  145. data/lib/active_record/railtie.rb +86 -45
  146. data/lib/active_record/railties/console_sandbox.rb +3 -4
  147. data/lib/active_record/railties/controller_runtime.rb +7 -4
  148. data/lib/active_record/railties/databases.rake +198 -377
  149. data/lib/active_record/railties/jdbcmysql_error.rb +2 -2
  150. data/lib/active_record/readonly_attributes.rb +23 -0
  151. data/lib/active_record/reflection.rb +516 -165
  152. data/lib/active_record/relation/batches.rb +96 -45
  153. data/lib/active_record/relation/calculations.rb +221 -144
  154. data/lib/active_record/relation/delegation.rb +140 -0
  155. data/lib/active_record/relation/finder_methods.rb +362 -243
  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 -41
  160. data/lib/active_record/relation/query_methods.rb +982 -155
  161. data/lib/active_record/relation/spawn_methods.rb +50 -110
  162. data/lib/active_record/relation.rb +371 -180
  163. data/lib/active_record/result.rb +109 -12
  164. data/lib/active_record/runtime_registry.rb +22 -0
  165. data/lib/active_record/sanitization.rb +191 -0
  166. data/lib/active_record/schema.rb +19 -13
  167. data/lib/active_record/schema_dumper.rb +111 -61
  168. data/lib/active_record/schema_migration.rb +53 -0
  169. data/lib/active_record/scoping/default.rb +135 -0
  170. data/lib/active_record/scoping/named.rb +164 -0
  171. data/lib/active_record/scoping.rb +87 -0
  172. data/lib/active_record/serialization.rb +7 -45
  173. data/lib/active_record/serializers/xml_serializer.rb +14 -65
  174. data/lib/active_record/statement_cache.rb +111 -0
  175. data/lib/active_record/store.rb +205 -0
  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 +35 -14
  181. data/lib/active_record/transactions.rb +141 -74
  182. data/lib/active_record/translation.rb +22 -0
  183. data/lib/active_record/type/big_integer.rb +13 -0
  184. data/lib/active_record/type/binary.rb +50 -0
  185. data/lib/active_record/type/boolean.rb +31 -0
  186. data/lib/active_record/type/date.rb +50 -0
  187. data/lib/active_record/type/date_time.rb +54 -0
  188. data/lib/active_record/type/decimal.rb +64 -0
  189. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  190. data/lib/active_record/type/decorator.rb +14 -0
  191. data/lib/active_record/type/float.rb +19 -0
  192. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  193. data/lib/active_record/type/integer.rb +59 -0
  194. data/lib/active_record/type/mutable.rb +16 -0
  195. data/lib/active_record/type/numeric.rb +36 -0
  196. data/lib/active_record/type/serialized.rb +62 -0
  197. data/lib/active_record/type/string.rb +40 -0
  198. data/lib/active_record/type/text.rb +11 -0
  199. data/lib/active_record/type/time.rb +26 -0
  200. data/lib/active_record/type/time_value.rb +38 -0
  201. data/lib/active_record/type/type_map.rb +64 -0
  202. data/lib/active_record/type/unsigned_integer.rb +15 -0
  203. data/lib/active_record/type/value.rb +110 -0
  204. data/lib/active_record/type.rb +23 -0
  205. data/lib/active_record/validations/associated.rb +27 -18
  206. data/lib/active_record/validations/presence.rb +67 -0
  207. data/lib/active_record/validations/uniqueness.rb +125 -66
  208. data/lib/active_record/validations.rb +37 -30
  209. data/lib/active_record/version.rb +5 -7
  210. data/lib/active_record.rb +80 -25
  211. data/lib/rails/generators/active_record/migration/migration_generator.rb +54 -9
  212. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  213. data/lib/rails/generators/active_record/migration/templates/migration.rb +25 -11
  214. data/lib/rails/generators/active_record/migration.rb +11 -8
  215. data/lib/rails/generators/active_record/model/model_generator.rb +17 -4
  216. data/lib/rails/generators/active_record/model/templates/model.rb +5 -2
  217. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  218. data/lib/rails/generators/active_record.rb +3 -11
  219. metadata +132 -53
  220. data/examples/associations.png +0 -0
  221. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -62
  222. data/lib/active_record/associations/join_helper.rb +0 -55
  223. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  224. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -135
  225. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -556
  226. data/lib/active_record/dynamic_finder_match.rb +0 -56
  227. data/lib/active_record/dynamic_scope_match.rb +0 -23
  228. data/lib/active_record/identity_map.rb +0 -163
  229. data/lib/active_record/named_scope.rb +0 -200
  230. data/lib/active_record/observer.rb +0 -121
  231. data/lib/active_record/session_store.rb +0 -358
  232. data/lib/active_record/test_case.rb +0 -69
  233. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -17
  234. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  235. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  236. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  237. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
@@ -5,105 +5,44 @@ module ActiveRecord
5
5
  module ConnectionAdapters
6
6
  # An abstract definition of a column in a table.
7
7
  class Column
8
- TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].to_set
9
- FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE'].to_set
8
+ TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON'].to_set
9
+ FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF'].to_set
10
10
 
11
11
  module Format
12
12
  ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
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 value.to_i rescue value ? 1 : 0
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 "(#{var_name}.to_i rescue #{var_name} ? 1 : 0)"
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,158 +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
- string_to_time "2000-01-01 #{string}"
154
- end
155
-
156
- # convert something to a boolean
157
- def value_to_boolean(value)
158
- if value.is_a?(String) && value.blank?
159
- nil
160
- else
161
- TRUE_VALUES.include?(value)
162
- end
163
- end
164
-
165
- # convert something to a BigDecimal
166
- def value_to_decimal(value)
167
- # Using .class is faster than .is_a? and
168
- # subclasses of BigDecimal will be handled
169
- # in the else clause
170
- if value.class == BigDecimal
171
- value
172
- elsif value.respond_to?(:to_d)
173
- value.to_d
174
- else
175
- value.to_s.to_d
176
- end
177
- end
178
-
179
- protected
180
- # '0.123456' -> 123456
181
- # '1.123456' -> 123456
182
- def microseconds(time)
183
- ((time[:sec_fraction].to_f % 1) * 1_000_000).to_i
184
- end
185
-
186
- def new_date(year, mon, mday)
187
- if year && year != 0
188
- Date.new(year, mon, mday) rescue nil
189
- end
190
- end
191
-
192
- def new_time(year, mon, mday, hour, min, sec, microsec)
193
- # Treat 0000-00-00 00:00:00 as nil.
194
- return nil if year.nil? || (year == 0 && mon == 0 && mday == 0)
195
-
196
- Time.time_with_datetime_fallback(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
197
- end
198
-
199
- def fast_string_to_date(string)
200
- if string =~ Format::ISO_DATE
201
- new_date $1.to_i, $2.to_i, $3.to_i
202
- end
203
- end
204
-
205
- # Doesn't handle time zones.
206
- def fast_string_to_time(string)
207
- if string =~ Format::ISO_DATETIME
208
- microsec = ($7.to_f * 1_000_000).to_i
209
- new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
210
- end
211
- end
212
-
213
- def fallback_string_to_date(string)
214
- new_date(*::Date._parse(string, false).values_at(:year, :mon, :mday))
215
- end
216
-
217
- def fallback_string_to_time(string)
218
- time_hash = Date._parse(string)
219
- time_hash[:sec_fraction] = microseconds(time_hash)
220
-
221
- new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction))
222
- end
70
+ def hash
71
+ attributes_for_hash.hash
223
72
  end
224
73
 
225
74
  private
226
- def extract_limit(sql_type)
227
- $1.to_i if sql_type =~ /\((.*)\)/
228
- end
229
75
 
230
- def extract_precision(sql_type)
231
- $2.to_i if sql_type =~ /^(numeric|decimal|number)\((\d+)(,\d+)?\)/i
232
- end
233
-
234
- def extract_scale(sql_type)
235
- case sql_type
236
- when /^(numeric|decimal|number)\((\d+)\)/i then 0
237
- when /^(numeric|decimal|number)\((\d+)(,(\d+))\)/i then $4.to_i
238
- end
239
- end
240
-
241
- def simplified_type(field_type)
242
- case field_type
243
- when /int/i
244
- :integer
245
- when /float|double/i
246
- :float
247
- when /decimal|numeric|number/i
248
- extract_scale(field_type) == 0 ? :integer : :decimal
249
- when /datetime/i
250
- :datetime
251
- when /timestamp/i
252
- :timestamp
253
- when /time/i
254
- :time
255
- when /date/i
256
- :date
257
- when /clob/i, /text/i
258
- :text
259
- when /blob/i, /binary/i
260
- :binary
261
- when /char/i, /string/i
262
- :string
263
- when /boolean/i
264
- :boolean
265
- end
266
- end
76
+ def attributes_for_hash
77
+ [self.class, name, default, cast_type, sql_type, null, default_function]
78
+ end
267
79
  end
268
80
  end
269
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