activerecord 4.2.0

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 (221) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1372 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +218 -0
  5. data/examples/performance.rb +184 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record.rb +173 -0
  8. data/lib/active_record/aggregations.rb +266 -0
  9. data/lib/active_record/association_relation.rb +22 -0
  10. data/lib/active_record/associations.rb +1724 -0
  11. data/lib/active_record/associations/alias_tracker.rb +87 -0
  12. data/lib/active_record/associations/association.rb +253 -0
  13. data/lib/active_record/associations/association_scope.rb +194 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +111 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
  16. data/lib/active_record/associations/builder/association.rb +149 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +116 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +91 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +124 -0
  20. data/lib/active_record/associations/builder/has_many.rb +15 -0
  21. data/lib/active_record/associations/builder/has_one.rb +23 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +38 -0
  23. data/lib/active_record/associations/collection_association.rb +634 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1027 -0
  25. data/lib/active_record/associations/has_many_association.rb +184 -0
  26. data/lib/active_record/associations/has_many_through_association.rb +238 -0
  27. data/lib/active_record/associations/has_one_association.rb +105 -0
  28. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  29. data/lib/active_record/associations/join_dependency.rb +282 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +122 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +22 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/preloader.rb +203 -0
  34. data/lib/active_record/associations/preloader/association.rb +162 -0
  35. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  36. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  37. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  38. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  39. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  40. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  41. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  42. data/lib/active_record/associations/preloader/through_association.rb +96 -0
  43. data/lib/active_record/associations/singular_association.rb +86 -0
  44. data/lib/active_record/associations/through_association.rb +96 -0
  45. data/lib/active_record/attribute.rb +149 -0
  46. data/lib/active_record/attribute_assignment.rb +212 -0
  47. data/lib/active_record/attribute_decorators.rb +66 -0
  48. data/lib/active_record/attribute_methods.rb +439 -0
  49. data/lib/active_record/attribute_methods/before_type_cast.rb +71 -0
  50. data/lib/active_record/attribute_methods/dirty.rb +181 -0
  51. data/lib/active_record/attribute_methods/primary_key.rb +128 -0
  52. data/lib/active_record/attribute_methods/query.rb +40 -0
  53. data/lib/active_record/attribute_methods/read.rb +103 -0
  54. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  55. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
  56. data/lib/active_record/attribute_methods/write.rb +83 -0
  57. data/lib/active_record/attribute_set.rb +77 -0
  58. data/lib/active_record/attribute_set/builder.rb +86 -0
  59. data/lib/active_record/attributes.rb +139 -0
  60. data/lib/active_record/autosave_association.rb +439 -0
  61. data/lib/active_record/base.rb +317 -0
  62. data/lib/active_record/callbacks.rb +313 -0
  63. data/lib/active_record/coders/json.rb +13 -0
  64. data/lib/active_record/coders/yaml_column.rb +38 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +659 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +373 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +133 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +574 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +991 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +219 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +883 -0
  78. data/lib/active_record/connection_adapters/column.rb +82 -0
  79. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +282 -0
  81. data/lib/active_record/connection_adapters/mysql_adapter.rb +491 -0
  82. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  111. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  112. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +588 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +754 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +628 -0
  119. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  120. data/lib/active_record/connection_handling.rb +132 -0
  121. data/lib/active_record/core.rb +566 -0
  122. data/lib/active_record/counter_cache.rb +175 -0
  123. data/lib/active_record/dynamic_matchers.rb +140 -0
  124. data/lib/active_record/enum.rb +198 -0
  125. data/lib/active_record/errors.rb +252 -0
  126. data/lib/active_record/explain.rb +38 -0
  127. data/lib/active_record/explain_registry.rb +30 -0
  128. data/lib/active_record/explain_subscriber.rb +29 -0
  129. data/lib/active_record/fixture_set/file.rb +56 -0
  130. data/lib/active_record/fixtures.rb +1007 -0
  131. data/lib/active_record/gem_version.rb +15 -0
  132. data/lib/active_record/inheritance.rb +247 -0
  133. data/lib/active_record/integration.rb +113 -0
  134. data/lib/active_record/locale/en.yml +47 -0
  135. data/lib/active_record/locking/optimistic.rb +204 -0
  136. data/lib/active_record/locking/pessimistic.rb +77 -0
  137. data/lib/active_record/log_subscriber.rb +75 -0
  138. data/lib/active_record/migration.rb +1051 -0
  139. data/lib/active_record/migration/command_recorder.rb +197 -0
  140. data/lib/active_record/migration/join_table.rb +15 -0
  141. data/lib/active_record/model_schema.rb +340 -0
  142. data/lib/active_record/nested_attributes.rb +548 -0
  143. data/lib/active_record/no_touching.rb +52 -0
  144. data/lib/active_record/null_relation.rb +81 -0
  145. data/lib/active_record/persistence.rb +532 -0
  146. data/lib/active_record/query_cache.rb +56 -0
  147. data/lib/active_record/querying.rb +68 -0
  148. data/lib/active_record/railtie.rb +162 -0
  149. data/lib/active_record/railties/console_sandbox.rb +5 -0
  150. data/lib/active_record/railties/controller_runtime.rb +50 -0
  151. data/lib/active_record/railties/databases.rake +391 -0
  152. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  153. data/lib/active_record/readonly_attributes.rb +23 -0
  154. data/lib/active_record/reflection.rb +881 -0
  155. data/lib/active_record/relation.rb +681 -0
  156. data/lib/active_record/relation/batches.rb +138 -0
  157. data/lib/active_record/relation/calculations.rb +403 -0
  158. data/lib/active_record/relation/delegation.rb +140 -0
  159. data/lib/active_record/relation/finder_methods.rb +528 -0
  160. data/lib/active_record/relation/merger.rb +170 -0
  161. data/lib/active_record/relation/predicate_builder.rb +126 -0
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +47 -0
  163. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  164. data/lib/active_record/relation/query_methods.rb +1176 -0
  165. data/lib/active_record/relation/spawn_methods.rb +75 -0
  166. data/lib/active_record/result.rb +131 -0
  167. data/lib/active_record/runtime_registry.rb +22 -0
  168. data/lib/active_record/sanitization.rb +191 -0
  169. data/lib/active_record/schema.rb +64 -0
  170. data/lib/active_record/schema_dumper.rb +251 -0
  171. data/lib/active_record/schema_migration.rb +56 -0
  172. data/lib/active_record/scoping.rb +87 -0
  173. data/lib/active_record/scoping/default.rb +134 -0
  174. data/lib/active_record/scoping/named.rb +164 -0
  175. data/lib/active_record/serialization.rb +22 -0
  176. data/lib/active_record/serializers/xml_serializer.rb +193 -0
  177. data/lib/active_record/statement_cache.rb +111 -0
  178. data/lib/active_record/store.rb +205 -0
  179. data/lib/active_record/tasks/database_tasks.rb +296 -0
  180. data/lib/active_record/tasks/mysql_database_tasks.rb +145 -0
  181. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  182. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  183. data/lib/active_record/timestamp.rb +121 -0
  184. data/lib/active_record/transactions.rb +417 -0
  185. data/lib/active_record/translation.rb +22 -0
  186. data/lib/active_record/type.rb +23 -0
  187. data/lib/active_record/type/big_integer.rb +13 -0
  188. data/lib/active_record/type/binary.rb +50 -0
  189. data/lib/active_record/type/boolean.rb +30 -0
  190. data/lib/active_record/type/date.rb +46 -0
  191. data/lib/active_record/type/date_time.rb +43 -0
  192. data/lib/active_record/type/decimal.rb +40 -0
  193. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  194. data/lib/active_record/type/decorator.rb +14 -0
  195. data/lib/active_record/type/float.rb +19 -0
  196. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  197. data/lib/active_record/type/integer.rb +55 -0
  198. data/lib/active_record/type/mutable.rb +16 -0
  199. data/lib/active_record/type/numeric.rb +36 -0
  200. data/lib/active_record/type/serialized.rb +56 -0
  201. data/lib/active_record/type/string.rb +36 -0
  202. data/lib/active_record/type/text.rb +11 -0
  203. data/lib/active_record/type/time.rb +26 -0
  204. data/lib/active_record/type/time_value.rb +38 -0
  205. data/lib/active_record/type/type_map.rb +64 -0
  206. data/lib/active_record/type/unsigned_integer.rb +15 -0
  207. data/lib/active_record/type/value.rb +101 -0
  208. data/lib/active_record/validations.rb +90 -0
  209. data/lib/active_record/validations/associated.rb +51 -0
  210. data/lib/active_record/validations/presence.rb +67 -0
  211. data/lib/active_record/validations/uniqueness.rb +229 -0
  212. data/lib/active_record/version.rb +8 -0
  213. data/lib/rails/generators/active_record.rb +17 -0
  214. data/lib/rails/generators/active_record/migration.rb +18 -0
  215. data/lib/rails/generators/active_record/migration/migration_generator.rb +70 -0
  216. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +22 -0
  217. data/lib/rails/generators/active_record/migration/templates/migration.rb +45 -0
  218. data/lib/rails/generators/active_record/model/model_generator.rb +52 -0
  219. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  220. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  221. metadata +309 -0
