PageRenderTime 60ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/activerecord/lib/active_record/associations/collection_proxy.rb

https://github.com/kuroda/rails
Ruby | 126 lines | 75 code | 16 blank | 35 comment | 11 complexity | 5c01e87d9cb7a73619d402ae9a662aa9 MD5 | raw file
  1. module ActiveRecord
  2. module Associations
  3. # Association proxies in Active Record are middlemen between the object that
  4. # holds the association, known as the <tt>@owner</tt>, and the actual associated
  5. # object, known as the <tt>@target</tt>. The kind of association any proxy is
  6. # about is available in <tt>@reflection</tt>. That's an instance of the class
  7. # ActiveRecord::Reflection::AssociationReflection.
  8. #
  9. # For example, given
  10. #
  11. # class Blog < ActiveRecord::Base
  12. # has_many :posts
  13. # end
  14. #
  15. # blog = Blog.first
  16. #
  17. # the association proxy in <tt>blog.posts</tt> has the object in +blog+ as
  18. # <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and
  19. # the <tt>@reflection</tt> object represents a <tt>:has_many</tt> macro.
  20. #
  21. # This class has most of the basic instance methods removed, and delegates
  22. # unknown methods to <tt>@target</tt> via <tt>method_missing</tt>. As a
  23. # corner case, it even removes the +class+ method and that's why you get
  24. #
  25. # blog.posts.class # => Array
  26. #
  27. # though the object behind <tt>blog.posts</tt> is not an Array, but an
  28. # ActiveRecord::Associations::HasManyAssociation.
  29. #
  30. # The <tt>@target</tt> object is not \loaded until needed. For example,
  31. #
  32. # blog.posts.count
  33. #
  34. # is computed directly through SQL and does not trigger by itself the
  35. # instantiation of the actual post records.
  36. class CollectionProxy # :nodoc:
  37. alias :proxy_extend :extend
  38. instance_methods.each { |m| undef_method m unless m.to_s =~ /^(?:nil\?|send|object_id|to_a)$|^__|^respond_to|proxy_/ }
  39. delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from,
  40. :lock, :readonly, :having, :pluck, :to => :scoped
  41. delegate :target, :load_target, :loaded?, :scoped,
  42. :to => :@association
  43. delegate :select, :find, :first, :last,
  44. :build, :create, :create!,
  45. :concat, :replace, :delete_all, :destroy_all, :delete, :destroy, :uniq,
  46. :sum, :count, :size, :length, :empty?,
  47. :any?, :many?, :include?,
  48. :to => :@association
  49. def initialize(association)
  50. @association = association
  51. Array.wrap(association.options[:extend]).each { |ext| proxy_extend(ext) }
  52. end
  53. alias_method :new, :build
  54. def proxy_association
  55. @association
  56. end
  57. def respond_to?(name, include_private = false)
  58. super ||
  59. (load_target && target.respond_to?(name, include_private)) ||
  60. proxy_association.klass.respond_to?(name, include_private)
  61. end
  62. def method_missing(method, *args, &block)
  63. match = DynamicFinderMatch.match(method)
  64. if match && match.instantiator?
  65. send(:find_or_instantiator_by_attributes, match, match.attribute_names, *args) do |r|
  66. proxy_association.send :set_owner_attributes, r
  67. proxy_association.send :add_to_target, r
  68. yield(r) if block_given?
  69. end
  70. end
  71. if target.respond_to?(method) || (!proxy_association.klass.respond_to?(method) && Class.respond_to?(method))
  72. if load_target
  73. if target.respond_to?(method)
  74. target.send(method, *args, &block)
  75. else
  76. begin
  77. super
  78. rescue NoMethodError => e
  79. raise e, e.message.sub(/ for #<.*$/, " via proxy for #{target}")
  80. end
  81. end
  82. end
  83. else
  84. scoped.readonly(nil).send(method, *args, &block)
  85. end
  86. end
  87. # Forwards <tt>===</tt> explicitly to the \target because the instance method
  88. # removal above doesn't catch it. Loads the \target if needed.
  89. def ===(other)
  90. other === load_target
  91. end
  92. def to_ary
  93. load_target.dup
  94. end
  95. alias_method :to_a, :to_ary
  96. def <<(*records)
  97. proxy_association.concat(records) && self
  98. end
  99. alias_method :push, :<<
  100. def clear
  101. delete_all
  102. self
  103. end
  104. def reload
  105. proxy_association.reload
  106. self
  107. end
  108. end
  109. end
  110. end