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.
Files changed (120) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +6 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +32 -0
  5. data/Rakefile +1 -0
  6. data/activesupport-refinements.gemspec +21 -0
  7. data/lib/active_support/refinements/core_ext/array.rb +7 -0
  8. data/lib/active_support/refinements/core_ext/array/access.rb +56 -0
  9. data/lib/active_support/refinements/core_ext/array/conversions.rb +224 -0
  10. data/lib/active_support/refinements/core_ext/array/extract_options.rb +31 -0
  11. data/lib/active_support/refinements/core_ext/array/grouping.rb +101 -0
  12. data/lib/active_support/refinements/core_ext/array/prepend_and_append.rb +9 -0
  13. data/lib/active_support/refinements/core_ext/array/uniq_by.rb +21 -0
  14. data/lib/active_support/refinements/core_ext/array/wrap.rb +48 -0
  15. data/lib/active_support/refinements/core_ext/benchmark.rb +7 -0
  16. data/lib/active_support/refinements/core_ext/big_decimal.rb +1 -0
  17. data/lib/active_support/refinements/core_ext/big_decimal/conversions.rb +32 -0
  18. data/lib/active_support/refinements/core_ext/class.rb +4 -0
  19. data/lib/active_support/refinements/core_ext/class/attribute.rb +119 -0
  20. data/lib/active_support/refinements/core_ext/class/attribute_accessors.rb +172 -0
  21. data/lib/active_support/refinements/core_ext/class/delegating_attributes.rb +42 -0
  22. data/lib/active_support/refinements/core_ext/class/subclasses.rb +44 -0
  23. data/lib/active_support/refinements/core_ext/date.rb +5 -0
  24. data/lib/active_support/refinements/core_ext/date/acts_like.rb +10 -0
  25. data/lib/active_support/refinements/core_ext/date/calculations.rb +123 -0
  26. data/lib/active_support/refinements/core_ext/date/conversions.rb +86 -0
  27. data/lib/active_support/refinements/core_ext/date/zones.rb +17 -0
  28. data/lib/active_support/refinements/core_ext/date_and_time/calculations.rb +232 -0
  29. data/lib/active_support/refinements/core_ext/date_time.rb +4 -0
  30. data/lib/active_support/refinements/core_ext/date_time/acts_like.rb +15 -0
  31. data/lib/active_support/refinements/core_ext/date_time/calculations.rb +143 -0
  32. data/lib/active_support/refinements/core_ext/date_time/conversions.rb +93 -0
  33. data/lib/active_support/refinements/core_ext/date_time/zones.rb +26 -0
  34. data/lib/active_support/refinements/core_ext/enumerable.rb +82 -0
  35. data/lib/active_support/refinements/core_ext/exception.rb +5 -0
  36. data/lib/active_support/refinements/core_ext/file.rb +1 -0
  37. data/lib/active_support/refinements/core_ext/file/atomic.rb +60 -0
  38. data/lib/active_support/refinements/core_ext/hash.rb +8 -0
  39. data/lib/active_support/refinements/core_ext/hash/conversions.rb +161 -0
  40. data/lib/active_support/refinements/core_ext/hash/deep_merge.rb +29 -0
  41. data/lib/active_support/refinements/core_ext/hash/diff.rb +15 -0
  42. data/lib/active_support/refinements/core_ext/hash/except.rb +17 -0
  43. data/lib/active_support/refinements/core_ext/hash/indifferent_access.rb +24 -0
  44. data/lib/active_support/refinements/core_ext/hash/keys.rb +140 -0
  45. data/lib/active_support/refinements/core_ext/hash/reverse_merge.rb +24 -0
  46. data/lib/active_support/refinements/core_ext/hash/slice.rb +42 -0
  47. data/lib/active_support/refinements/core_ext/integer.rb +3 -0
  48. data/lib/active_support/refinements/core_ext/integer/inflections.rb +31 -0
  49. data/lib/active_support/refinements/core_ext/integer/multiple.rb +12 -0
  50. data/lib/active_support/refinements/core_ext/integer/time.rb +43 -0
  51. data/lib/active_support/refinements/core_ext/kernel.rb +4 -0
  52. data/lib/active_support/refinements/core_ext/kernel/agnostics.rb +13 -0
  53. data/lib/active_support/refinements/core_ext/kernel/debugger.rb +12 -0
  54. data/lib/active_support/refinements/core_ext/kernel/reporting.rb +97 -0
  55. data/lib/active_support/refinements/core_ext/kernel/singleton_class.rb +8 -0
  56. data/lib/active_support/refinements/core_ext/load_error.rb +27 -0
  57. data/lib/active_support/refinements/core_ext/logger.rb +86 -0
  58. data/lib/active_support/refinements/core_ext/module.rb +10 -0
  59. data/lib/active_support/refinements/core_ext/module/aliasing.rb +69 -0
  60. data/lib/active_support/refinements/core_ext/module/anonymous.rb +21 -0
  61. data/lib/active_support/refinements/core_ext/module/attr_internal.rb +40 -0
  62. data/lib/active_support/refinements/core_ext/module/attribute_accessors.rb +68 -0
  63. data/lib/active_support/refinements/core_ext/module/delegation.rb +172 -0
  64. data/lib/active_support/refinements/core_ext/module/deprecation.rb +27 -0
  65. data/lib/active_support/refinements/core_ext/module/introspection.rb +80 -0
  66. data/lib/active_support/refinements/core_ext/module/qualified_const.rb +54 -0
  67. data/lib/active_support/refinements/core_ext/module/reachable.rb +10 -0
  68. data/lib/active_support/refinements/core_ext/module/remove_method.rb +14 -0
  69. data/lib/active_support/refinements/core_ext/name_error.rb +20 -0
  70. data/lib/active_support/refinements/core_ext/numeric.rb +3 -0
  71. data/lib/active_support/refinements/core_ext/numeric/bytes.rb +46 -0
  72. data/lib/active_support/refinements/core_ext/numeric/conversions.rb +137 -0
  73. data/lib/active_support/refinements/core_ext/numeric/time.rb +81 -0
  74. data/lib/active_support/refinements/core_ext/object.rb +14 -0
  75. data/lib/active_support/refinements/core_ext/object/acts_like.rb +12 -0
  76. data/lib/active_support/refinements/core_ext/object/blank.rb +107 -0
  77. data/lib/active_support/refinements/core_ext/object/conversions.rb +4 -0
  78. data/lib/active_support/refinements/core_ext/object/deep_dup.rb +48 -0
  79. data/lib/active_support/refinements/core_ext/object/duplicable.rb +92 -0
  80. data/lib/active_support/refinements/core_ext/object/inclusion.rb +27 -0
  81. data/lib/active_support/refinements/core_ext/object/instance_variables.rb +30 -0
  82. data/lib/active_support/refinements/core_ext/object/to_json.rb +27 -0
  83. data/lib/active_support/refinements/core_ext/object/to_param.rb +60 -0
  84. data/lib/active_support/refinements/core_ext/object/to_query.rb +29 -0
  85. data/lib/active_support/refinements/core_ext/object/try.rb +72 -0
  86. data/lib/active_support/refinements/core_ext/object/with_options.rb +44 -0
  87. data/lib/active_support/refinements/core_ext/proc.rb +19 -0
  88. data/lib/active_support/refinements/core_ext/range.rb +3 -0
  89. data/lib/active_support/refinements/core_ext/range/conversions.rb +21 -0
  90. data/lib/active_support/refinements/core_ext/range/include_range.rb +23 -0
  91. data/lib/active_support/refinements/core_ext/range/overlaps.rb +10 -0
  92. data/lib/active_support/refinements/core_ext/regexp.rb +7 -0
  93. data/lib/active_support/refinements/core_ext/string.rb +13 -0
  94. data/lib/active_support/refinements/core_ext/string/access.rb +106 -0
  95. data/lib/active_support/refinements/core_ext/string/behavior.rb +8 -0
  96. data/lib/active_support/refinements/core_ext/string/conversions.rb +60 -0
  97. data/lib/active_support/refinements/core_ext/string/encoding.rb +10 -0
  98. data/lib/active_support/refinements/core_ext/string/exclude.rb +13 -0
  99. data/lib/active_support/refinements/core_ext/string/filters.rb +54 -0
  100. data/lib/active_support/refinements/core_ext/string/indent.rb +45 -0
  101. data/lib/active_support/refinements/core_ext/string/inflections.rb +214 -0
  102. data/lib/active_support/refinements/core_ext/string/inquiry.rb +15 -0
  103. data/lib/active_support/refinements/core_ext/string/multibyte.rb +58 -0
  104. data/lib/active_support/refinements/core_ext/string/output_safety.rb +194 -0
  105. data/lib/active_support/refinements/core_ext/string/starts_ends_with.rb +6 -0
  106. data/lib/active_support/refinements/core_ext/string/strip.rb +28 -0
  107. data/lib/active_support/refinements/core_ext/string/xchar.rb +18 -0
  108. data/lib/active_support/refinements/core_ext/time.rb +5 -0
  109. data/lib/active_support/refinements/core_ext/time/acts_like.rb +10 -0
  110. data/lib/active_support/refinements/core_ext/time/calculations.rb +251 -0
  111. data/lib/active_support/refinements/core_ext/time/conversions.rb +65 -0
  112. data/lib/active_support/refinements/core_ext/time/marshal.rb +30 -0
  113. data/lib/active_support/refinements/core_ext/time/zones.rb +98 -0
  114. data/lib/active_support/refinements/core_ext/uri.rb +28 -0
  115. data/lib/activesupport-refinements.rb +9 -0
  116. data/lib/activesupport-refinements/version.rb +5 -0
  117. data/refine_core_ext.rb +45 -0
  118. data/spec/hwia_spec.rb +15 -0
  119. data/spec/try_spec.rb +18 -0
  120. 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,7 @@
1
+ require 'benchmark'
2
+
3
+ class << Benchmark
4
+ def ms
5
+ 1000 * realtime { yield }
6
+ end
7
+ 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