@@ -0,0 +1,145 @@
1
+ module ActiveRecord
2
+ module Tasks # :nodoc:
3
+ class MySQLDatabaseTasks # :nodoc:
4
+ DEFAULT_CHARSET = ENV['CHARSET'] || 'utf8'
5
+ DEFAULT_COLLATION = ENV['COLLATION'] || 'utf8_unicode_ci'
6
+ ACCESS_DENIED_ERROR = 1045
7
+
8
+ delegate :connection, :establish_connection, to: ActiveRecord::Base
9
+
10
+ def initialize(configuration)
11
+ @configuration = configuration
12
+ end
13
+
14
+ def create
15
+ establish_connection configuration_without_database
16
+ connection.create_database configuration['database'], creation_options
17
+ establish_connection configuration
18
+ rescue ActiveRecord::StatementInvalid => error
19
+ if /database exists/ === error.message
20
+ raise DatabaseAlreadyExists
21
+ else
22
+ raise
23
+ end
24
+ rescue error_class => error
25
+ if error.respond_to?(:errno) && error.errno == ACCESS_DENIED_ERROR
26
+ $stdout.print error.error
27
+ establish_connection root_configuration_without_database
28
+ connection.create_database configuration['database'], creation_options
29
+ if configuration['username'] != 'root'
30
+ connection.execute grant_statement.gsub(/\s+/, ' ').strip
31
+ end
32
+ establish_connection configuration
33
+ else
34
+ $stderr.puts error.inspect
35
+ $stderr.puts "Couldn't create database for #{configuration.inspect}, #{creation_options.inspect}"
36
+ $stderr.puts "(If you set the charset manually, make sure you have a matching collation)" if configuration['encoding']
37
+ end
38
+ end
39
+
40
+ def drop
41
+ establish_connection configuration
42
+ connection.drop_database configuration['database']
43
+ end
44
+
45
+ def purge
46
+ establish_connection configuration
47
+ connection.recreate_database configuration['database'], creation_options
48
+ end
49
+
50
+ def charset
51
+ connection.charset
52
+ end
53
+
54
+ def collation
55
+ connection.collation
56
+ end
57
+
58
+ def structure_dump(filename)
59
+ args = prepare_command_options('mysqldump')
60
+ args.concat(["--result-file", "#{filename}"])
61
+ args.concat(["--no-data"])
62
+ args.concat(["#{configuration['database']}"])
63
+ unless Kernel.system(*args)
64
+ $stderr.puts "Could not dump the database structure. "\
65
+ "Make sure `mysqldump` is in your PATH and check the command output for warnings."
66
+ end
67
+ end
68
+
69
+ def structure_load(filename)
70
+ args = prepare_command_options('mysql')
71
+ args.concat(['--execute', %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}])
72
+ args.concat(["--database", "#{configuration['database']}"])
73
+ Kernel.system(*args)
74
+ end
75
+
76
+ private
77
+
78
+ def configuration
79
+ @configuration
80
+ end
81
+
82
+ def configuration_without_database
83
+ configuration.merge('database' => nil)
84
+ end
85
+
86
+ def creation_options
87
+ Hash.new.tap do |options|
88
+ options[:charset] = configuration['encoding'] if configuration.include? 'encoding'
89
+ options[:collation] = configuration['collation'] if configuration.include? 'collation'
90
+
91
+ # Set default charset only when collation isn't set.
92
+ options[:charset] ||= DEFAULT_CHARSET unless options[:collation]
93
+
94
+ # Set default collation only when charset is also default.
95
+ options[:collation] ||= DEFAULT_COLLATION if options[:charset] == DEFAULT_CHARSET
96
+ end
97
+ end
98
+
99
+ def error_class
100
+ if configuration['adapter'] =~ /jdbc/
101
+ require 'active_record/railties/jdbcmysql_error'
102
+ ArJdbcMySQL::Error
103
+ elsif defined?(Mysql2)
104
+ Mysql2::Error
105
+ elsif defined?(Mysql)
106
+ Mysql::Error
107
+ else
108
+ StandardError
109
+ end
110
+ end
111
+
112
+ def grant_statement
113
+ <<-SQL
114
+ GRANT ALL PRIVILEGES ON #{configuration['database']}.*
115
+ TO '#{configuration['username']}'@'localhost'
116
+ IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
117
+ SQL
118
+ end
119
+
120
+ def root_configuration_without_database
121
+ configuration_without_database.merge(
122
+ 'username' => 'root',
123
+ 'password' => root_password
124
+ )
125
+ end
126
+
127
+ def root_password
128
+ $stdout.print "Please provide the root password for your MySQL installation\n>"
129
+ $stdin.gets.strip
130
+ end
131
+
132
+ def prepare_command_options(command)
133
+ args = [command]
134
+ args.concat(['--user', configuration['username']]) if configuration['username']
135
+ args << "--password=#{configuration['password']}" if configuration['password']
136
+ args.concat(['--default-character-set', configuration['encoding']]) if configuration['encoding']
137
+ configuration.slice('host', 'port', 'socket').each do |k, v|
138
+ args.concat([ "--#{k}", v.to_s ]) if v
139
+ end
140
+
141
+ args
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,90 @@
1
+ require 'shellwords'
2
+
3
+ module ActiveRecord
4
+ module Tasks # :nodoc:
5
+ class PostgreSQLDatabaseTasks # :nodoc:
6
+ DEFAULT_ENCODING = ENV['CHARSET'] || 'utf8'
7
+
8
+ delegate :connection, :establish_connection, :clear_active_connections!,
9
+ to: ActiveRecord::Base
10
+
11
+ def initialize(configuration)
12
+ @configuration = configuration
13
+ end
14
+
15
+ def create(master_established = false)
16
+ establish_master_connection unless master_established
17
+ connection.create_database configuration['database'],
18
+ configuration.merge('encoding' => encoding)
19
+ establish_connection configuration
20
+ rescue ActiveRecord::StatementInvalid => error
21
+ if /database .* already exists/ === error.message
22
+ raise DatabaseAlreadyExists
23
+ else
24
+ raise
25
+ end
26
+ end
27
+
28
+ def drop
29
+ establish_master_connection
30
+ connection.drop_database configuration['database']
31
+ end
32
+
33
+ def charset
34
+ connection.encoding
35
+ end
36
+
37
+ def collation
38
+ connection.collation
39
+ end
40
+
41
+ def purge
42
+ clear_active_connections!
43
+ drop
44
+ create true
45
+ end
46
+
47
+ def structure_dump(filename)
48
+ set_psql_env
49
+ search_path = configuration['schema_search_path']
50
+ unless search_path.blank?
51
+ search_path = search_path.split(",").map{|search_path_part| "--schema=#{Shellwords.escape(search_path_part.strip)}" }.join(" ")
52
+ end
53
+
54
+ command = "pg_dump -i -s -x -O -f #{Shellwords.escape(filename)} #{search_path} #{Shellwords.escape(configuration['database'])}"
55
+ raise 'Error dumping database' unless Kernel.system(command)
56
+
57
+ File.open(filename, "a") { |f| f << "SET search_path TO #{connection.schema_search_path};\n\n" }
58
+ end
59
+
60
+ def structure_load(filename)
61
+ set_psql_env
62
+ Kernel.system("psql -q -f #{Shellwords.escape(filename)} #{configuration['database']}")
63
+ end
64
+
65
+ private
66
+
67
+ def configuration
68
+ @configuration
69
+ end
70
+
71
+ def encoding
72
+ configuration['encoding'] || DEFAULT_ENCODING
73
+ end
74
+
75
+ def establish_master_connection
76
+ establish_connection configuration.merge(
77
+ 'database' => 'postgres',
78
+ 'schema_search_path' => 'public'
79
+ )
80
+ end
81
+
82
+ def set_psql_env
83
+ ENV['PGHOST'] = configuration['host'] if configuration['host']
84
+ ENV['PGPORT'] = configuration['port'].to_s if configuration['port']
85
+ ENV['PGPASSWORD'] = configuration['password'].to_s if configuration['password']
86
+ ENV['PGUSER'] = configuration['username'].to_s if configuration['username']
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,55 @@
1
+ module ActiveRecord
2
+ module Tasks # :nodoc:
3
+ class SQLiteDatabaseTasks # :nodoc:
4
+ delegate :connection, :establish_connection, to: ActiveRecord::Base
5
+
6
+ def initialize(configuration, root = ActiveRecord::Tasks::DatabaseTasks.root)
7
+ @configuration, @root = configuration, root
8
+ end
9
+
10
+ def create
11
+ raise DatabaseAlreadyExists if File.exist?(configuration['database'])
12
+
13
+ establish_connection configuration
14
+ connection
15
+ end
16
+
17
+ def drop
18
+ require 'pathname'
19
+ path = Pathname.new configuration['database']
20
+ file = path.absolute? ? path.to_s : File.join(root, path)
21
+
22
+ FileUtils.rm(file) if File.exist?(file)
23
+ end
24
+
25
+ def purge
26
+ drop
27
+ create
28
+ end
29
+
30
+ def charset
31
+ connection.encoding
32
+ end
33
+
34
+ def structure_dump(filename)
35
+ dbfile = configuration['database']
36
+ `sqlite3 #{dbfile} .schema > #{filename}`
37
+ end
38
+
39
+ def structure_load(filename)
40
+ dbfile = configuration['database']
41
+ `sqlite3 #{dbfile} < "#{filename}"`
42
+ end
43
+
44
+ private
45
+
46
+ def configuration
47
+ @configuration
48
+ end
49
+
50
+ def root
51
+ @root
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,121 @@
1
+ module ActiveRecord
2
+ # = Active Record Timestamp
3
+ #
4
+ # Active Record automatically timestamps create and update operations if the
5
+ # table has fields named <tt>created_at/created_on</tt> or
6
+ # <tt>updated_at/updated_on</tt>.
7
+ #
8
+ # Timestamping can be turned off by setting:
9
+ #
10
+ # config.active_record.record_timestamps = false
11
+ #
12
+ # Timestamps are in UTC by default but you can use the local timezone by setting:
13
+ #
14
+ # config.active_record.default_timezone = :local
15
+ #
16
+ # == Time Zone aware attributes
17
+ #
18
+ # By default, ActiveRecord::Base keeps all the datetime columns time zone aware by executing following code.
19
+ #
20
+ # config.active_record.time_zone_aware_attributes = true
21
+ #
22
+ # This feature can easily be turned off by assigning value <tt>false</tt> .
23
+ #
24
+ # If your attributes are time zone aware and you desire to skip time zone conversion to the current Time.zone
25
+ # when reading certain attributes then you can do following:
26
+ #
27
+ # class Topic < ActiveRecord::Base
28
+ # self.skip_time_zone_conversion_for_attributes = [:written_on]
29
+ # end
30
+ module Timestamp
31
+ extend ActiveSupport::Concern
32
+
33
+ included do
34
+ class_attribute :record_timestamps
35
+ self.record_timestamps = true
36
+ end
37
+
38
+ def initialize_dup(other) # :nodoc:
39
+ super
40
+ clear_timestamp_attributes
41
+ end
42
+
43
+ private
44
+
45
+ def _create_record
46
+ if self.record_timestamps
47
+ current_time = current_time_from_proper_timezone
48
+
49
+ all_timestamp_attributes.each do |column|
50
+ column = column.to_s
51
+ if has_attribute?(column) && !attribute_present?(column)
52
+ write_attribute(column, current_time)
53
+ end
54
+ end
55
+ end
56
+
57
+ super
58
+ end
59
+
60
+ def _update_record(*args)
61
+ if should_record_timestamps?
62
+ current_time = current_time_from_proper_timezone
63
+
64
+ timestamp_attributes_for_update_in_model.each do |column|
65
+ column = column.to_s
66
+ next if attribute_changed?(column)
67
+ write_attribute(column, current_time)
68
+ end
69
+ end
70
+ super
71
+ end
72
+
73
+ def should_record_timestamps?
74
+ self.record_timestamps && (!partial_writes? || changed?)
75
+ end
76
+
77
+ def timestamp_attributes_for_create_in_model
78
+ timestamp_attributes_for_create.select { |c| self.class.column_names.include?(c.to_s) }
79
+ end
80
+
81
+ def timestamp_attributes_for_update_in_model
82
+ timestamp_attributes_for_update.select { |c| self.class.column_names.include?(c.to_s) }
83
+ end
84
+
85
+ def all_timestamp_attributes_in_model
86
+ timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model
87
+ end
88
+
89
+ def timestamp_attributes_for_update
90
+ [:updated_at, :updated_on]
91
+ end
92
+
93
+ def timestamp_attributes_for_create
94
+ [:created_at, :created_on]
95
+ end
96
+
97
+ def all_timestamp_attributes
98
+ timestamp_attributes_for_create + timestamp_attributes_for_update
99
+ end
100
+
101
+ def max_updated_column_timestamp(timestamp_names = timestamp_attributes_for_update)
102
+ timestamp_names
103
+ .map { |attr| self[attr] }
104
+ .compact
105
+ .map(&:to_time)
106
+ .max
107
+ end
108
+
109
+ def current_time_from_proper_timezone
110
+ self.class.default_timezone == :utc ? Time.now.utc : Time.now
111
+ end
112
+
113
+ # Clear attributes and changed_attributes
114
+ def clear_timestamp_attributes
115
+ all_timestamp_attributes_in_model.each do |attribute_name|
116
+ self[attribute_name] = nil
117
+ clear_attribute_changes([attribute_name])
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,417 @@
1
+ module ActiveRecord
2
+ # See ActiveRecord::Transactions::ClassMethods for documentation.
3
+ module Transactions
4
+ extend ActiveSupport::Concern
5
+ #:nodoc:
6
+ ACTIONS = [:create, :destroy, :update]
7
+ #:nodoc:
8
+ CALLBACK_WARN_MESSAGE = "Currently, Active Record suppresses errors raised " \
9
+ "within `after_rollback`/`after_commit` callbacks and only print them to " \
10
+ "the logs. In the next version, these errors will no longer be suppressed. " \
11
+ "Instead, the errors will propagate normally just like in other Active " \
12
+ "Record callbacks.\n" \
13
+ "\n" \
14
+ "You can opt into the new behavior and remove this warning by setting:\n" \
15
+ "\n" \
16
+ " config.active_record.raise_in_transactional_callbacks = true\n\n"
17
+
18
+ included do
19
+ define_callbacks :commit, :rollback,
20
+ terminator: ->(_, result) { result == false },
21
+ scope: [:kind, :name]
22
+
23
+ mattr_accessor :raise_in_transactional_callbacks, instance_writer: false
24
+ self.raise_in_transactional_callbacks = false
25
+ end
26
+
27
+ # = Active Record Transactions
28
+ #
29
+ # Transactions are protective blocks where SQL statements are only permanent
30
+ # if they can all succeed as one atomic action. The classic example is a
31
+ # transfer between two accounts where you can only have a deposit if the
32
+ # withdrawal succeeded and vice versa. Transactions enforce the integrity of
33
+ # the database and guard the data against program errors or database
34
+ # break-downs. So basically you should use transaction blocks whenever you
35
+ # have a number of statements that must be executed together or not at all.
36
+ #
37
+ # For example:
38
+ #
39
+ # ActiveRecord::Base.transaction do
40
+ # david.withdrawal(100)
41
+ # mary.deposit(100)
42
+ # end
43
+ #
44
+ # This example will only take money from David and give it to Mary if neither
45
+ # +withdrawal+ nor +deposit+ raise an exception. Exceptions will force a
46
+ # ROLLBACK that returns the database to the state before the transaction
47
+ # began. Be aware, though, that the objects will _not_ have their instance
48
+ # data returned to their pre-transactional state.
49
+ #
50
+ # == Different Active Record classes in a single transaction
51
+ #
52
+ # Though the transaction class method is called on some Active Record class,
53
+ # the objects within the transaction block need not all be instances of
54
+ # that class. This is because transactions are per-database connection, not
55
+ # per-model.
56
+ #
57
+ # In this example a +balance+ record is transactionally saved even
58
+ # though +transaction+ is called on the +Account+ class:
59
+ #
60
+ # Account.transaction do
61
+ # balance.save!
62
+ # account.save!
63
+ # end
64
+ #
65
+ # The +transaction+ method is also available as a model instance method.
66
+ # For example, you can also do this:
67
+ #
68
+ # balance.transaction do
69
+ # balance.save!
70
+ # account.save!
71
+ # end
72
+ #
73
+ # == Transactions are not distributed across database connections
74
+ #
75
+ # A transaction acts on a single database connection. If you have
76
+ # multiple class-specific databases, the transaction will not protect
77
+ # interaction among them. One workaround is to begin a transaction
78
+ # on each class whose models you alter:
79
+ #
80
+ # Student.transaction do
81
+ # Course.transaction do
82
+ # course.enroll(student)
83
+ # student.units += course.units
84
+ # end
85
+ # end
86
+ #
87
+ # This is a poor solution, but fully distributed transactions are beyond
88
+ # the scope of Active Record.
89
+ #
90
+ # == +save+ and +destroy+ are automatically wrapped in a transaction
91
+ #
92
+ # Both +save+ and +destroy+ come wrapped in a transaction that ensures
93
+ # that whatever you do in validations or callbacks will happen under its
94
+ # protected cover. So you can use validations to check for values that
95
+ # the transaction depends on or you can raise exceptions in the callbacks
96
+ # to rollback, including <tt>after_*</tt> callbacks.
97
+ #
98
+ # As a consequence changes to the database are not seen outside your connection
99
+ # until the operation is complete. For example, if you try to update the index
100
+ # of a search engine in +after_save+ the indexer won't see the updated record.
101
+ # The +after_commit+ callback is the only one that is triggered once the update
102
+ # is committed. See below.
103
+ #
104
+ # == Exception handling and rolling back
105
+ #
106
+ # Also have in mind that exceptions thrown within a transaction block will
107
+ # be propagated (after triggering the ROLLBACK), so you should be ready to
108
+ # catch those in your application code.
109
+ #
110
+ # One exception is the <tt>ActiveRecord::Rollback</tt> exception, which will trigger
111
+ # a ROLLBACK when raised, but not be re-raised by the transaction block.
112
+ #
113
+ # *Warning*: one should not catch <tt>ActiveRecord::StatementInvalid</tt> exceptions
114
+ # inside a transaction block. <tt>ActiveRecord::StatementInvalid</tt> exceptions indicate that an
115
+ # error occurred at the database level, for example when a unique constraint
116
+ # is violated. On some database systems, such as PostgreSQL, database errors
117
+ # inside a transaction cause the entire transaction to become unusable
118
+ # until it's restarted from the beginning. Here is an example which
119
+ # demonstrates the problem:
120
+ #
121
+ # # Suppose that we have a Number model with a unique column called 'i'.
122
+ # Number.transaction do
123
+ # Number.create(i: 0)
124
+ # begin
125
+ # # This will raise a unique constraint error...
126
+ # Number.create(i: 0)
127
+ # rescue ActiveRecord::StatementInvalid
128
+ # # ...which we ignore.
129
+ # end
130
+ #
131
+ # # On PostgreSQL, the transaction is now unusable. The following
132
+ # # statement will cause a PostgreSQL error, even though the unique
133
+ # # constraint is no longer violated:
134
+ # Number.create(i: 1)
135
+ # # => "PGError: ERROR: current transaction is aborted, commands
136
+ # # ignored until end of transaction block"
137
+ # end
138
+ #
139
+ # One should restart the entire transaction if an
140
+ # <tt>ActiveRecord::StatementInvalid</tt> occurred.
141
+ #
142
+ # == Nested transactions
143
+ #
144
+ # +transaction+ calls can be nested. By default, this makes all database
145
+ # statements in the nested transaction block become part of the parent
146
+ # transaction. For example, the following behavior may be surprising:
147
+ #
148
+ # User.transaction do
149
+ # User.create(username: 'Kotori')
150
+ # User.transaction do
151
+ # User.create(username: 'Nemu')
152
+ # raise ActiveRecord::Rollback
153
+ # end
154
+ # end
155
+ #
156
+ # creates both "Kotori" and "Nemu". Reason is the <tt>ActiveRecord::Rollback</tt>
157
+ # exception in the nested block does not issue a ROLLBACK. Since these exceptions
158
+ # are captured in transaction blocks, the parent block does not see it and the
159
+ # real transaction is committed.
160
+ #
161
+ # In order to get a ROLLBACK for the nested transaction you may ask for a real
162
+ # sub-transaction by passing <tt>requires_new: true</tt>. If anything goes wrong,
163
+ # the database rolls back to the beginning of the sub-transaction without rolling
164
+ # back the parent transaction. If we add it to the previous example:
165
+ #
166
+ # User.transaction do
167
+ # User.create(username: 'Kotori')
168
+ # User.transaction(requires_new: true) do
169
+ # User.create(username: 'Nemu')
170
+ # raise ActiveRecord::Rollback
171
+ # end
172
+ # end
173
+ #
174
+ # only "Kotori" is created. This works on MySQL and PostgreSQL. SQLite3 version >= '3.6.8' also supports it.
175
+ #
176
+ # Most databases don't support true nested transactions. At the time of
177
+ # writing, the only database that we're aware of that supports true nested
178
+ # transactions, is MS-SQL. Because of this, Active Record emulates nested
179
+ # transactions by using savepoints on MySQL and PostgreSQL. See
180
+ # http://dev.mysql.com/doc/refman/5.6/en/savepoint.html
181
+ # for more information about savepoints.
182
+ #
183
+ # === Callbacks
184
+ #
185
+ # There are two types of callbacks associated with committing and rolling back transactions:
186
+ # +after_commit+ and +after_rollback+.
187
+ #
188
+ # +after_commit+ callbacks are called on every record saved or destroyed within a
189
+ # transaction immediately after the transaction is committed. +after_rollback+ callbacks
190
+ # are called on every record saved or destroyed within a transaction immediately after the
191
+ # transaction or savepoint is rolled back.
192
+ #
193
+ # These callbacks are useful for interacting with other systems since you will be guaranteed
194
+ # that the callback is only executed when the database is in a permanent state. For example,
195
+ # +after_commit+ is a good spot to put in a hook to clearing a cache since clearing it from
196
+ # within a transaction could trigger the cache to be regenerated before the database is updated.
197
+ #
198
+ # === Caveats
199
+ #
200
+ # If you're on MySQL, then do not use DDL operations in nested transactions
201
+ # blocks that are emulated with savepoints. That is, do not execute statements
202
+ # like 'CREATE TABLE' inside such blocks. This is because MySQL automatically
203
+ # releases all savepoints upon executing a DDL operation. When +transaction+
204
+ # is finished and tries to release the savepoint it created earlier, a
205
+ # database error will occur because the savepoint has already been
206
+ # automatically released. The following example demonstrates the problem:
207
+ #
208
+ # Model.connection.transaction do # BEGIN
209
+ # Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
210
+ # Model.connection.create_table(...) # active_record_1 now automatically released
211
+ # end # RELEASE savepoint active_record_1
212
+ # # ^^^^ BOOM! database error!
213
+ # end
214
+ #
215
+ # Note that "TRUNCATE" is also a MySQL DDL statement!
216
+ module ClassMethods
217
+ # See ActiveRecord::Transactions::ClassMethods for detailed documentation.
218
+ def transaction(options = {}, &block)
219
+ # See the ConnectionAdapters::DatabaseStatements#transaction API docs.
220
+ connection.transaction(options, &block)
221
+ end
222
+
223
+ # This callback is called after a record has been created, updated, or destroyed.
224
+ #
225
+ # You can specify that the callback should only be fired by a certain action with
226
+ # the +:on+ option:
227
+ #
228
+ # after_commit :do_foo, on: :create
229
+ # after_commit :do_bar, on: :update
230
+ # after_commit :do_baz, on: :destroy
231
+ #
232
+ # after_commit :do_foo_bar, on: [:create, :update]
233
+ # after_commit :do_bar_baz, on: [:update, :destroy]
234
+ #
235
+ # Note that transactional fixtures do not play well with this feature. Please
236
+ # use the +test_after_commit+ gem to have these hooks fired in tests.
237
+ def after_commit(*args, &block)
238
+ set_options_for_callbacks!(args)
239
+ set_callback(:commit, :after, *args, &block)
240
+ unless ActiveRecord::Base.raise_in_transactional_callbacks
241
+ ActiveSupport::Deprecation.warn(CALLBACK_WARN_MESSAGE)
242
+ end
243
+ end
244
+
245
+ # This callback is called after a create, update, or destroy are rolled back.
246
+ #
247
+ # Please check the documentation of +after_commit+ for options.
248
+ def after_rollback(*args, &block)
249
+ set_options_for_callbacks!(args)
250
+ set_callback(:rollback, :after, *args, &block)
251
+ unless ActiveRecord::Base.raise_in_transactional_callbacks
252
+ ActiveSupport::Deprecation.warn(CALLBACK_WARN_MESSAGE)
253
+ end
254
+ end
255
+
256
+ private
257
+
258
+ def set_options_for_callbacks!(args)
259
+ options = args.last
260
+ if options.is_a?(Hash) && options[:on]
261
+ fire_on = Array(options[:on])
262
+ assert_valid_transaction_action(fire_on)
263
+ options[:if] = Array(options[:if])
264
+ options[:if] << "transaction_include_any_action?(#{fire_on})"
265
+ end
266
+ end
267
+
268
+ def assert_valid_transaction_action(actions)
269
+ if (actions - ACTIONS).any?
270
+ raise ArgumentError, ":on conditions for after_commit and after_rollback callbacks have to be one of #{ACTIONS}"
271
+ end
272
+ end
273
+ end
274
+
275
+ # See ActiveRecord::Transactions::ClassMethods for detailed documentation.
276
+ def transaction(options = {}, &block)
277
+ self.class.transaction(options, &block)
278
+ end
279
+
280
+ def destroy #:nodoc:
281
+ with_transaction_returning_status { super }
282
+ end
283
+
284
+ def save(*) #:nodoc:
285
+ rollback_active_record_state! do
286
+ with_transaction_returning_status { super }
287
+ end
288
+ end
289
+
290
+ def save!(*) #:nodoc:
291
+ with_transaction_returning_status { super }
292
+ end
293
+
294
+ def touch(*) #:nodoc:
295
+ with_transaction_returning_status { super }
296
+ end
297
+
298
+ # Reset id and @new_record if the transaction rolls back.
299
+ def rollback_active_record_state!
300
+ remember_transaction_record_state
301
+ yield
302
+ rescue Exception
303
+ restore_transaction_record_state
304
+ raise
305
+ ensure
306
+ clear_transaction_record_state
307
+ end
308
+
309
+ # Call the +after_commit+ callbacks.
310
+ #
311
+ # Ensure that it is not called if the object was never persisted (failed create),
312
+ # but call it after the commit of a destroyed object.
313
+ def committed!(should_run_callbacks = true) #:nodoc:
314
+ _run_commit_callbacks if should_run_callbacks && destroyed? || persisted?
315
+ ensure
316
+ force_clear_transaction_record_state
317
+ end
318
+
319
+ # Call the +after_rollback+ callbacks. The +force_restore_state+ argument indicates if the record
320
+ # state should be rolled back to the beginning or just to the last savepoint.
321
+ def rolledback!(force_restore_state = false, should_run_callbacks = true) #:nodoc:
322
+ _run_rollback_callbacks if should_run_callbacks
323
+ ensure
324
+ restore_transaction_record_state(force_restore_state)
325
+ clear_transaction_record_state
326
+ end
327
+
328
+ # Add the record to the current transaction so that the +after_rollback+ and +after_commit+ callbacks
329
+ # can be called.
330
+ def add_to_transaction
331
+ if self.class.connection.add_transaction_record(self)
332
+ remember_transaction_record_state
333
+ end
334
+ end
335
+
336
+ # Executes +method+ within a transaction and captures its return value as a
337
+ # status flag. If the status is true the transaction is committed, otherwise
338
+ # a ROLLBACK is issued. In any case the status flag is returned.
339
+ #
340
+ # This method is available within the context of an ActiveRecord::Base
341
+ # instance.
342
+ def with_transaction_returning_status
343
+ status = nil
344
+ self.class.transaction do
345
+ add_to_transaction
346
+ begin
347
+ status = yield
348
+ rescue ActiveRecord::Rollback
349
+ clear_transaction_record_state
350
+ status = nil
351
+ end
352
+
353
+ raise ActiveRecord::Rollback unless status
354
+ end
355
+ status
356
+ end
357
+
358
+ protected
359
+
360
+ # Save the new record state and id of a record so it can be restored later if a transaction fails.
361
+ def remember_transaction_record_state #:nodoc:
362
+ @_start_transaction_state[:id] = id
363
+ unless @_start_transaction_state.include?(:new_record)
364
+ @_start_transaction_state[:new_record] = @new_record
365
+ end
366
+ unless @_start_transaction_state.include?(:destroyed)
367
+ @_start_transaction_state[:destroyed] = @destroyed
368
+ end
369
+ @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
370
+ @_start_transaction_state[:frozen?] = frozen?
371
+ end
372
+
373
+ # Clear the new record state and id of a record.
374
+ def clear_transaction_record_state #:nodoc:
375
+ @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
376
+ force_clear_transaction_record_state if @_start_transaction_state[:level] < 1
377
+ end
378
+
379
+ # Force to clear the transaction record state.
380
+ def force_clear_transaction_record_state #:nodoc:
381
+ @_start_transaction_state.clear
382
+ end
383
+
384
+ # Restore the new record state and id of a record that was previously saved by a call to save_record_state.
385
+ def restore_transaction_record_state(force = false) #:nodoc:
386
+ unless @_start_transaction_state.empty?
387
+ transaction_level = (@_start_transaction_state[:level] || 0) - 1
388
+ if transaction_level < 1 || force
389
+ restore_state = @_start_transaction_state
390
+ thaw unless restore_state[:frozen?]
391
+ @new_record = restore_state[:new_record]
392
+ @destroyed = restore_state[:destroyed]
393
+ write_attribute(self.class.primary_key, restore_state[:id])
394
+ end
395
+ end
396
+ end
397
+
398
+ # Determine if a record was created or destroyed in a transaction. State should be one of :new_record or :destroyed.
399
+ def transaction_record_state(state) #:nodoc:
400
+ @_start_transaction_state[state]
401
+ end
402
+
403
+ # Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks.
404
+ def transaction_include_any_action?(actions) #:nodoc:
405
+ actions.any? do |action|
406
+ case action
407
+ when :create
408
+ transaction_record_state(:new_record)
409
+ when :destroy
410
+ destroyed?
411
+ when :update
412
+ !(transaction_record_state(:new_record) || destroyed?)
413
+ end
414
+ end
415
+ end
416
+ end
417
+ end