activesupport-refinements 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +32 -0
- data/Rakefile +1 -0
- data/activesupport-refinements.gemspec +21 -0
- data/lib/active_support/refinements/core_ext/array.rb +7 -0
- data/lib/active_support/refinements/core_ext/array/access.rb +56 -0
- data/lib/active_support/refinements/core_ext/array/conversions.rb +224 -0
- data/lib/active_support/refinements/core_ext/array/extract_options.rb +31 -0
- data/lib/active_support/refinements/core_ext/array/grouping.rb +101 -0
- data/lib/active_support/refinements/core_ext/array/prepend_and_append.rb +9 -0
- data/lib/active_support/refinements/core_ext/array/uniq_by.rb +21 -0
- data/lib/active_support/refinements/core_ext/array/wrap.rb +48 -0
- data/lib/active_support/refinements/core_ext/benchmark.rb +7 -0
- data/lib/active_support/refinements/core_ext/big_decimal.rb +1 -0
- data/lib/active_support/refinements/core_ext/big_decimal/conversions.rb +32 -0
- data/lib/active_support/refinements/core_ext/class.rb +4 -0
- data/lib/active_support/refinements/core_ext/class/attribute.rb +119 -0
- data/lib/active_support/refinements/core_ext/class/attribute_accessors.rb +172 -0
- data/lib/active_support/refinements/core_ext/class/delegating_attributes.rb +42 -0
- data/lib/active_support/refinements/core_ext/class/subclasses.rb +44 -0
- data/lib/active_support/refinements/core_ext/date.rb +5 -0
- data/lib/active_support/refinements/core_ext/date/acts_like.rb +10 -0
- data/lib/active_support/refinements/core_ext/date/calculations.rb +123 -0
- data/lib/active_support/refinements/core_ext/date/conversions.rb +86 -0
- data/lib/active_support/refinements/core_ext/date/zones.rb +17 -0
- data/lib/active_support/refinements/core_ext/date_and_time/calculations.rb +232 -0
- data/lib/active_support/refinements/core_ext/date_time.rb +4 -0
- data/lib/active_support/refinements/core_ext/date_time/acts_like.rb +15 -0
- data/lib/active_support/refinements/core_ext/date_time/calculations.rb +143 -0
- data/lib/active_support/refinements/core_ext/date_time/conversions.rb +93 -0
- data/lib/active_support/refinements/core_ext/date_time/zones.rb +26 -0
- data/lib/active_support/refinements/core_ext/enumerable.rb +82 -0
- data/lib/active_support/refinements/core_ext/exception.rb +5 -0
- data/lib/active_support/refinements/core_ext/file.rb +1 -0
- data/lib/active_support/refinements/core_ext/file/atomic.rb +60 -0
- data/lib/active_support/refinements/core_ext/hash.rb +8 -0
- data/lib/active_support/refinements/core_ext/hash/conversions.rb +161 -0
- data/lib/active_support/refinements/core_ext/hash/deep_merge.rb +29 -0
- data/lib/active_support/refinements/core_ext/hash/diff.rb +15 -0
- data/lib/active_support/refinements/core_ext/hash/except.rb +17 -0
- data/lib/active_support/refinements/core_ext/hash/indifferent_access.rb +24 -0
- data/lib/active_support/refinements/core_ext/hash/keys.rb +140 -0
- data/lib/active_support/refinements/core_ext/hash/reverse_merge.rb +24 -0
- data/lib/active_support/refinements/core_ext/hash/slice.rb +42 -0
- data/lib/active_support/refinements/core_ext/integer.rb +3 -0
- data/lib/active_support/refinements/core_ext/integer/inflections.rb +31 -0
- data/lib/active_support/refinements/core_ext/integer/multiple.rb +12 -0
- data/lib/active_support/refinements/core_ext/integer/time.rb +43 -0
- data/lib/active_support/refinements/core_ext/kernel.rb +4 -0
- data/lib/active_support/refinements/core_ext/kernel/agnostics.rb +13 -0
- data/lib/active_support/refinements/core_ext/kernel/debugger.rb +12 -0
- data/lib/active_support/refinements/core_ext/kernel/reporting.rb +97 -0
- data/lib/active_support/refinements/core_ext/kernel/singleton_class.rb +8 -0
- data/lib/active_support/refinements/core_ext/load_error.rb +27 -0
- data/lib/active_support/refinements/core_ext/logger.rb +86 -0
- data/lib/active_support/refinements/core_ext/module.rb +10 -0
- data/lib/active_support/refinements/core_ext/module/aliasing.rb +69 -0
- data/lib/active_support/refinements/core_ext/module/anonymous.rb +21 -0
- data/lib/active_support/refinements/core_ext/module/attr_internal.rb +40 -0
- data/lib/active_support/refinements/core_ext/module/attribute_accessors.rb +68 -0
- data/lib/active_support/refinements/core_ext/module/delegation.rb +172 -0
- data/lib/active_support/refinements/core_ext/module/deprecation.rb +27 -0
- data/lib/active_support/refinements/core_ext/module/introspection.rb +80 -0
- data/lib/active_support/refinements/core_ext/module/qualified_const.rb +54 -0
- data/lib/active_support/refinements/core_ext/module/reachable.rb +10 -0
- data/lib/active_support/refinements/core_ext/module/remove_method.rb +14 -0
- data/lib/active_support/refinements/core_ext/name_error.rb +20 -0
- data/lib/active_support/refinements/core_ext/numeric.rb +3 -0
- data/lib/active_support/refinements/core_ext/numeric/bytes.rb +46 -0
- data/lib/active_support/refinements/core_ext/numeric/conversions.rb +137 -0
- data/lib/active_support/refinements/core_ext/numeric/time.rb +81 -0
- data/lib/active_support/refinements/core_ext/object.rb +14 -0
- data/lib/active_support/refinements/core_ext/object/acts_like.rb +12 -0
- data/lib/active_support/refinements/core_ext/object/blank.rb +107 -0
- data/lib/active_support/refinements/core_ext/object/conversions.rb +4 -0
- data/lib/active_support/refinements/core_ext/object/deep_dup.rb +48 -0
- data/lib/active_support/refinements/core_ext/object/duplicable.rb +92 -0
- data/lib/active_support/refinements/core_ext/object/inclusion.rb +27 -0
- data/lib/active_support/refinements/core_ext/object/instance_variables.rb +30 -0
- data/lib/active_support/refinements/core_ext/object/to_json.rb +27 -0
- data/lib/active_support/refinements/core_ext/object/to_param.rb +60 -0
- data/lib/active_support/refinements/core_ext/object/to_query.rb +29 -0
- data/lib/active_support/refinements/core_ext/object/try.rb +72 -0
- data/lib/active_support/refinements/core_ext/object/with_options.rb +44 -0
- data/lib/active_support/refinements/core_ext/proc.rb +19 -0
- data/lib/active_support/refinements/core_ext/range.rb +3 -0
- data/lib/active_support/refinements/core_ext/range/conversions.rb +21 -0
- data/lib/active_support/refinements/core_ext/range/include_range.rb +23 -0
- data/lib/active_support/refinements/core_ext/range/overlaps.rb +10 -0
- data/lib/active_support/refinements/core_ext/regexp.rb +7 -0
- data/lib/active_support/refinements/core_ext/string.rb +13 -0
- data/lib/active_support/refinements/core_ext/string/access.rb +106 -0
- data/lib/active_support/refinements/core_ext/string/behavior.rb +8 -0
- data/lib/active_support/refinements/core_ext/string/conversions.rb +60 -0
- data/lib/active_support/refinements/core_ext/string/encoding.rb +10 -0
- data/lib/active_support/refinements/core_ext/string/exclude.rb +13 -0
- data/lib/active_support/refinements/core_ext/string/filters.rb +54 -0
- data/lib/active_support/refinements/core_ext/string/indent.rb +45 -0
- data/lib/active_support/refinements/core_ext/string/inflections.rb +214 -0
- data/lib/active_support/refinements/core_ext/string/inquiry.rb +15 -0
- data/lib/active_support/refinements/core_ext/string/multibyte.rb +58 -0
- data/lib/active_support/refinements/core_ext/string/output_safety.rb +194 -0
- data/lib/active_support/refinements/core_ext/string/starts_ends_with.rb +6 -0
- data/lib/active_support/refinements/core_ext/string/strip.rb +28 -0
- data/lib/active_support/refinements/core_ext/string/xchar.rb +18 -0
- data/lib/active_support/refinements/core_ext/time.rb +5 -0
- data/lib/active_support/refinements/core_ext/time/acts_like.rb +10 -0
- data/lib/active_support/refinements/core_ext/time/calculations.rb +251 -0
- data/lib/active_support/refinements/core_ext/time/conversions.rb +65 -0
- data/lib/active_support/refinements/core_ext/time/marshal.rb +30 -0
- data/lib/active_support/refinements/core_ext/time/zones.rb +98 -0
- data/lib/active_support/refinements/core_ext/uri.rb +28 -0
- data/lib/activesupport-refinements.rb +9 -0
- data/lib/activesupport-refinements/version.rb +5 -0
- data/refine_core_ext.rb +45 -0
- data/spec/hwia_spec.rb +15 -0
- data/spec/try_spec.rb +18 -0
- metadata +182 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
module ArrayExt; end; module ArrayExt::PrependAndAppend
|
|
2
|
+
refine Array do
|
|
3
|
+
# The human way of thinking about adding stuff to the end of a list is with append
|
|
4
|
+
# alias_method :append, :<<
|
|
5
|
+
|
|
6
|
+
# The human way of thinking about adding stuff to the beginning of a list is with prepend
|
|
7
|
+
# alias_method :prepend, :unshift
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module ArrayExt; end; module ArrayExt::UniqBy
|
|
2
|
+
refine Array do
|
|
3
|
+
# *DEPRECATED*: Use +Array#uniq+ instead.
|
|
4
|
+
#
|
|
5
|
+
# Returns a unique array based on the criteria in the block.
|
|
6
|
+
#
|
|
7
|
+
# [1, 2, 3, 4].uniq_by { |i| i.odd? } # => [1, 2]
|
|
8
|
+
def uniq_by(&block)
|
|
9
|
+
ActiveSupport::Deprecation.warn 'uniq_by is deprecated. Use Array#uniq instead', caller
|
|
10
|
+
uniq(&block)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# *DEPRECATED*: Use +Array#uniq!+ instead.
|
|
14
|
+
#
|
|
15
|
+
# Same as +uniq_by+, but modifies +self+.
|
|
16
|
+
def uniq_by!(&block)
|
|
17
|
+
ActiveSupport::Deprecation.warn 'uniq_by! is deprecated. Use Array#uniq! instead', caller
|
|
18
|
+
uniq!(&block)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module ArrayExt; end; module ArrayExt::Wrap
|
|
2
|
+
refine Array do
|
|
3
|
+
# Wraps its argument in an array unless it is already an array (or array-like).
|
|
4
|
+
#
|
|
5
|
+
# Specifically:
|
|
6
|
+
#
|
|
7
|
+
# * If the argument is +nil+ an empty list is returned.
|
|
8
|
+
# * Otherwise, if the argument responds to +to_ary+ it is invoked, and its result returned.
|
|
9
|
+
# * Otherwise, returns an array with the argument as its single element.
|
|
10
|
+
#
|
|
11
|
+
# Array.wrap(nil) # => []
|
|
12
|
+
# Array.wrap([1, 2, 3]) # => [1, 2, 3]
|
|
13
|
+
# Array.wrap(0) # => [0]
|
|
14
|
+
#
|
|
15
|
+
# This method is similar in purpose to <tt>Kernel#Array</tt>, but there are some differences:
|
|
16
|
+
#
|
|
17
|
+
# * If the argument responds to +to_ary+ the method is invoked. <tt>Kernel#Array</tt>
|
|
18
|
+
# moves on to try +to_a+ if the returned value is +nil+, but <tt>Array.wrap</tt> returns
|
|
19
|
+
# such a +nil+ right away.
|
|
20
|
+
# * If the returned value from +to_ary+ is neither +nil+ nor an +Array+ object, <tt>Kernel#Array</tt>
|
|
21
|
+
# raises an exception, while <tt>Array.wrap</tt> does not, it just returns the value.
|
|
22
|
+
# * It does not call +to_a+ on the argument, though special-cases +nil+ to return an empty array.
|
|
23
|
+
#
|
|
24
|
+
# The last point is particularly worth comparing for some enumerables:
|
|
25
|
+
#
|
|
26
|
+
# Array(foo: :bar) # => [[:foo, :bar]]
|
|
27
|
+
# Array.wrap(foo: :bar) # => [{foo: :bar}]
|
|
28
|
+
#
|
|
29
|
+
# There's also a related idiom that uses the splat operator:
|
|
30
|
+
#
|
|
31
|
+
# [*object]
|
|
32
|
+
#
|
|
33
|
+
# which for +nil+ returns <tt>[nil]</tt> (Ruby 1.8.7) or <tt>[]</tt> (Ruby
|
|
34
|
+
# 1.9), and calls to <tt>Array(object)</tt> otherwise.
|
|
35
|
+
#
|
|
36
|
+
# Thus, in this case the behavior may be different for +nil+, and the differences with
|
|
37
|
+
# <tt>Kernel#Array</tt> explained above apply to the rest of <tt>object</tt>s.
|
|
38
|
+
def self.wrap(object)
|
|
39
|
+
if object.nil?
|
|
40
|
+
[]
|
|
41
|
+
elsif object.respond_to?(:to_ary)
|
|
42
|
+
object.to_ary || [object]
|
|
43
|
+
else
|
|
44
|
+
[object]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'active_support/refinements/core_ext/big_decimal/conversions'
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module BigDecimalExt; end; module BigDecimalExt::Conversions
|
|
2
|
+
require 'bigdecimal'
|
|
3
|
+
require 'yaml'
|
|
4
|
+
|
|
5
|
+
refine BigDecimal do
|
|
6
|
+
YAML_MAPPING = { 'Infinity' => '.Inf', '-Infinity' => '-.Inf', 'NaN' => '.NaN' }
|
|
7
|
+
|
|
8
|
+
def encode_with(coder)
|
|
9
|
+
string = to_s
|
|
10
|
+
coder.represent_scalar(nil, YAML_MAPPING[string] || string)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Backport this method if it doesn't exist
|
|
14
|
+
unless method_defined?(:to_d)
|
|
15
|
+
def to_d
|
|
16
|
+
self
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
DEFAULT_STRING_FORMAT = 'F'
|
|
21
|
+
def to_formatted_s(*args)
|
|
22
|
+
if args[0].is_a?(Symbol)
|
|
23
|
+
super
|
|
24
|
+
else
|
|
25
|
+
format = args[0] || DEFAULT_STRING_FORMAT
|
|
26
|
+
_original_to_s(format)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
# alias_method :_original_to_s, :to_s
|
|
30
|
+
# alias_method :to_s, :to_formatted_s
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
require 'active_support/refinements/core_ext/class/attribute'
|
|
2
|
+
require 'active_support/refinements/core_ext/class/attribute_accessors'
|
|
3
|
+
require 'active_support/refinements/core_ext/class/delegating_attributes'
|
|
4
|
+
require 'active_support/refinements/core_ext/class/subclasses'
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
module ClassExt; end; module ClassExt::Attribute
|
|
2
|
+
require 'active_support/refinements/core_ext/kernel/singleton_class'
|
|
3
|
+
require 'active_support/refinements/core_ext/module/remove_method'
|
|
4
|
+
require 'active_support/refinements/core_ext/array/extract_options'
|
|
5
|
+
|
|
6
|
+
refine Class do
|
|
7
|
+
# Declare a class-level attribute whose value is inheritable by subclasses.
|
|
8
|
+
# Subclasses can change their own value and it will not impact parent class.
|
|
9
|
+
#
|
|
10
|
+
# class Base
|
|
11
|
+
# class_attribute :setting
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# class Subclass < Base
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# Base.setting = true
|
|
18
|
+
# Subclass.setting # => true
|
|
19
|
+
# Subclass.setting = false
|
|
20
|
+
# Subclass.setting # => false
|
|
21
|
+
# Base.setting # => true
|
|
22
|
+
#
|
|
23
|
+
# In the above case as long as Subclass does not assign a value to setting
|
|
24
|
+
# by performing <tt>Subclass.setting = _something_ </tt>, <tt>Subclass.setting</tt>
|
|
25
|
+
# would read value assigned to parent class. Once Subclass assigns a value then
|
|
26
|
+
# the value assigned by Subclass would be returned.
|
|
27
|
+
#
|
|
28
|
+
# This matches normal Ruby method inheritance: think of writing an attribute
|
|
29
|
+
# on a subclass as overriding the reader method. However, you need to be aware
|
|
30
|
+
# when using +class_attribute+ with mutable structures as +Array+ or +Hash+.
|
|
31
|
+
# In such cases, you don't want to do changes in places but use setters:
|
|
32
|
+
#
|
|
33
|
+
# Base.setting = []
|
|
34
|
+
# Base.setting # => []
|
|
35
|
+
# Subclass.setting # => []
|
|
36
|
+
#
|
|
37
|
+
# # Appending in child changes both parent and child because it is the same object:
|
|
38
|
+
# Subclass.setting << :foo
|
|
39
|
+
# Base.setting # => [:foo]
|
|
40
|
+
# Subclass.setting # => [:foo]
|
|
41
|
+
#
|
|
42
|
+
# # Use setters to not propagate changes:
|
|
43
|
+
# Base.setting = []
|
|
44
|
+
# Subclass.setting += [:foo]
|
|
45
|
+
# Base.setting # => []
|
|
46
|
+
# Subclass.setting # => [:foo]
|
|
47
|
+
#
|
|
48
|
+
# For convenience, a query method is defined as well:
|
|
49
|
+
#
|
|
50
|
+
# Subclass.setting? # => false
|
|
51
|
+
#
|
|
52
|
+
# Instances may overwrite the class value in the same way:
|
|
53
|
+
#
|
|
54
|
+
# Base.setting = true
|
|
55
|
+
# object = Base.new
|
|
56
|
+
# object.setting # => true
|
|
57
|
+
# object.setting = false
|
|
58
|
+
# object.setting # => false
|
|
59
|
+
# Base.setting # => true
|
|
60
|
+
#
|
|
61
|
+
# To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
|
|
62
|
+
#
|
|
63
|
+
# object.setting # => NoMethodError
|
|
64
|
+
# object.setting? # => NoMethodError
|
|
65
|
+
#
|
|
66
|
+
# To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
|
|
67
|
+
#
|
|
68
|
+
# object.setting = false # => NoMethodError
|
|
69
|
+
#
|
|
70
|
+
# To opt out of both instance methods, pass <tt>instance_accessor: false</tt>.
|
|
71
|
+
def class_attribute(*attrs)
|
|
72
|
+
options = attrs.extract_options!
|
|
73
|
+
instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
|
|
74
|
+
instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
|
|
75
|
+
|
|
76
|
+
attrs.each do |name|
|
|
77
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
78
|
+
def self.#{name}() nil end
|
|
79
|
+
def self.#{name}?() !!#{name} end
|
|
80
|
+
|
|
81
|
+
def self.#{name}=(val)
|
|
82
|
+
singleton_class.class_eval do
|
|
83
|
+
remove_possible_method(:#{name})
|
|
84
|
+
define_method(:#{name}) { val }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
if singleton_class?
|
|
88
|
+
class_eval do
|
|
89
|
+
remove_possible_method(:#{name})
|
|
90
|
+
def #{name}
|
|
91
|
+
defined?(@#{name}) ? @#{name} : singleton_class.#{name}
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
val
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
if instance_reader
|
|
99
|
+
remove_possible_method :#{name}
|
|
100
|
+
def #{name}
|
|
101
|
+
defined?(@#{name}) ? @#{name} : self.class.#{name}
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def #{name}?
|
|
105
|
+
!!#{name}
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
RUBY
|
|
109
|
+
|
|
110
|
+
attr_writer name if instance_writer
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
private
|
|
115
|
+
def singleton_class?
|
|
116
|
+
ancestors.first != self
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
module ClassExt; end; module ClassExt::AttributeAccessors
|
|
2
|
+
require 'active_support/refinements/core_ext/array/extract_options'
|
|
3
|
+
|
|
4
|
+
# Extends the class object with class and instance accessors for class attributes,
|
|
5
|
+
# just like the native attr* accessors for instance attributes.
|
|
6
|
+
refine Class do
|
|
7
|
+
# Defines a class attribute if it's not defined and creates a reader method that
|
|
8
|
+
# returns the attribute value.
|
|
9
|
+
#
|
|
10
|
+
# class Person
|
|
11
|
+
# cattr_reader :hair_colors
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# Person.class_variable_set("@@hair_colors", [:brown, :black])
|
|
15
|
+
# Person.hair_colors # => [:brown, :black]
|
|
16
|
+
# Person.new.hair_colors # => [:brown, :black]
|
|
17
|
+
#
|
|
18
|
+
# The attribute name must be a valid method name in Ruby.
|
|
19
|
+
#
|
|
20
|
+
# class Person
|
|
21
|
+
# cattr_reader :"1_Badname "
|
|
22
|
+
# end
|
|
23
|
+
# # => NameError: invalid attribute name
|
|
24
|
+
#
|
|
25
|
+
# If you want to opt out the instance reader method, you can pass <tt>instance_reader: false</tt>
|
|
26
|
+
# or <tt>instance_accessor: false</tt>.
|
|
27
|
+
#
|
|
28
|
+
# class Person
|
|
29
|
+
# cattr_reader :hair_colors, instance_reader: false
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
# Person.new.hair_colors # => NoMethodError
|
|
33
|
+
def cattr_reader(*syms)
|
|
34
|
+
options = syms.extract_options!
|
|
35
|
+
syms.each do |sym|
|
|
36
|
+
raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
|
|
37
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
|
38
|
+
unless defined? @@#{sym}
|
|
39
|
+
@@#{sym} = nil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.#{sym}
|
|
43
|
+
@@#{sym}
|
|
44
|
+
end
|
|
45
|
+
EOS
|
|
46
|
+
|
|
47
|
+
unless options[:instance_reader] == false || options[:instance_accessor] == false
|
|
48
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
|
49
|
+
def #{sym}
|
|
50
|
+
@@#{sym}
|
|
51
|
+
end
|
|
52
|
+
EOS
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Defines a class attribute if it's not defined and creates a writer method to allow
|
|
58
|
+
# assignment to the attribute.
|
|
59
|
+
#
|
|
60
|
+
# class Person
|
|
61
|
+
# cattr_writer :hair_colors
|
|
62
|
+
# end
|
|
63
|
+
#
|
|
64
|
+
# Person.hair_colors = [:brown, :black]
|
|
65
|
+
# Person.class_variable_get("@@hair_colors") # => [:brown, :black]
|
|
66
|
+
# Person.new.hair_colors = [:blonde, :red]
|
|
67
|
+
# Person.class_variable_get("@@hair_colors") # => [:blonde, :red]
|
|
68
|
+
#
|
|
69
|
+
# The attribute name must be a valid method name in Ruby.
|
|
70
|
+
#
|
|
71
|
+
# class Person
|
|
72
|
+
# cattr_writer :"1_Badname "
|
|
73
|
+
# end
|
|
74
|
+
# # => NameError: invalid attribute name
|
|
75
|
+
#
|
|
76
|
+
# If you want to opt out the instance writer method, pass <tt>instance_writer: false</tt>
|
|
77
|
+
# or <tt>instance_accessor: false</tt>.
|
|
78
|
+
#
|
|
79
|
+
# class Person
|
|
80
|
+
# cattr_writer :hair_colors, instance_writer: false
|
|
81
|
+
# end
|
|
82
|
+
#
|
|
83
|
+
# Person.new.hair_colors = [:blonde, :red] # => NoMethodError
|
|
84
|
+
#
|
|
85
|
+
# Also, you can pass a block to set up the attribute with a default value.
|
|
86
|
+
#
|
|
87
|
+
# class Person
|
|
88
|
+
# cattr_writer :hair_colors do
|
|
89
|
+
# [:brown, :black, :blonde, :red]
|
|
90
|
+
# end
|
|
91
|
+
# end
|
|
92
|
+
#
|
|
93
|
+
# Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
|
|
94
|
+
def cattr_writer(*syms)
|
|
95
|
+
options = syms.extract_options!
|
|
96
|
+
syms.each do |sym|
|
|
97
|
+
raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
|
|
98
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
|
99
|
+
unless defined? @@#{sym}
|
|
100
|
+
@@#{sym} = nil
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def self.#{sym}=(obj)
|
|
104
|
+
@@#{sym} = obj
|
|
105
|
+
end
|
|
106
|
+
EOS
|
|
107
|
+
|
|
108
|
+
unless options[:instance_writer] == false || options[:instance_accessor] == false
|
|
109
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
|
110
|
+
def #{sym}=(obj)
|
|
111
|
+
@@#{sym} = obj
|
|
112
|
+
end
|
|
113
|
+
EOS
|
|
114
|
+
end
|
|
115
|
+
send("#{sym}=", yield) if block_given?
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Defines both class and instance accessors for class attributes.
|
|
120
|
+
#
|
|
121
|
+
# class Person
|
|
122
|
+
# cattr_accessor :hair_colors
|
|
123
|
+
# end
|
|
124
|
+
#
|
|
125
|
+
# Person.hair_colors = [:brown, :black, :blonde, :red]
|
|
126
|
+
# Person.hair_colors # => [:brown, :black, :blonde, :red]
|
|
127
|
+
# Person.new.hair_colors # => [:brown, :black, :blonde, :red]
|
|
128
|
+
#
|
|
129
|
+
# If a subclass changes the value then that would also change the value for
|
|
130
|
+
# parent class. Similarly if parent class changes the value then that would
|
|
131
|
+
# change the value of subclasses too.
|
|
132
|
+
#
|
|
133
|
+
# class Male < Person
|
|
134
|
+
# end
|
|
135
|
+
#
|
|
136
|
+
# Male.hair_colors << :blue
|
|
137
|
+
# Person.hair_colors # => [:brown, :black, :blonde, :red, :blue]
|
|
138
|
+
#
|
|
139
|
+
# To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
|
|
140
|
+
# To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
|
|
141
|
+
#
|
|
142
|
+
# class Person
|
|
143
|
+
# cattr_accessor :hair_colors, instance_writer: false, instance_reader: false
|
|
144
|
+
# end
|
|
145
|
+
#
|
|
146
|
+
# Person.new.hair_colors = [:brown] # => NoMethodError
|
|
147
|
+
# Person.new.hair_colors # => NoMethodError
|
|
148
|
+
#
|
|
149
|
+
# Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
|
|
150
|
+
#
|
|
151
|
+
# class Person
|
|
152
|
+
# cattr_accessor :hair_colors, instance_accessor: false
|
|
153
|
+
# end
|
|
154
|
+
#
|
|
155
|
+
# Person.new.hair_colors = [:brown] # => NoMethodError
|
|
156
|
+
# Person.new.hair_colors # => NoMethodError
|
|
157
|
+
#
|
|
158
|
+
# Also you can pass a block to set up the attribute with a default value.
|
|
159
|
+
#
|
|
160
|
+
# class Person
|
|
161
|
+
# cattr_accessor :hair_colors do
|
|
162
|
+
# [:brown, :black, :blonde, :red]
|
|
163
|
+
# end
|
|
164
|
+
# end
|
|
165
|
+
#
|
|
166
|
+
# Person.class_variable_get("@@hair_colors") #=> [:brown, :black, :blonde, :red]
|
|
167
|
+
def cattr_accessor(*syms, &blk)
|
|
168
|
+
cattr_reader(*syms)
|
|
169
|
+
cattr_writer(*syms, &blk)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module ClassExt; end; module ClassExt::DelegatingAttributes
|
|
2
|
+
require 'active_support/refinements/core_ext/kernel/singleton_class'
|
|
3
|
+
require 'active_support/refinements/core_ext/module/remove_method'
|
|
4
|
+
|
|
5
|
+
refine Class do
|
|
6
|
+
def superclass_delegating_accessor(name, options = {})
|
|
7
|
+
# Create private _name and _name= methods that can still be used if the public
|
|
8
|
+
# methods are overridden. This allows
|
|
9
|
+
_superclass_delegating_accessor("_#{name}")
|
|
10
|
+
|
|
11
|
+
# Generate the public methods name, name=, and name?
|
|
12
|
+
# These methods dispatch to the private _name, and _name= methods, making them
|
|
13
|
+
# overridable
|
|
14
|
+
singleton_class.send(:define_method, name) { send("_#{name}") }
|
|
15
|
+
singleton_class.send(:define_method, "#{name}?") { !!send("_#{name}") }
|
|
16
|
+
singleton_class.send(:define_method, "#{name}=") { |value| send("_#{name}=", value) }
|
|
17
|
+
|
|
18
|
+
# If an instance_reader is needed, generate methods for name and name= on the
|
|
19
|
+
# class itself, so instances will be able to see them
|
|
20
|
+
define_method(name) { send("_#{name}") } if options[:instance_reader] != false
|
|
21
|
+
define_method("#{name}?") { !!send("#{name}") } if options[:instance_reader] != false
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
# Take the object being set and store it in a method. This gives us automatic
|
|
26
|
+
# inheritance behavior, without having to store the object in an instance
|
|
27
|
+
# variable and look up the superclass chain manually.
|
|
28
|
+
def _stash_object_in_method(object, method, instance_reader = true)
|
|
29
|
+
singleton_class.remove_possible_method(method)
|
|
30
|
+
singleton_class.send(:define_method, method) { object }
|
|
31
|
+
remove_possible_method(method)
|
|
32
|
+
define_method(method) { object } if instance_reader
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def _superclass_delegating_accessor(name, options = {})
|
|
36
|
+
singleton_class.send(:define_method, "#{name}=") do |value|
|
|
37
|
+
_stash_object_in_method(value, name, options[:instance_reader] != false)
|
|
38
|
+
end
|
|
39
|
+
send("#{name}=", nil)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|