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,164 @@
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
+ default_scoped
29
+ end
30
+ end
31
+
32
+ def default_scoped # :nodoc:
33
+ relation.merge(build_default_scope)
34
+ end
35
+
36
+ # Collects attributes from scopes that should be applied when creating
37
+ # an AR instance for the particular class this is called on.
38
+ def scope_attributes # :nodoc:
39
+ all.scope_for_create
40
+ end
41
+
42
+ # Are there default attributes associated with this scope?
43
+ def scope_attributes? # :nodoc:
44
+ current_scope || default_scopes.any?
45
+ end
46
+
47
+ # Adds a class method for retrieving and querying objects. A \scope
48
+ # represents a narrowing of a database query, such as
49
+ # <tt>where(color: :red).select('shirts.*').includes(:washing_instructions)</tt>.
50
+ #
51
+ # class Shirt < ActiveRecord::Base
52
+ # scope :red, -> { where(color: 'red') }
53
+ # scope :dry_clean_only, -> { joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true) }
54
+ # end
55
+ #
56
+ # The above calls to +scope+ define class methods <tt>Shirt.red</tt> and
57
+ # <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>, in effect,
58
+ # represents the query <tt>Shirt.where(color: 'red')</tt>.
59
+ #
60
+ # You should always pass a callable object to the scopes defined
61
+ # with +scope+. This ensures that the scope is re-evaluated each
62
+ # time it is called.
63
+ #
64
+ # Note that this is simply 'syntactic sugar' for defining an actual
65
+ # class method:
66
+ #
67
+ # class Shirt < ActiveRecord::Base
68
+ # def self.red
69
+ # where(color: 'red')
70
+ # end
71
+ # end
72
+ #
73
+ # Unlike <tt>Shirt.find(...)</tt>, however, the object returned by
74
+ # <tt>Shirt.red</tt> is not an Array; it resembles the association object
75
+ # constructed by a +has_many+ declaration. For instance, you can invoke
76
+ # <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>,
77
+ # <tt>Shirt.red.where(size: 'small')</tt>. Also, just as with the
78
+ # association objects, named \scopes act like an Array, implementing
79
+ # Enumerable; <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>,
80
+ # and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if
81
+ # <tt>Shirt.red</tt> really was an Array.
82
+ #
83
+ # These named \scopes are composable. For instance,
84
+ # <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are
85
+ # both red and dry clean only. Nested finds and calculations also work
86
+ # with these compositions: <tt>Shirt.red.dry_clean_only.count</tt>
87
+ # returns the number of garments for which these criteria obtain.
88
+ # Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
89
+ #
90
+ # All scopes are available as class methods on the ActiveRecord::Base
91
+ # descendant upon which the \scopes were defined. But they are also
92
+ # available to +has_many+ associations. If,
93
+ #
94
+ # class Person < ActiveRecord::Base
95
+ # has_many :shirts
96
+ # end
97
+ #
98
+ # then <tt>elton.shirts.red.dry_clean_only</tt> will return all of
99
+ # Elton's red, dry clean only shirts.
100
+ #
101
+ # \Named scopes can also have extensions, just as with +has_many+
102
+ # declarations:
103
+ #
104
+ # class Shirt < ActiveRecord::Base
105
+ # scope :red, -> { where(color: 'red') } do
106
+ # def dom_id
107
+ # 'red_shirts'
108
+ # end
109
+ # end
110
+ # end
111
+ #
112
+ # Scopes can also be used while creating/building a record.
113
+ #
114
+ # class Article < ActiveRecord::Base
115
+ # scope :published, -> { where(published: true) }
116
+ # end
117
+ #
118
+ # Article.published.new.published # => true
119
+ # Article.published.create.published # => true
120
+ #
121
+ # \Class methods on your model are automatically available
122
+ # on scopes. Assuming the following setup:
123
+ #
124
+ # class Article < ActiveRecord::Base
125
+ # scope :published, -> { where(published: true) }
126
+ # scope :featured, -> { where(featured: true) }
127
+ #
128
+ # def self.latest_article
129
+ # order('published_at desc').first
130
+ # end
131
+ #
132
+ # def self.titles
133
+ # pluck(:title)
134
+ # end
135
+ # end
136
+ #
137
+ # We are able to call the methods like this:
138
+ #
139
+ # Article.published.featured.latest_article
140
+ # Article.featured.titles
141
+ def scope(name, body, &block)
142
+ unless body.respond_to?(:call)
143
+ raise ArgumentError, 'The scope body needs to be callable.'
144
+ end
145
+
146
+ if dangerous_class_method?(name)
147
+ raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
148
+ "on the model \"#{self.name}\", but Active Record already defined " \
149
+ "a class method with the same name."
150
+ end
151
+
152
+ extension = Module.new(&block) if block
153
+
154
+ singleton_class.send(:define_method, name) do |*args|
155
+ scope = all.scoping { body.call(*args) }
156
+ scope = scope.extending(extension) if extension
157
+
158
+ scope || all
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ 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,193 @@
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
+ column = klass.columns_hash[name] || Type::Value.new
184
+
185
+ type = ActiveSupport::XmlMini::TYPE_NAMES[value.class.name] || column.type
186
+
187
+ { :text => :string,
188
+ :time => :datetime }[type] || type
189
+ end
190
+ protected :compute_type
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,111 @@
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 create block:
5
+ #
6
+ # cache = StatementCache.create(Book.connection) do |params|
7
+ # Book.where(name: "my book").where("author_id > 3")
8
+ # end
9
+ #
10
+ # The cached statement is executed by using the +execute+ method:
11
+ #
12
+ # cache.execute([], Book, Book.connection)
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
+ #
17
+ # If you want to cache the statement without the values you can use the +bind+ method of the
18
+ # block parameter.
19
+ #
20
+ # cache = StatementCache.create(Book.connection) do |params|
21
+ # Book.where(name: params.bind)
22
+ # end
23
+ #
24
+ # And pass the bind values as the first argument of +execute+ call.
25
+ #
26
+ # cache.execute(["my book"], Book, Book.connection)
27
+ class StatementCache # :nodoc:
28
+ class Substitute; end # :nodoc:
29
+
30
+ class Query # :nodoc:
31
+ def initialize(sql)
32
+ @sql = sql
33
+ end
34
+
35
+ def sql_for(binds, connection)
36
+ @sql
37
+ end
38
+ end
39
+
40
+ class PartialQuery < Query # :nodoc:
41
+ def initialize values
42
+ @values = values
43
+ @indexes = values.each_with_index.find_all { |thing,i|
44
+ Arel::Nodes::BindParam === thing
45
+ }.map(&:last)
46
+ end
47
+
48
+ def sql_for(binds, connection)
49
+ val = @values.dup
50
+ binds = binds.dup
51
+ @indexes.each { |i| val[i] = connection.quote(*binds.shift.reverse) }
52
+ val.join
53
+ end
54
+ end
55
+
56
+ def self.query(visitor, ast)
57
+ Query.new visitor.accept(ast, Arel::Collectors::SQLString.new).value
58
+ end
59
+
60
+ def self.partial_query(visitor, ast, collector)
61
+ collected = visitor.accept(ast, collector).value
62
+ PartialQuery.new collected
63
+ end
64
+
65
+ class Params # :nodoc:
66
+ def bind; Substitute.new; end
67
+ end
68
+
69
+ class BindMap # :nodoc:
70
+ def initialize(bind_values)
71
+ @indexes = []
72
+ @bind_values = bind_values
73
+
74
+ bind_values.each_with_index do |(_, value), i|
75
+ if Substitute === value
76
+ @indexes << i
77
+ end
78
+ end
79
+ end
80
+
81
+ def bind(values)
82
+ bvs = @bind_values.map { |pair| pair.dup }
83
+ @indexes.each_with_index { |offset,i| bvs[offset][1] = values[i] }
84
+ bvs
85
+ end
86
+ end
87
+
88
+ attr_reader :bind_map, :query_builder
89
+
90
+ def self.create(connection, block = Proc.new)
91
+ relation = block.call Params.new
92
+ bind_map = BindMap.new relation.bind_values
93
+ query_builder = connection.cacheable_query relation.arel
94
+ new query_builder, bind_map
95
+ end
96
+
97
+ def initialize(query_builder, bind_map)
98
+ @query_builder = query_builder
99
+ @bind_map = bind_map
100
+ end
101
+
102
+ def execute(params, klass, connection)
103
+ bind_values = bind_map.bind params
104
+
105
+ sql = query_builder.sql_for bind_values, connection
106
+
107
+ klass.find_by_sql sql, bind_values
108
+ end
109
+ alias :call :execute
110
+ end
111
+ end