Class: Object

Inherits:
BasicObject
Includes:
ActiveSupport::Dependencies::RequireDependency, ActiveSupport::Tryable
Defined in:
lib/active_support/core_ext/object/duplicable.rb,
lib/active_support/core_ext/object/try.rb,
lib/active_support/core_ext/object/json.rb,
lib/active_support/core_ext/object/with.rb,
lib/active_support/core_ext/object/blank.rb,
lib/active_support/core_ext/object/deep_dup.rb,
lib/active_support/core_ext/object/to_query.rb,
lib/active_support/core_ext/object/acts_like.rb,
lib/active_support/core_ext/object/inclusion.rb,
lib/active_support/core_ext/object/with_options.rb,
lib/active_support/core_ext/string/output_safety.rb,
lib/active_support/core_ext/object/instance_variables.rb
more...

Overview

– Most objects are cloneable, but not all. For example you can’t dup methods:

method(:puts).dup # => TypeError: allocator undefined for Method

Classes may signal their instances are not duplicable removing dup/clone or raising exceptions from them. So, to dup an arbitrary object you normally use an optimistic approach and are ready to catch an exception, say:

arbitrary_object.dup rescue object

Rails dups objects in a few critical spots where they are not that arbitrary. That rescue is very expensive (like 40 times slower than a predicate), and it is often triggered.

That’s why we hardcode the following cases and check duplicable? instead of using that rescue idiom. ++

Instance Method Summary collapse

Methods included from ActiveSupport::Tryable

#try, #try!

Methods included from ActiveSupport::Dependencies::RequireDependency

#require_dependency

Instance Method Details

#acts_like?(duck) ⇒ Boolean

Provides a way to check whether some class acts like some other class based on the existence of an appropriately-named marker method.

A class that provides the same interface as SomeClass may define a marker method named acts_like_some_class? to signal its compatibility to callers of acts_like?(:some_class).

For example, Active Support extends Date to define an acts_like_date? method, and extends Time to define acts_like_time?. As a result, developers can call x.acts_like?(:time) and x.acts_like?(:date) to test duck-type compatibility, and classes that are able to act like Time can also define an acts_like_time? method to interoperate.

Note that the marker method is only expected to exist. It isn’t called, so its body or return value are irrelevant.

Example: A class that provides the same interface as String

This class may define:

class Stringish
  def acts_like_string?
  end
end

Then client code can query for duck-type-safeness this way:

Stringish.new.acts_like?(:string) # => true

Returns:

  • (Boolean)
[View source]

33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/active_support/core_ext/object/acts_like.rb', line 33

def acts_like?(duck)
  case duck
  when :time
    respond_to? :acts_like_time?
  when :date
    respond_to? :acts_like_date?
  when :string
    respond_to? :acts_like_string?
  else
    respond_to? :"acts_like_#{duck}?"
  end
end

#as_json(options = nil) ⇒ Object

:nodoc:

[View source]

59
60
61
62
63
64
65
# File 'lib/active_support/core_ext/object/json.rb', line 59

def as_json(options = nil) # :nodoc:
  if respond_to?(:to_hash)
    to_hash.as_json(options)
  else
    instance_values.as_json(options)
  end
end

#blank?true, false

An object is blank if it’s false, empty, or a whitespace string. For example, nil, ”, ‘ ’, [], {}, and false are all blank.

This simplifies

!address || address.empty?

to

address.blank?

Returns:

  • (true, false)
[View source]

18
19
20
# File 'lib/active_support/core_ext/object/blank.rb', line 18

def blank?
  respond_to?(:empty?) ? !!empty? : false
end

#deep_dupObject

Returns a deep copy of object if it’s duplicable. If it’s not duplicable, returns self.

object = Object.new
dup    = object.deep_dup
dup.instance_variable_set(:@a, 1)

object.instance_variable_defined?(:@a) # => false
dup.instance_variable_defined?(:@a)    # => true
[View source]

15
16
17
# File 'lib/active_support/core_ext/object/deep_dup.rb', line 15

def deep_dup
  duplicable? ? dup : self
end

#duplicable?Boolean

Can you safely dup this object?

False for method objects; true otherwise.

Returns:

  • (Boolean)
[View source]

26
27
28
# File 'lib/active_support/core_ext/object/duplicable.rb', line 26

def duplicable?
  true
end

#html_safe?Boolean

Returns:

  • (Boolean)
[View source]

7
8
9
# File 'lib/active_support/core_ext/string/output_safety.rb', line 7

def html_safe?
  false
end

#in?(another_object) ⇒ Boolean

Returns true if this object is included in the argument.

When argument is a Range, #cover? is used to properly handle inclusion check within open ranges. Otherwise, argument must be any object which responds to #include?. Usage:

characters = ["Konata", "Kagami", "Tsukasa"]
"Konata".in?(characters) # => true

For non Range arguments, this will throw an ArgumentError if the argument doesn’t respond to #include?.

Returns:

  • (Boolean)
[View source]

15
16
17
18
19
20
21
22
23
24
# File 'lib/active_support/core_ext/object/inclusion.rb', line 15

def in?(another_object)
  case another_object
  when Range
    another_object.cover?(self)
  else
    another_object.include?(self)
  end
rescue NoMethodError
  raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
end

#instance_valuesObject

Returns a hash with string keys that maps instance variable names without “@” to their corresponding values.

class C
  def initialize(x, y)
    @x, @y = x, y
  end
end

C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
[View source]

14
15
16
17
18
# File 'lib/active_support/core_ext/object/instance_variables.rb', line 14

