activerecord 1.0.0 → 4.0.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 (255) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2102 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +213 -0
  5. data/examples/performance.rb +172 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record/aggregations.rb +180 -84
  8. data/lib/active_record/associations/alias_tracker.rb +76 -0
  9. data/lib/active_record/associations/association.rb +248 -0
  10. data/lib/active_record/associations/association_scope.rb +135 -0
  11. data/lib/active_record/associations/belongs_to_association.rb +92 -0
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +35 -0
  13. data/lib/active_record/associations/builder/association.rb +108 -0
  14. data/lib/active_record/associations/builder/belongs_to.rb +98 -0
  15. data/lib/active_record/associations/builder/collection_association.rb +89 -0
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
  17. data/lib/active_record/associations/builder/has_many.rb +15 -0
  18. data/lib/active_record/associations/builder/has_one.rb +25 -0
  19. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  20. data/lib/active_record/associations/collection_association.rb +608 -0
  21. data/lib/active_record/associations/collection_proxy.rb +986 -0
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +58 -39
  23. data/lib/active_record/associations/has_many_association.rb +116 -85
  24. data/lib/active_record/associations/has_many_through_association.rb +197 -0
  25. data/lib/active_record/associations/has_one_association.rb +102 -0
  26. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  27. data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
  28. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  29. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  30. data/lib/active_record/associations/join_dependency.rb +235 -0
  31. data/lib/active_record/associations/join_helper.rb +45 -0
  32. data/lib/active_record/associations/preloader/association.rb +121 -0
  33. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  34. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  35. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  36. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  37. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  38. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  39. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  40. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  41. data/lib/active_record/associations/preloader/through_association.rb +63 -0
  42. data/lib/active_record/associations/preloader.rb +178 -0
  43. data/lib/active_record/associations/singular_association.rb +64 -0
  44. data/lib/active_record/associations/through_association.rb +87 -0
  45. data/lib/active_record/associations.rb +1437 -431
  46. data/lib/active_record/attribute_assignment.rb +201 -0
  47. data/lib/active_record/attribute_methods/before_type_cast.rb +70 -0
  48. data/lib/active_record/attribute_methods/dirty.rb +118 -0
  49. data/lib/active_record/attribute_methods/primary_key.rb +122 -0
  50. data/lib/active_record/attribute_methods/query.rb +40 -0
  51. data/lib/active_record/attribute_methods/read.rb +107 -0
  52. data/lib/active_record/attribute_methods/serialization.rb +162 -0
  53. data/lib/active_record/attribute_methods/time_zone_conversion.rb +59 -0
  54. data/lib/active_record/attribute_methods/write.rb +63 -0
  55. data/lib/active_record/attribute_methods.rb +393 -0
  56. data/lib/active_record/autosave_association.rb +426 -0
  57. data/lib/active_record/base.rb +268 -930
  58. data/lib/active_record/callbacks.rb +203 -230
  59. data/lib/active_record/coders/yaml_column.rb +38 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +638 -0
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +390 -0
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +129 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +501 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +873 -0
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +389 -275
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
  71. data/lib/active_record/connection_adapters/column.rb +318 -0
  72. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  73. data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
  74. data/lib/active_record/connection_adapters/mysql_adapter.rb +517 -90
  75. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  76. data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
  77. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
  79. data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
  80. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  81. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
  82. data/lib/active_record/connection_adapters/postgresql_adapter.rb +911 -138
  83. data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
  84. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +624 -0
  85. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  86. data/lib/active_record/connection_handling.rb +98 -0
  87. data/lib/active_record/core.rb +463 -0
  88. data/lib/active_record/counter_cache.rb +122 -0
  89. data/lib/active_record/dynamic_matchers.rb +131 -0
  90. data/lib/active_record/errors.rb +213 -0
  91. data/lib/active_record/explain.rb +38 -0
  92. data/lib/active_record/explain_registry.rb +30 -0
  93. data/lib/active_record/explain_subscriber.rb +29 -0
  94. data/lib/active_record/fixture_set/file.rb +55 -0
  95. data/lib/active_record/fixtures.rb +892 -138
  96. data/lib/active_record/inheritance.rb +200 -0
  97. data/lib/active_record/integration.rb +60 -0
  98. data/lib/active_record/locale/en.yml +47 -0
  99. data/lib/active_record/locking/optimistic.rb +181 -0
  100. data/lib/active_record/locking/pessimistic.rb +77 -0
  101. data/lib/active_record/log_subscriber.rb +82 -0
  102. data/lib/active_record/migration/command_recorder.rb +164 -0
  103. data/lib/active_record/migration/join_table.rb +15 -0
  104. data/lib/active_record/migration.rb +1015 -0
  105. data/lib/active_record/model_schema.rb +345 -0
  106. data/lib/active_record/nested_attributes.rb +546 -0
  107. data/lib/active_record/null_relation.rb +65 -0
  108. data/lib/active_record/persistence.rb +509 -0
  109. data/lib/active_record/query_cache.rb +56 -0
  110. data/lib/active_record/querying.rb +62 -0
  111. data/lib/active_record/railtie.rb +205 -0
  112. data/lib/active_record/railties/console_sandbox.rb +5 -0
  113. data/lib/active_record/railties/controller_runtime.rb +50 -0
  114. data/lib/active_record/railties/databases.rake +402 -0
  115. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  116. data/lib/active_record/readonly_attributes.rb +30 -0
  117. data/lib/active_record/reflection.rb +544 -87
  118. data/lib/active_record/relation/batches.rb +93 -0
  119. data/lib/active_record/relation/calculations.rb +399 -0
  120. data/lib/active_record/relation/delegation.rb +125 -0
  121. data/lib/active_record/relation/finder_methods.rb +349 -0
  122. data/lib/active_record/relation/merger.rb +161 -0
  123. data/lib/active_record/relation/predicate_builder.rb +106 -0
  124. data/lib/active_record/relation/query_methods.rb +1044 -0
  125. data/lib/active_record/relation/spawn_methods.rb +73 -0
  126. data/lib/active_record/relation.rb +655 -0
  127. data/lib/active_record/result.rb +67 -0
  128. data/lib/active_record/runtime_registry.rb +17 -0
  129. data/lib/active_record/sanitization.rb +168 -0
  130. data/lib/active_record/schema.rb +65 -0
  131. data/lib/active_record/schema_dumper.rb +204 -0
  132. data/lib/active_record/schema_migration.rb +39 -0
  133. data/lib/active_record/scoping/default.rb +146 -0
  134. data/lib/active_record/scoping/named.rb +175 -0
  135. data/lib/active_record/scoping.rb +82 -0
  136. data/lib/active_record/serialization.rb +22 -0
  137. data/lib/active_record/serializers/xml_serializer.rb +197 -0
  138. data/lib/active_record/statement_cache.rb +26 -0
  139. data/lib/active_record/store.rb +156 -0
  140. data/lib/active_record/tasks/database_tasks.rb +203 -0
  141. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  142. data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
  143. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  144. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  145. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  146. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  147. data/lib/active_record/test_case.rb +96 -0
  148. data/lib/active_record/timestamp.rb +119 -0
  149. data/lib/active_record/transactions.rb +366 -69
  150. data/lib/active_record/translation.rb +22 -0
  151. data/lib/active_record/validations/associated.rb +49 -0
  152. data/lib/active_record/validations/presence.rb +65 -0
  153. data/lib/active_record/validations/uniqueness.rb +225 -0
  154. data/lib/active_record/validations.rb +64 -185
  155. data/lib/active_record/version.rb +11 -0
  156. data/lib/active_record.rb +149 -24
  157. data/lib/rails/generators/active_record/migration/migration_generator.rb +62 -0
  158. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  159. data/lib/rails/generators/active_record/migration/templates/migration.rb +39 -0
  160. data/lib/rails/generators/active_record/model/model_generator.rb +48 -0
  161. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  162. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  163. data/lib/rails/generators/active_record.rb +23 -0
  164. metadata +261 -161
  165. data/CHANGELOG +0 -581
  166. data/README +0 -361
  167. data/RUNNING_UNIT_TESTS +0 -36
  168. data/dev-utils/eval_debugger.rb +0 -9
  169. data/examples/associations.png +0 -0
  170. data/examples/associations.rb +0 -87
  171. data/examples/shared_setup.rb +0 -15
  172. data/examples/validation.rb +0 -88
  173. data/install.rb +0 -60
  174. data/lib/active_record/associations/association_collection.rb +0 -70
  175. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -107
  176. data/lib/active_record/deprecated_associations.rb +0 -70
  177. data/lib/active_record/observer.rb +0 -71
  178. data/lib/active_record/support/class_attribute_accessors.rb +0 -43
  179. data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
  180. data/lib/active_record/support/clean_logger.rb +0 -10
  181. data/lib/active_record/support/inflector.rb +0 -70
  182. data/lib/active_record/vendor/mysql.rb +0 -1117
  183. data/lib/active_record/vendor/simple.rb +0 -702
  184. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  185. data/lib/active_record/wrappings.rb +0 -59
  186. data/rakefile +0 -122
  187. data/test/abstract_unit.rb +0 -16
  188. data/test/aggregations_test.rb +0 -34
  189. data/test/all.sh +0 -8
  190. data/test/associations_test.rb +0 -477
  191. data/test/base_test.rb +0 -513
  192. data/test/class_inheritable_attributes_test.rb +0 -33
  193. data/test/connections/native_mysql/connection.rb +0 -24
  194. data/test/connections/native_postgresql/connection.rb +0 -24
  195. data/test/connections/native_sqlite/connection.rb +0 -24
  196. data/test/deprecated_associations_test.rb +0 -336
  197. data/test/finder_test.rb +0 -67
  198. data/test/fixtures/accounts/signals37 +0 -3
  199. data/test/fixtures/accounts/unknown +0 -2
  200. data/test/fixtures/auto_id.rb +0 -4
  201. data/test/fixtures/column_name.rb +0 -3
  202. data/test/fixtures/companies/first_client +0 -6
  203. data/test/fixtures/companies/first_firm +0 -4
  204. data/test/fixtures/companies/second_client +0 -6
  205. data/test/fixtures/company.rb +0 -37
  206. data/test/fixtures/company_in_module.rb +0 -33
  207. data/test/fixtures/course.rb +0 -3
  208. data/test/fixtures/courses/java +0 -2
  209. data/test/fixtures/courses/ruby +0 -2
  210. data/test/fixtures/customer.rb +0 -30
  211. data/test/fixtures/customers/david +0 -6
  212. data/test/fixtures/db_definitions/mysql.sql +0 -96
  213. data/test/fixtures/db_definitions/mysql2.sql +0 -4
  214. data/test/fixtures/db_definitions/postgresql.sql +0 -113
  215. data/test/fixtures/db_definitions/postgresql2.sql +0 -4
  216. data/test/fixtures/db_definitions/sqlite.sql +0 -85
  217. data/test/fixtures/db_definitions/sqlite2.sql +0 -4
  218. data/test/fixtures/default.rb +0 -2
  219. data/test/fixtures/developer.rb +0 -8
  220. data/test/fixtures/developers/david +0 -2
  221. data/test/fixtures/developers/jamis +0 -2
  222. data/test/fixtures/developers_projects/david_action_controller +0 -2
  223. data/test/fixtures/developers_projects/david_active_record +0 -2
  224. data/test/fixtures/developers_projects/jamis_active_record +0 -2
  225. data/test/fixtures/entrant.rb +0 -3
  226. data/test/fixtures/entrants/first +0 -3
  227. data/test/fixtures/entrants/second +0 -3
  228. data/test/fixtures/entrants/third +0 -3
  229. data/test/fixtures/fixture_database.sqlite +0 -0
  230. data/test/fixtures/fixture_database_2.sqlite +0 -0
  231. data/test/fixtures/movie.rb +0 -5
  232. data/test/fixtures/movies/first +0 -2
  233. data/test/fixtures/movies/second +0 -2
  234. data/test/fixtures/project.rb +0 -3
  235. data/test/fixtures/projects/action_controller +0 -2
  236. data/test/fixtures/projects/active_record +0 -2
  237. data/test/fixtures/reply.rb +0 -21
  238. data/test/fixtures/subscriber.rb +0 -5
  239. data/test/fixtures/subscribers/first +0 -2
  240. data/test/fixtures/subscribers/second +0 -2
  241. data/test/fixtures/topic.rb +0 -20
  242. data/test/fixtures/topics/first +0 -9
  243. data/test/fixtures/topics/second +0 -8
  244. data/test/fixtures_test.rb +0 -20
  245. data/test/inflector_test.rb +0 -104
  246. data/test/inheritance_test.rb +0 -125
  247. data/test/lifecycle_test.rb +0 -110
  248. data/test/modules_test.rb +0 -21
  249. data/test/multiple_db_test.rb +0 -46
  250. data/test/pk_test.rb +0 -57
  251. data/test/reflection_test.rb +0 -78
  252. data/test/thread_safety_test.rb +0 -33
  253. data/test/transactions_test.rb +0 -83
  254. data/test/unconnected_test.rb +0 -24
  255. data/test/validations_test.rb +0 -126
