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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1372 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +218 -0
- data/examples/performance.rb +184 -0
- data/examples/simple.rb +14 -0
- data/lib/active_record.rb +173 -0
- data/lib/active_record/aggregations.rb +266 -0
- data/lib/active_record/association_relation.rb +22 -0
- data/lib/active_record/associations.rb +1724 -0
- data/lib/active_record/associations/alias_tracker.rb +87 -0
- data/lib/active_record/associations/association.rb +253 -0
- data/lib/active_record/associations/association_scope.rb +194 -0
- data/lib/active_record/associations/belongs_to_association.rb +111 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
- data/lib/active_record/associations/builder/association.rb +149 -0
- data/lib/active_record/associations/builder/belongs_to.rb +116 -0
- data/lib/active_record/associations/builder/collection_association.rb +91 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +124 -0
- data/lib/active_record/associations/builder/has_many.rb +15 -0
- data/lib/active_record/associations/builder/has_one.rb +23 -0
- data/lib/active_record/associations/builder/singular_association.rb +38 -0
- data/lib/active_record/associations/collection_association.rb +634 -0
- data/lib/active_record/associations/collection_proxy.rb +1027 -0
- data/lib/active_record/associations/has_many_association.rb +184 -0
- data/lib/active_record/associations/has_many_through_association.rb +238 -0
- data/lib/active_record/associations/has_one_association.rb +105 -0
- data/lib/active_record/associations/has_one_through_association.rb +36 -0
- data/lib/active_record/associations/join_dependency.rb +282 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +122 -0
- data/lib/active_record/associations/join_dependency/join_base.rb +22 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
- data/lib/active_record/associations/preloader.rb +203 -0
- data/lib/active_record/associations/preloader/association.rb +162 -0
- data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
- data/lib/active_record/associations/preloader/collection_association.rb +24 -0
- data/lib/active_record/associations/preloader/has_many.rb +17 -0
- data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
- data/lib/active_record/associations/preloader/has_one.rb +23 -0
- data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
- data/lib/active_record/associations/preloader/singular_association.rb +21 -0
- data/lib/active_record/associations/preloader/through_association.rb +96 -0
- data/lib/active_record/associations/singular_association.rb +86 -0
- data/lib/active_record/associations/through_association.rb +96 -0
- data/lib/active_record/attribute.rb +149 -0
- data/lib/active_record/attribute_assignment.rb +212 -0
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods.rb +439 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +71 -0
- data/lib/active_record/attribute_methods/dirty.rb +181 -0
- data/lib/active_record/attribute_methods/primary_key.rb +128 -0
- data/lib/active_record/attribute_methods/query.rb +40 -0
- data/lib/active_record/attribute_methods/read.rb +103 -0
- data/lib/active_record/attribute_methods/serialization.rb +70 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
- data/lib/active_record/attribute_methods/write.rb +83 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attribute_set/builder.rb +86 -0
- data/lib/active_record/attributes.rb +139 -0
- data/lib/active_record/autosave_association.rb +439 -0
- data/lib/active_record/base.rb +317 -0
- data/lib/active_record/callbacks.rb +313 -0
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/coders/yaml_column.rb +38 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +659 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +373 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +133 -0
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +574 -0
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +991 -0
- data/lib/active_record/connection_adapters/abstract/transaction.rb +219 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +883 -0
- data/lib/active_record/connection_adapters/column.rb +82 -0
- data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +282 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +491 -0
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +588 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +754 -0
- data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +628 -0
- data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
- data/lib/active_record/connection_handling.rb +132 -0
- data/lib/active_record/core.rb +566 -0
- data/lib/active_record/counter_cache.rb +175 -0
- data/lib/active_record/dynamic_matchers.rb +140 -0
- data/lib/active_record/enum.rb +198 -0
- data/lib/active_record/errors.rb +252 -0
- data/lib/active_record/explain.rb +38 -0
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +29 -0
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +1007 -0
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +247 -0
- data/lib/active_record/integration.rb +113 -0
- data/lib/active_record/locale/en.yml +47 -0
- data/lib/active_record/locking/optimistic.rb +204 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/log_subscriber.rb +75 -0
- data/lib/active_record/migration.rb +1051 -0
- data/lib/active_record/migration/command_recorder.rb +197 -0
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/model_schema.rb +340 -0
- data/lib/active_record/nested_attributes.rb +548 -0
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +81 -0
- data/lib/active_record/persistence.rb +532 -0
- data/lib/active_record/query_cache.rb +56 -0
- data/lib/active_record/querying.rb +68 -0
- data/lib/active_record/railtie.rb +162 -0
- data/lib/active_record/railties/console_sandbox.rb +5 -0
- data/lib/active_record/railties/controller_runtime.rb +50 -0
- data/lib/active_record/railties/databases.rake +391 -0
- data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
- data/lib/active_record/readonly_attributes.rb +23 -0
- data/lib/active_record/reflection.rb +881 -0
- data/lib/active_record/relation.rb +681 -0
- data/lib/active_record/relation/batches.rb +138 -0
- data/lib/active_record/relation/calculations.rb +403 -0
- data/lib/active_record/relation/delegation.rb +140 -0
- data/lib/active_record/relation/finder_methods.rb +528 -0
- data/lib/active_record/relation/merger.rb +170 -0
- data/lib/active_record/relation/predicate_builder.rb +126 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +47 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/query_methods.rb +1176 -0
- data/lib/active_record/relation/spawn_methods.rb +75 -0
- data/lib/active_record/result.rb +131 -0
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +191 -0
- data/lib/active_record/schema.rb +64 -0
- data/lib/active_record/schema_dumper.rb +251 -0
- data/lib/active_record/schema_migration.rb +56 -0
- data/lib/active_record/scoping.rb +87 -0
- data/lib/active_record/scoping/default.rb +134 -0
- data/lib/active_record/scoping/named.rb +164 -0
- data/lib/active_record/serialization.rb +22 -0
- data/lib/active_record/serializers/xml_serializer.rb +193 -0
- data/lib/active_record/statement_cache.rb +111 -0
- data/lib/active_record/store.rb +205 -0
- data/lib/active_record/tasks/database_tasks.rb +296 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +145 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
- data/lib/active_record/timestamp.rb +121 -0
- data/lib/active_record/transactions.rb +417 -0
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +30 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
- data/lib/active_record/type/integer.rb +55 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +56 -0
- data/lib/active_record/type/string.rb +36 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/validations.rb +90 -0
- data/lib/active_record/validations/associated.rb +51 -0
- data/lib/active_record/validations/presence.rb +67 -0
- data/lib/active_record/validations/uniqueness.rb +229 -0
- data/lib/active_record/version.rb +8 -0
- data/lib/rails/generators/active_record.rb +17 -0
- data/lib/rails/generators/active_record/migration.rb +18 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +70 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +22 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +45 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +52 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
- data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
- 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
|