def instance_values
  instance_variables.to_h do |ivar|
    [ivar[1..-1].freeze, instance_variable_get(ivar)]
  end
end

#instance_variable_namesObject

Returns an array of instance variable names as strings including “@”.

class C
  def initialize(x, y)
    @x, @y = x, y
  end
end

C.new(0, 1).instance_variable_names # => ["@y", "@x"]
[View source]

29
30
31
# File 'lib/active_support/core_ext/object/instance_variables.rb', line 29

def instance_variable_names
  instance_variables.map(&:name)
end

#presenceObject

Returns the receiver if it’s present otherwise returns nil. object.presence is equivalent to

object.present? ? object : nil

For example, something like

state   = params[:state]   if params[:state].present?
country = params[:country] if params[:country].present?
region  = state || country || 'US'

becomes

region = params[:state].presence || params[:country].presence || 'US'

Returns:

[View source]

45
46
47
# File 'lib/active_support/core_ext/object/blank.rb', line 45

def presence
  self if present?
end

#presence_in(another_object) ⇒ Object

Returns the receiver if it’s included in the argument otherwise returns nil. Argument must be any object which responds to #include?. Usage:

params[:bucket_type].presence_in %w( project calendar )

This will throw an ArgumentError if the argument doesn’t respond to #include?.

Returns:

[View source]

34
35
36
# File 'lib/active_support/core_ext/object/inclusion.rb', line 34

def presence_in(another_object)
  in?(another_object) ? self : nil
end

#present?true, false

An object is present if it’s not blank.

Returns:

  • (true, false)
[View source]

25
26
27
# File 'lib/active_support/core_ext/object/blank.rb', line 25

def present?
  !blank?
end

#to_paramObject

Alias of to_s.

[View source]

7
8
9
# File 'lib/active_support/core_ext/object/to_query.rb', line 7

def to_param
  to_s
end

#to_query(key) ⇒ Object

Converts an object into a string suitable for use as a URL query string, using the given key as the param name.

[View source]

13
14
15
# File 'lib/active_support/core_ext/object/to_query.rb', line 13

def to_query(key)
  "#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
end

#with(**attributes) ⇒ Object

Set and restore public attributes around a block.

client.timeout # => 5
client.with(timeout: 1) do |c|
  c.timeout # => 1
end
client.timeout # => 5

The receiver is yielded to the provided block.

This method is a shorthand for the common begin/ensure pattern:

old_value = object.attribute
begin
  object.attribute = new_value
  # do things
ensure
  object.attribute = old_value
end

It can be used on any object as long as both the reader and writer methods are public.

[View source]

26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/active_support/core_ext/object/with.rb', line 26

def with(**attributes)
  old_values = {}
  begin
    attributes.each do |key, value|
      old_values[key] = public_send(key)
      public_send("#{key}=", value)
    end
    yield self
  ensure
    old_values.each do |key, old_value|
      public_send("#{key}=", old_value)
    end
  end
end

#with_options(options, &block) ⇒ Object

An elegant way to factor duplication out of options passed to a series of method calls. Each method called in the block, with the block variable as the receiver, will have its options merged with the default options Hash or Hash-like object provided. Each method called on the block variable must take an options hash as its final argument.

Without with_options, this code contains duplication:

class Account < ActiveRecord::Base
  has_many :customers, dependent: :destroy
  has_many :products,  dependent: :destroy
  has_many :invoices,  dependent: :destroy
  has_many :expenses,  dependent: :destroy
end

Using with_options, we can remove the duplication:

class Account < ActiveRecord::Base
  with_options dependent: :destroy do |assoc|
    assoc.has_many :customers
    assoc.has_many :products
    assoc.has_many :invoices
    assoc.has_many :expenses
  end
end

It can also be used with an explicit receiver:

I18n.with_options locale: user.locale, scope: 'newsletter' do |i18n|
  subject i18n.t :subject
  body    i18n.t :body, user_name: user.name
end

When you don’t pass an explicit receiver, it executes the whole block in merging options context:

class Account < ActiveRecord::Base
  with_options dependent: :destroy do
    has_many :customers
    has_many :products
    has_many :invoices
    has_many :expenses
  end
end

with_options can also be nested since the call is forwarded to its receiver.

NOTE: Each nesting level will merge inherited defaults in addition to their own.

class Post < ActiveRecord::Base
  with_options if: :persisted?, length: { minimum: 50 } do
    validates :content, if: -> { content.present? }
  end
end

The code is equivalent to:

validates :content, length: { minimum: 50 }, if: -> { content.present? }

Hence the inherited default for if key is ignored.

NOTE: You cannot call class methods implicitly inside of with_options. You can access these methods using the class name instead:

class Phone < ActiveRecord::Base
  enum :phone_number_type, { home: 0, office: 1, mobile: 2 }

  with_options presence: true do
    validates :phone_number_type, inclusion: { in: Phone.phone_number_types.keys }
  end
end

When the block argument is omitted, the decorated Object instance is returned:

module MyStyledHelpers
  def styled
    with_options style: "color: red;"
  end
end

styled.link_to "I'm red", "/"
# => <a href="/" style="color: red;">I'm red</a>

styled.button_tag "I'm red too!"
# => <button style="color: red;">I'm red too!</button>
[View source]

92
93
94
95
96
97
98
99
100
# File 'lib/active_support/core_ext/object/with_options.rb', line 92

def with_options(options, &block)
  option_merger = ActiveSupport::OptionMerger.new(self, options)

  if block
    block.arity.zero? ? option_merger.instance_eval(&block) : block.call(option_merger)
  else
    option_merger
  end
end