@@ -0,0 +1,146 @@
1
+ module ActiveRecord
2
+ module Scoping
3
+ module Default
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ # Stores the default scope for the class.
8
+ class_attribute :default_scopes, instance_writer: false, instance_predicate: false
9
+
10
+ self.default_scopes = []
11
+
12
+ def self.default_scopes?
13
+ ActiveSupport::Deprecation.warn(
14
+ "#default_scopes? is deprecated. Do something like #default_scopes.empty? instead."
15
+ )
16
+
17
+ !!self.default_scopes
18
+ end
19
+ end
20
+
21
+ module ClassMethods
22
+ # Returns a scope for the model without the +default_scope+.
23
+ #
24
+ # class Post < ActiveRecord::Base
25
+ # def self.default_scope
26
+ # where published: true
27
+ # end
28
+ # end
29
+ #
30
+ # Post.all # Fires "SELECT * FROM posts WHERE published = true"
31
+ # Post.unscoped.all # Fires "SELECT * FROM posts"
32
+ #
33
+ # This method also accepts a block. All queries inside the block will
34
+ # not use the +default_scope+:
35
+ #
36
+ # Post.unscoped {
37
+ # Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
38
+ # }
39
+ def unscoped
40
+ block_given? ? relation.scoping { yield } : relation
41
+ end
42
+
43
+ def before_remove_const #:nodoc:
44
+ self.current_scope = nil
45
+ end
46
+
47
+ protected
48
+
49
+ # Use this macro in your model to set a default scope for all operations on
50
+ # the model.
51
+ #
52
+ # class Article < ActiveRecord::Base
53
+ # default_scope { where(published: true) }
54
+ # end
55
+ #
56
+ # Article.all # => SELECT * FROM articles WHERE published = true
57
+ #
58
+ # The +default_scope+ is also applied while creating/building a record.
59
+ # It is not applied while updating a record.
60
+ #
61
+ # Article.new.published # => true
62
+ # Article.create.published # => true
63
+ #
64
+ # (You can also pass any object which responds to +call+ to the
65
+ # +default_scope+ macro, and it will be called when building the
66
+ # default scope.)
67
+ #
68
+ # If you use multiple +default_scope+ declarations in your model then
69
+ # they will be merged together:
70
+ #
71
+ # class Article < ActiveRecord::Base
72
+ # default_scope { where(published: true) }
73
+ # default_scope { where(rating: 'G') }
74
+ # end
75
+ #
76
+ # Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
77
+ #
78
+ # This is also the case with inheritance and module includes where the
79
+ # parent or module defines a +default_scope+ and the child or including
80
+ # class defines a second one.
81
+ #
82
+ # If you need to do more complex things with a default scope, you can
83
+ # alternatively define it as a class method:
84
+ #
85
+ # class Article < ActiveRecord::Base
86
+ # def self.default_scope
87
+ # # Should return a scope, you can call 'super' here etc.
88
+ # end
89
+ # end
90
+ def default_scope(scope = nil)
91
+ scope = Proc.new if block_given?
92
+
93
+ if scope.is_a?(Relation) || !scope.respond_to?(:call)
94
+ ActiveSupport::Deprecation.warn(
95
+ "Calling #default_scope without a block is deprecated. For example instead " \
96
+ "of `default_scope where(color: 'red')`, please use " \
97
+ "`default_scope { where(color: 'red') }`. (Alternatively you can just redefine " \
98
+ "self.default_scope.)"
99
+ )
100
+ end
101
+
102
+ self.default_scopes += [scope]
103
+ end
104
+
105
+ def build_default_scope # :nodoc:
106
+ if !Base.is_a?(method(:default_scope).owner)
107
+ # The user has defined their own default scope method, so call that
108
+ evaluate_default_scope { default_scope }
109
+ elsif default_scopes.any?
110
+ evaluate_default_scope do
111
+ default_scopes.inject(relation) do |default_scope, scope|
112
+ if !scope.is_a?(Relation) && scope.respond_to?(:call)
113
+ default_scope.merge(unscoped { scope.call })
114
+ else
115
+ default_scope.merge(scope)
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ def ignore_default_scope? # :nodoc:
123
+ ScopeRegistry.value_for(:ignore_default_scope, self)
124
+ end
125
+
126
+ def ignore_default_scope=(ignore) # :nodoc:
127
+ ScopeRegistry.set_value_for(:ignore_default_scope, self, ignore)
128
+ end
129
+
130
+ # The ignore_default_scope flag is used to prevent an infinite recursion
131
+ # situation where a default scope references a scope which has a default
132
+ # scope which references a scope...
133
+ def evaluate_default_scope # :nodoc:
134
+ return if ignore_default_scope?
135
+
136
+ begin
137
+ self.ignore_default_scope = true
138
+ yield
139
+ ensure
140
+ self.ignore_default_scope = false
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,175 @@
1
+ require 'active_support/core_ext/array'
2
+ require 'active_support/core_ext/hash/except'
3
+ require 'active_support/core_ext/kernel/singleton_class'
4
+
5
+ module ActiveRecord
6
+ # = Active Record \Named \Scopes
7
+ module Scoping
8
+ module Named
9
+ extend ActiveSupport::Concern
10
+
11
+ module ClassMethods
12
+ # Returns an <tt>ActiveRecord::Relation</tt> scope object.
13
+ #
14
+ # posts = Post.all
15
+ # posts.size # Fires "select count(*) from posts" and returns the count
16
+ # posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
17
+ #
18
+ # fruits = Fruit.all
19
+ # fruits = fruits.where(color: 'red') if options[:red_only]
20
+ # fruits = fruits.limit(10) if limited?
21
+ #
22
+ # You can define a scope that applies to all finders using
23
+ # <tt>ActiveRecord::Base.default_scope</tt>.
24
+ def all
25
+ if current_scope
26
+ current_scope.clone
27
+ else
28
+ scope = relation
29
+ scope.default_scoped = true
30
+ scope
31
+ end
32
+ end
33
+
34
+ # Collects attributes from scopes that should be applied when creating
35
+ # an AR instance for the particular class this is called on.
36
+ def scope_attributes # :nodoc:
37
+ if current_scope
38
+ current_scope.scope_for_create
39
+ else
40
+ scope = relation
41
+ scope.default_scoped = true
42
+ scope.scope_for_create
43
+ end
44
+ end
45
+
46
+ # Are there default attributes associated with this scope?
47
+ def scope_attributes? # :nodoc:
48
+ current_scope || default_scopes.any?
49
+ end
50
+
51
+ # Adds a class method for retrieving and querying objects. A \scope
52
+ # represents a narrowing of a database query, such as
53
+ # <tt>where(color: :red).select('shirts.*').includes(:washing_instructions)</tt>.
54
+ #
55
+ # class Shirt < ActiveRecord::Base
56
+ # scope :red, -> { where(color: 'red') }
57
+ # scope :dry_clean_only, -> { joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true) }
58
+ # end
59
+ #
60
+ # The above calls to +scope+ define class methods <tt>Shirt.red</tt> and
61
+ # <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>, in effect,
62
+ # represents the query <tt>Shirt.where(color: 'red')</tt>.
63
+ #
64
+ # You should always pass a callable object to the scopes defined
65
+ # with +scope+. This ensures that the scope is re-evaluated each
66
+ # time it is called.
67
+ #
68
+ # Note that this is simply 'syntactic sugar' for defining an actual
69
+ # class method:
70
+ #
71
+ # class Shirt < ActiveRecord::Base
72
+ # def self.red
73
+ # where(color: 'red')
74
+ # end
75
+ # end
76
+ #
77
+ # Unlike <tt>Shirt.find(...)</tt>, however, the object returned by
78
+ # <tt>Shirt.red</tt> is not an Array; it resembles the association object
79
+ # constructed by a +has_many+ declaration. For instance, you can invoke
80
+ # <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>,
81
+ # <tt>Shirt.red.where(size: 'small')</tt>. Also, just as with the
82
+ # association objects, named \scopes act like an Array, implementing
83
+ # Enumerable; <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>,
84
+ # and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if
85
+ # <tt>Shirt.red</tt> really was an Array.
86
+ #
87
+ # These named \scopes are composable. For instance,
88
+ # <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are
89
+ # both red and dry clean only. Nested finds and calculations also work
90
+ # with these compositions: <tt>Shirt.red.dry_clean_only.count</tt>
91
+ # returns the number of garments for which these criteria obtain.
92
+ # Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
93
+ #
94
+ # All scopes are available as class methods on the ActiveRecord::Base
95
+ # descendant upon which the \scopes were defined. But they are also
96
+ # available to +has_many+ associations. If,
97
+ #
98
+ # class Person < ActiveRecord::Base
99
+ # has_many :shirts
100
+ # end
101
+ #
102
+ # then <tt>elton.shirts.red.dry_clean_only</tt> will return all of
103
+ # Elton's red, dry clean only shirts.
104
+ #
105
+ # \Named scopes can also have extensions, just as with +has_many+
106
+ # declarations:
107
+ #
108
+ # class Shirt < ActiveRecord::Base
109
+ # scope :red, -> { where(color: 'red') } do
110
+ # def dom_id
111
+ # 'red_shirts'
112
+ # end
113
+ # end
114
+ # end
115
+ #
116
+ # Scopes can also be used while creating/building a record.
117
+ #
118
+ # class Article < ActiveRecord::Base
119
+ # scope :published, -> { where(published: true) }
120
+ # end
121
+ #
122
+ # Article.published.new.published # => true
123
+ # Article.published.create.published # => true
124
+ #
125
+ # \Class methods on your model are automatically available
126
+ # on scopes. Assuming the following setup:
127
+ #
128
+ # class Article < ActiveRecord::Base
129
+ # scope :published, -> { where(published: true) }
130
+ # scope :featured, -> { where(featured: true) }
131
+ #
132
+ # def self.latest_article
133
+ # order('published_at desc').first
134
+ # end
135
+ #
136
+ # def self.titles
137
+ # pluck(:title)
138
+ # end
139
+ # end
140
+ #
141
+ # We are able to call the methods like this:
142
+ #
143
+ # Article.published.featured.latest_article
144
+ # Article.featured.titles
145
+ def scope(name, body, &block)
146
+ extension = Module.new(&block) if block
147
+
148
+ # Check body.is_a?(Relation) to prevent the relation actually being
149
+ # loaded by respond_to?
150
+ if body.is_a?(Relation) || !body.respond_to?(:call)
151
+ ActiveSupport::Deprecation.warn(
152
+ "Using #scope without passing a callable object is deprecated. For " \
153
+ "example `scope :red, where(color: 'red')` should be changed to " \
154
+ "`scope :red, -> { where(color: 'red') }`. There are numerous gotchas " \
155
+ "in the former usage and it makes the implementation more complicated " \
156
+ "and buggy. (If you prefer, you can just define a class method named " \
157
+ "`self.red`.)"
158
+ )
159
+ end
160
+
161
+ singleton_class.send(:define_method, name) do |*args|
162
+ if body.respond_to?(:call)
163
+ scope = all.scoping { body.call(*args) }
164
+ scope = scope.extending(extension) if extension
165
+ else
166
+ scope = body
167
+ end
168
+
169
+ scope || all
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,82 @@
1
+ require 'active_support/per_thread_registry'
2
+
3
+ module ActiveRecord
4
+ module Scoping
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ include Default
9
+ include Named
10
+ end
11
+
12
+ module ClassMethods
13
+ def current_scope #:nodoc:
14
+ ScopeRegistry.value_for(:current_scope, base_class.to_s)
15
+ end
16
+
17
+ def current_scope=(scope) #:nodoc:
18
+ ScopeRegistry.set_value_for(:current_scope, base_class.to_s, scope)
19
+ end
20
+ end
21
+
22
+ def populate_with_current_scope_attributes
23
+ return unless self.class.scope_attributes?
24
+
25
+ self.class.scope_attributes.each do |att,value|
26
+ send("#{att}=", value) if respond_to?("#{att}=")
27
+ end
28
+ end
29
+
30
+ # This class stores the +:current_scope+ and +:ignore_default_scope+ values
31
+ # for different classes. The registry is stored as a thread local, which is
32
+ # accessed through +ScopeRegistry.current+.
33
+ #
34
+ # This class allows you to store and get the scope values on different
35
+ # classes and different types of scopes. For example, if you are attempting
36
+ # to get the current_scope for the +Board+ model, then you would use the
37
+ # following code:
38
+ #
39
+ # registry = ActiveRecord::Scoping::ScopeRegistry
40
+ # registry.set_value_for(:current_scope, "Board", some_new_scope)
41
+ #
42
+ # Now when you run:
43
+ #
44
+ # registry.value_for(:current_scope, "Board")
45
+ #
46
+ # You will obtain whatever was defined in +some_new_scope+. The +value_for+
47
+ # and +set_value_for+ methods are delegated to the current +ScopeRegistry+
48
+ # object, so the above example code can also be called as:
49
+ #
50
+ # ActiveRecord::Scoping::ScopeRegistry.set_value_for(:current_scope,
51
+ # "Board", some_new_scope)
52
+ class ScopeRegistry # :nodoc:
53
+ extend ActiveSupport::PerThreadRegistry
54
+
55
+ VALID_SCOPE_TYPES = [:current_scope, :ignore_default_scope]
56
+
57
+ def initialize
58
+ @registry = Hash.new { |hash, key| hash[key] = {} }
59
+ end
60
+
61
+ # Obtains the value for a given +scope_name+ and +variable_name+.
62
+ def value_for(scope_type, variable_name)
63
+ raise_invalid_scope_type!(scope_type)
64
+ @registry[scope_type][variable_name]
65
+ end
66
+
67
+ # Sets the +value+ for a given +scope_type+ and +variable_name+.
68
+ def set_value_for(scope_type, variable_name, value)
69
+ raise_invalid_scope_type!(scope_type)
70
+ @registry[scope_type][variable_name] = value
71
+ end
72
+
73
+ private
74
+
75
+ def raise_invalid_scope_type!(scope_type)
76
+ if !VALID_SCOPE_TYPES.include?(scope_type)
77
+ raise ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES"
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,22 @@
1
+ module ActiveRecord #:nodoc:
2
+ # = Active Record Serialization
3
+ module Serialization
4
+ extend ActiveSupport::Concern
5
+ include ActiveModel::Serializers::JSON
6
+
7
+ included do
8
+ self.include_root_in_json = false
9
+ end
10
+
11
+ def serializable_hash(options = nil)
12
+ options = options.try(:clone) || {}
13
+
14
+ options[:except] = Array(options[:except]).map { |n| n.to_s }
15
+ options[:except] |= Array(self.class.inheritance_column)
16
+
17
+ super(options)
18
+ end
19
+ end
20
+ end
21
+
22
+ require 'active_record/serializers/xml_serializer'
@@ -0,0 +1,197 @@
1
+ require 'active_support/core_ext/hash/conversions'
2
+
3
+ module ActiveRecord #:nodoc:
4
+ module Serialization
5
+ include ActiveModel::Serializers::Xml
6
+
7
+ # Builds an XML document to represent the model. Some configuration is
8
+ # available through +options+. However more complicated cases should
9
+ # override ActiveRecord::Base#to_xml.
10
+ #
11
+ # By default the generated XML document will include the processing
12
+ # instruction and all the object's attributes. For example:
13
+ #
14
+ # <?xml version="1.0" encoding="UTF-8"?>
15
+ # <topic>
16
+ # <title>The First Topic</title>
17
+ # <author-name>David</author-name>
18
+ # <id type="integer">1</id>
19
+ # <approved type="boolean">false</approved>
20
+ # <replies-count type="integer">0</replies-count>
21
+ # <bonus-time type="dateTime">2000-01-01T08:28:00+12:00</bonus-time>
22
+ # <written-on type="dateTime">2003-07-16T09:28:00+1200</written-on>
23
+ # <content>Have a nice day</content>
24
+ # <author-email-address>david@loudthinking.com</author-email-address>
25
+ # <parent-id></parent-id>
26
+ # <last-read type="date">2004-04-15</last-read>
27
+ # </topic>
28
+ #
29
+ # This behavior can be controlled with <tt>:only</tt>, <tt>:except</tt>,
30
+ # <tt>:skip_instruct</tt>, <tt>:skip_types</tt>, <tt>:dasherize</tt> and <tt>:camelize</tt> .
31
+ # The <tt>:only</tt> and <tt>:except</tt> options are the same as for the
32
+ # +attributes+ method. The default is to dasherize all column names, but you
33
+ # can disable this setting <tt>:dasherize</tt> to +false+. Setting <tt>:camelize</tt>
34
+ # to +true+ will camelize all column names - this also overrides <tt>:dasherize</tt>.
35
+ # To not have the column type included in the XML output set <tt>:skip_types</tt> to +true+.
36
+ #
37
+ # For instance:
38
+ #
39
+ # topic.to_xml(skip_instruct: true, except: [ :id, :bonus_time, :written_on, :replies_count ])
40
+ #
41
+ # <topic>
42
+ # <title>The First Topic</title>
43
+ # <author-name>David</author-name>
44
+ # <approved type="boolean">false</approved>
45
+ # <content>Have a nice day</content>
46
+ # <author-email-address>david@loudthinking.com</author-email-address>
47
+ # <parent-id></parent-id>
48
+ # <last-read type="date">2004-04-15</last-read>
49
+ # </topic>
50
+ #
51
+ # To include first level associations use <tt>:include</tt>:
52
+ #
53
+ # firm.to_xml include: [ :account, :clients ]
54
+ #
55
+ # <?xml version="1.0" encoding="UTF-8"?>
56
+ # <firm>
57
+ # <id type="integer">1</id>
58
+ # <rating type="integer">1</rating>
59
+ # <name>37signals</name>
60
+ # <clients type="array">
61
+ # <client>
62
+ # <rating type="integer">1</rating>
63
+ # <name>Summit</name>
64
+ # </client>
65
+ # <client>
66
+ # <rating type="integer">1</rating>
67
+ # <name>Microsoft</name>
68
+ # </client>
69
+ # </clients>
70
+ # <account>
71
+ # <id type="integer">1</id>
72
+ # <credit-limit type="integer">50</credit-limit>
73
+ # </account>
74
+ # </firm>
75
+ #
76
+ # Additionally, the record being serialized will be passed to a Proc's second
77
+ # parameter. This allows for ad hoc additions to the resultant document that
78
+ # incorporate the context of the record being serialized. And by leveraging the
79
+ # closure created by a Proc, to_xml can be used to add elements that normally fall
80
+ # outside of the scope of the model -- for example, generating and appending URLs
81
+ # associated with models.
82
+ #
83
+ # proc = Proc.new { |options, record| options[:builder].tag!('name-reverse', record.name.reverse) }
84
+ # firm.to_xml procs: [ proc ]
85
+ #
86
+ # <firm>
87
+ # # ... normal attributes as shown above ...
88
+ # <name-reverse>slangis73</name-reverse>
89
+ # </firm>
90
+ #
91
+ # To include deeper levels of associations pass a hash like this:
92
+ #
93
+ # firm.to_xml include: {account: {}, clients: {include: :address}}
94
+ # <?xml version="1.0" encoding="UTF-8"?>
95
+ # <firm>
96
+ # <id type="integer">1</id>
97
+ # <rating type="integer">1</rating>
98
+ # <name>37signals</name>
99
+ # <clients type="array">
100
+ # <client>
101
+ # <rating type="integer">1</rating>
102
+ # <name>Summit</name>
103
+ # <address>
104
+ # ...
105
+ # </address>
106
+ # </client>
107
+ # <client>
108
+ # <rating type="integer">1</rating>
109
+ # <name>Microsoft</name>
110
+ # <address>
111
+ # ...
112
+ # </address>
113
+ # </client>
114
+ # </clients>
115
+ # <account>
116
+ # <id type="integer">1</id>
117
+ # <credit-limit type="integer">50</credit-limit>
118
+ # </account>
119
+ # </firm>
120
+ #
121
+ # To include any methods on the model being called use <tt>:methods</tt>:
122
+ #
123
+ # firm.to_xml methods: [ :calculated_earnings, :real_earnings ]
124
+ #
125
+ # <firm>
126
+ # # ... normal attributes as shown above ...
127
+ # <calculated-earnings>100000000000000000</calculated-earnings>
128
+ # <real-earnings>5</real-earnings>
129
+ # </firm>
130
+ #
131
+ # To call any additional Procs use <tt>:procs</tt>. The Procs are passed a
132
+ # modified version of the options hash that was given to +to_xml+:
133
+ #
134
+ # proc = Proc.new { |options| options[:builder].tag!('abc', 'def') }
135
+ # firm.to_xml procs: [ proc ]
136
+ #
137
+ # <firm>
138
+ # # ... normal attributes as shown above ...
139
+ # <abc>def</abc>
140
+ # </firm>
141
+ #
142
+ # Alternatively, you can yield the builder object as part of the +to_xml+ call:
143
+ #
144
+ # firm.to_xml do |xml|
145
+ # xml.creator do
146
+ # xml.first_name "David"
147
+ # xml.last_name "Heinemeier Hansson"
148
+ # end
149
+ # end
150
+ #
151
+ # <firm>
152
+ # # ... normal attributes as shown above ...
153
+ # <creator>
154
+ # <first_name>David</first_name>
155
+ # <last_name>Heinemeier Hansson</last_name>
156
+ # </creator>
157
+ # </firm>
158
+ #
159
+ # As noted above, you may override +to_xml+ in your ActiveRecord::Base
160
+ # subclasses to have complete control about what's generated. The general
161
+ # form of doing this is:
162
+ #
163
+ # class IHaveMyOwnXML < ActiveRecord::Base
164
+ # def to_xml(options = {})
165
+ # require 'builder'
166
+ # options[:indent] ||= 2
167
+ # xml = options[:builder] ||= ::Builder::XmlMarkup.new(indent: options[:indent])
168
+ # xml.instruct! unless options[:skip_instruct]
169
+ # xml.level_one do
170
+ # xml.tag!(:second_level, 'content')
171
+ # end
172
+ # end
173
+ # end
174
+ def to_xml(options = {}, &block)
175
+ XmlSerializer.new(self, options).serialize(&block)
176
+ end
177
+ end
178
+
179
+ class XmlSerializer < ActiveModel::Serializers::Xml::Serializer #:nodoc:
180
+ class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc:
181
+ def compute_type
182
+ klass = @serializable.class
183
+ type = if klass.serialized_attributes.key?(name)
184
+ super
185
+ elsif klass.columns_hash.key?(name)
186
+ klass.columns_hash[name].type
187
+ else
188
+ NilClass
189
+ end
190
+
191
+ { :text => :string,
192
+ :time => :datetime }[type] || type
193
+ end
194
+ protected :compute_type
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,26 @@
1
+ module ActiveRecord
2
+
3
+ # Statement cache is used to cache a single statement in order to avoid creating the AST again.
4
+ # Initializing the cache is done by passing the statement in the initialization block:
5
+ #
6
+ # cache = ActiveRecord::StatementCache.new do
7
+ # Book.where(name: "my book").limit(100)
8
+ # end
9
+ #
10
+ # The cached statement is executed by using the +execute+ method:
11
+ #
12
+ # cache.execute
13
+ #
14
+ # The relation returned by the block is cached, and for each +execute+ call the cached relation gets duped.
15
+ # Database is queried when +to_a+ is called on the relation.
16
+ class StatementCache
17
+ def initialize
18
+ @relation = yield
19
+ raise ArgumentError.new("Statement cannot be nil") if @relation.nil?
20
+ end
21
+
22
+ def execute
23
+ @relation.dup.to_a
24
+ end
25
+ end
26
+ end