core_ext 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (175) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +3 -0
  3. data/lib/core_ext/array/access.rb +76 -0
  4. data/lib/core_ext/array/conversions.rb +211 -0
  5. data/lib/core_ext/array/extract_options.rb +29 -0
  6. data/lib/core_ext/array/grouping.rb +116 -0
  7. data/lib/core_ext/array/inquiry.rb +17 -0
  8. data/lib/core_ext/array/prepend_and_append.rb +7 -0
  9. data/lib/core_ext/array/wrap.rb +46 -0
  10. data/lib/core_ext/array.rb +7 -0
  11. data/lib/core_ext/array_inquirer.rb +44 -0
  12. data/lib/core_ext/benchmark.rb +14 -0
  13. data/lib/core_ext/benchmarkable.rb +49 -0
  14. data/lib/core_ext/big_decimal/conversions.rb +14 -0
  15. data/lib/core_ext/big_decimal.rb +1 -0
  16. data/lib/core_ext/builder.rb +6 -0
  17. data/lib/core_ext/callbacks.rb +770 -0
  18. data/lib/core_ext/class/attribute.rb +128 -0
  19. data/lib/core_ext/class/attribute_accessors.rb +4 -0
  20. data/lib/core_ext/class/subclasses.rb +42 -0
  21. data/lib/core_ext/class.rb +2 -0
  22. data/lib/core_ext/concern.rb +142 -0
  23. data/lib/core_ext/configurable.rb +148 -0
  24. data/lib/core_ext/date/acts_like.rb +8 -0
  25. data/lib/core_ext/date/blank.rb +12 -0
  26. data/lib/core_ext/date/calculations.rb +143 -0
  27. data/lib/core_ext/date/conversions.rb +93 -0
  28. data/lib/core_ext/date/zones.rb +6 -0
  29. data/lib/core_ext/date.rb +5 -0
  30. data/lib/core_ext/date_and_time/calculations.rb +328 -0
  31. data/lib/core_ext/date_and_time/zones.rb +40 -0
  32. data/lib/core_ext/date_time/acts_like.rb +14 -0
  33. data/lib/core_ext/date_time/blank.rb +12 -0
  34. data/lib/core_ext/date_time/calculations.rb +177 -0
  35. data/lib/core_ext/date_time/conversions.rb +104 -0
  36. data/lib/core_ext/date_time/zones.rb +6 -0
  37. data/lib/core_ext/date_time.rb +5 -0
  38. data/lib/core_ext/deprecation/behaviors.rb +86 -0
  39. data/lib/core_ext/deprecation/instance_delegator.rb +24 -0
  40. data/lib/core_ext/deprecation/method_wrappers.rb +70 -0
  41. data/lib/core_ext/deprecation/proxy_wrappers.rb +149 -0
  42. data/lib/core_ext/deprecation/reporting.rb +105 -0
  43. data/lib/core_ext/deprecation.rb +43 -0
  44. data/lib/core_ext/digest/uuid.rb +51 -0
  45. data/lib/core_ext/duration.rb +157 -0
  46. data/lib/core_ext/enumerable.rb +106 -0
  47. data/lib/core_ext/file/atomic.rb +68 -0
  48. data/lib/core_ext/file.rb +1 -0
  49. data/lib/core_ext/hash/compact.rb +20 -0
  50. data/lib/core_ext/hash/conversions.rb +261 -0
  51. data/lib/core_ext/hash/deep_merge.rb +38 -0
  52. data/lib/core_ext/hash/except.rb +22 -0
  53. data/lib/core_ext/hash/indifferent_access.rb +23 -0
  54. data/lib/core_ext/hash/keys.rb +170 -0
  55. data/lib/core_ext/hash/reverse_merge.rb +22 -0
  56. data/lib/core_ext/hash/slice.rb +48 -0
  57. data/lib/core_ext/hash/transform_values.rb +29 -0
  58. data/lib/core_ext/hash.rb +9 -0
  59. data/lib/core_ext/hash_with_indifferent_access.rb +298 -0
  60. data/lib/core_ext/inflections.rb +70 -0
  61. data/lib/core_ext/inflector/inflections.rb +244 -0
  62. data/lib/core_ext/inflector/methods.rb +381 -0
  63. data/lib/core_ext/inflector/transliterate.rb +112 -0
  64. data/lib/core_ext/inflector.rb +7 -0
  65. data/lib/core_ext/integer/inflections.rb +29 -0
  66. data/lib/core_ext/integer/multiple.rb +10 -0
  67. data/lib/core_ext/integer/time.rb +29 -0
  68. data/lib/core_ext/integer.rb +3 -0
  69. data/lib/core_ext/json/decoding.rb +67 -0
  70. data/lib/core_ext/json/encoding.rb +127 -0
  71. data/lib/core_ext/json.rb +2 -0
  72. data/lib/core_ext/kernel/agnostics.rb +11 -0
  73. data/lib/core_ext/kernel/concern.rb +10 -0
  74. data/lib/core_ext/kernel/reporting.rb +41 -0
  75. data/lib/core_ext/kernel/singleton_class.rb +6 -0
  76. data/lib/core_ext/kernel.rb +4 -0
  77. data/lib/core_ext/load_error.rb +30 -0
  78. data/lib/core_ext/logger.rb +57 -0
  79. data/lib/core_ext/logger_silence.rb +24 -0
  80. data/lib/core_ext/marshal.rb +19 -0
  81. data/lib/core_ext/module/aliasing.rb +74 -0
  82. data/lib/core_ext/module/anonymous.rb +28 -0
  83. data/lib/core_ext/module/attr_internal.rb +36 -0
  84. data/lib/core_ext/module/attribute_accessors.rb +212 -0
  85. data/lib/core_ext/module/concerning.rb +135 -0
  86. data/lib/core_ext/module/delegation.rb +218 -0
  87. data/lib/core_ext/module/deprecation.rb +23 -0
  88. data/lib/core_ext/module/introspection.rb +62 -0
  89. data/lib/core_ext/module/method_transplanting.rb +3 -0
  90. data/lib/core_ext/module/qualified_const.rb +52 -0
  91. data/lib/core_ext/module/reachable.rb +8 -0
  92. data/lib/core_ext/module/remove_method.rb +35 -0
  93. data/lib/core_ext/module.rb +11 -0
  94. data/lib/core_ext/multibyte/chars.rb +231 -0
  95. data/lib/core_ext/multibyte/unicode.rb +388 -0
  96. data/lib/core_ext/multibyte.rb +21 -0
  97. data/lib/core_ext/name_error.rb +31 -0
  98. data/lib/core_ext/numeric/bytes.rb +64 -0
  99. data/lib/core_ext/numeric/conversions.rb +132 -0
  100. data/lib/core_ext/numeric/inquiry.rb +26 -0
  101. data/lib/core_ext/numeric/time.rb +74 -0
  102. data/lib/core_ext/numeric.rb +4 -0
  103. data/lib/core_ext/object/acts_like.rb +10 -0
  104. data/lib/core_ext/object/blank.rb +140 -0
  105. data/lib/core_ext/object/conversions.rb +4 -0
  106. data/lib/core_ext/object/deep_dup.rb +53 -0
  107. data/lib/core_ext/object/duplicable.rb +98 -0
  108. data/lib/core_ext/object/inclusion.rb +27 -0
  109. data/lib/core_ext/object/instance_variables.rb +28 -0
  110. data/lib/core_ext/object/json.rb +199 -0
  111. data/lib/core_ext/object/to_param.rb +1 -0
  112. data/lib/core_ext/object/to_query.rb +84 -0
  113. data/lib/core_ext/object/try.rb +146 -0
  114. data/lib/core_ext/object/with_options.rb +69 -0
  115. data/lib/core_ext/object.rb +14 -0
  116. data/lib/core_ext/option_merger.rb +25 -0
  117. data/lib/core_ext/ordered_hash.rb +48 -0
  118. data/lib/core_ext/ordered_options.rb +81 -0
  119. data/lib/core_ext/range/conversions.rb +34 -0
  120. data/lib/core_ext/range/each.rb +21 -0
  121. data/lib/core_ext/range/include_range.rb +23 -0
  122. data/lib/core_ext/range/overlaps.rb +8 -0
  123. data/lib/core_ext/range.rb +4 -0
  124. data/lib/core_ext/regexp.rb +5 -0
  125. data/lib/core_ext/rescuable.rb +119 -0
  126. data/lib/core_ext/securerandom.rb +23 -0
  127. data/lib/core_ext/security_utils.rb +20 -0
  128. data/lib/core_ext/string/access.rb +104 -0
  129. data/lib/core_ext/string/behavior.rb +6 -0
  130. data/lib/core_ext/string/conversions.rb +56 -0
  131. data/lib/core_ext/string/exclude.rb +11 -0
  132. data/lib/core_ext/string/filters.rb +102 -0
  133. data/lib/core_ext/string/indent.rb +43 -0
  134. data/lib/core_ext/string/inflections.rb +235 -0
  135. data/lib/core_ext/string/inquiry.rb +13 -0
  136. data/lib/core_ext/string/multibyte.rb +53 -0
  137. data/lib/core_ext/string/output_safety.rb +261 -0
  138. data/lib/core_ext/string/starts_ends_with.rb +4 -0
  139. data/lib/core_ext/string/strip.rb +23 -0
  140. data/lib/core_ext/string/zones.rb +14 -0
  141. data/lib/core_ext/string.rb +13 -0
  142. data/lib/core_ext/string_inquirer.rb +26 -0
  143. data/lib/core_ext/tagged_logging.rb +78 -0
  144. data/lib/core_ext/test_case.rb +88 -0
  145. data/lib/core_ext/testing/assertions.rb +99 -0
  146. data/lib/core_ext/testing/autorun.rb +12 -0
  147. data/lib/core_ext/testing/composite_filter.rb +54 -0
  148. data/lib/core_ext/testing/constant_lookup.rb +50 -0
  149. data/lib/core_ext/testing/declarative.rb +26 -0
  150. data/lib/core_ext/testing/deprecation.rb +36 -0
  151. data/lib/core_ext/testing/file_fixtures.rb +34 -0
  152. data/lib/core_ext/testing/isolation.rb +115 -0
  153. data/lib/core_ext/testing/method_call_assertions.rb +41 -0
  154. data/lib/core_ext/testing/setup_and_teardown.rb +50 -0
  155. data/lib/core_ext/testing/stream.rb +42 -0
  156. data/lib/core_ext/testing/tagged_logging.rb +25 -0
  157. data/lib/core_ext/testing/time_helpers.rb +134 -0
  158. data/lib/core_ext/time/acts_like.rb +8 -0
  159. data/lib/core_ext/time/calculations.rb +284 -0
  160. data/lib/core_ext/time/conversions.rb +66 -0
  161. data/lib/core_ext/time/zones.rb +95 -0
  162. data/lib/core_ext/time.rb +20 -0
  163. data/lib/core_ext/time_with_zone.rb +503 -0
  164. data/lib/core_ext/time_zone.rb +464 -0
  165. data/lib/core_ext/uri.rb +25 -0
  166. data/lib/core_ext/version.rb +3 -0
  167. data/lib/core_ext/xml_mini/jdom.rb +181 -0
  168. data/lib/core_ext/xml_mini/libxml.rb +79 -0
  169. data/lib/core_ext/xml_mini/libxmlsax.rb +85 -0
  170. data/lib/core_ext/xml_mini/nokogiri.rb +83 -0
  171. data/lib/core_ext/xml_mini/nokogirisax.rb +87 -0
  172. data/lib/core_ext/xml_mini/rexml.rb +130 -0
  173. data/lib/core_ext/xml_mini.rb +194 -0
  174. data/lib/core_ext.rb +3 -0
  175. metadata +310 -0
@@ -0,0 +1,128 @@
1
+ require 'core_ext/kernel/singleton_class'
2
+ require 'core_ext/module/remove_method'
3
+ require 'core_ext/array/extract_options'
4
+
5
+ class Class
6
+ # Declare a class-level attribute whose value is inheritable by subclasses.
7
+ # Subclasses can change their own value and it will not impact parent class.
8
+ #
9
+ # class Base
10
+ # class_attribute :setting
11
+ # end
12
+ #
13
+ # class Subclass < Base
14
+ # end
15
+ #
16
+ # Base.setting = true
17
+ # Subclass.setting # => true
18
+ # Subclass.setting = false
19
+ # Subclass.setting # => false
20
+ # Base.setting # => true
21
+ #
22
+ # In the above case as long as Subclass does not assign a value to setting
23
+ # by performing <tt>Subclass.setting = _something_ </tt>, <tt>Subclass.setting</tt>
24
+ # would read value assigned to parent class. Once Subclass assigns a value then
25
+ # the value assigned by Subclass would be returned.
26
+ #
27
+ # This matches normal Ruby method inheritance: think of writing an attribute
28
+ # on a subclass as overriding the reader method. However, you need to be aware
29
+ # when using +class_attribute+ with mutable structures as +Array+ or +Hash+.
30
+ # In such cases, you don't want to do changes in places but use setters:
31
+ #
32
+ # Base.setting = []
33
+ # Base.setting # => []
34
+ # Subclass.setting # => []
35
+ #
36
+ # # Appending in child changes both parent and child because it is the same object:
37
+ # Subclass.setting << :foo
38
+ # Base.setting # => [:foo]
39
+ # Subclass.setting # => [:foo]
40
+ #
41
+ # # Use setters to not propagate changes:
42
+ # Base.setting = []
43
+ # Subclass.setting += [:foo]
44
+ # Base.setting # => []
45
+ # Subclass.setting # => [:foo]
46
+ #
47
+ # For convenience, an instance predicate method is defined as well.
48
+ # To skip it, pass <tt>instance_predicate: false</tt>.
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
+ instance_predicate = options.fetch(:instance_predicate, true)
76
+
77
+ attrs.each do |name|
78
+ remove_possible_singleton_method(name)
79
+ define_singleton_method(name) { nil }
80
+
81
+ remove_possible_singleton_method("#{name}?")
82
+ define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
83
+
84
+ ivar = "@#{name}"
85
+
86
+ remove_possible_singleton_method("#{name}=")
87
+ define_singleton_method("#{name}=") do |val|
88
+ singleton_class.class_eval do
89
+ remove_possible_method(name)
90
+ define_method(name) { val }
91
+ end
92
+
93
+ if singleton_class?
94
+ class_eval do
95
+ remove_possible_method(name)
96
+ define_method(name) do
97
+ if instance_variable_defined? ivar
98
+ instance_variable_get ivar
99
+ else
100
+ singleton_class.send name
101
+ end
102
+ end
103
+ end
104
+ end
105
+ val
106
+ end
107
+
108
+ if instance_reader
109
+ remove_possible_method name
110
+ define_method(name) do
111
+ if instance_variable_defined?(ivar)
112
+ instance_variable_get ivar
113
+ else
114
+ self.class.public_send name
115
+ end
116
+ end
117
+
118
+ remove_possible_method "#{name}?"
119
+ define_method("#{name}?") { !!public_send(name) } if instance_predicate
120
+ end
121
+
122
+ if instance_writer
123
+ remove_possible_method "#{name}="
124
+ attr_writer name
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,4 @@
1
+ # cattr_* became mattr_* aliases in 7dfbd91b0780fbd6a1dd9bfbc176e10894871d2d,
2
+ # but we keep this around for libraries that directly require it knowing they
3
+ # want cattr_*. No need to deprecate.
4
+ require 'core_ext/module/attribute_accessors'
@@ -0,0 +1,42 @@
1
+ require 'core_ext/module/anonymous'
2
+ require 'core_ext/module/reachable'
3
+
4
+ class Class
5
+ begin
6
+ ObjectSpace.each_object(Class.new) {}
7
+
8
+ def descendants # :nodoc:
9
+ descendants = []
10
+ ObjectSpace.each_object(singleton_class) do |k|
11
+ descendants.unshift k unless k == self
12
+ end
13
+ descendants
14
+ end
15
+ rescue StandardError # JRuby
16
+ def descendants # :nodoc:
17
+ descendants = []
18
+ ObjectSpace.each_object(Class) do |k|
19
+ descendants.unshift k if k < self
20
+ end
21
+ descendants.uniq!
22
+ descendants
23
+ end
24
+ end
25
+
26
+ # Returns an array with the direct children of +self+.
27
+ #
28
+ # Integer.subclasses # => [Fixnum, Bignum]
29
+ #
30
+ # class Foo; end
31
+ # class Bar < Foo; end
32
+ # class Baz < Bar; end
33
+ #
34
+ # Foo.subclasses # => [Bar]
35
+ def subclasses
36
+ subclasses, chain = [], descendants
37
+ chain.each do |k|
38
+ subclasses << k unless chain.any? { |c| c > k }
39
+ end
40
+ subclasses
41
+ end
42
+ end
@@ -0,0 +1,2 @@
1
+ require 'core_ext/class/attribute'
2
+ require 'core_ext/class/subclasses'
@@ -0,0 +1,142 @@
1
+ module CoreExt
2
+ # A typical module looks like this:
3
+ #
4
+ # module M
5
+ # def self.included(base)
6
+ # base.extend ClassMethods
7
+ # base.class_eval do
8
+ # scope :disabled, -> { where(disabled: true) }
9
+ # end
10
+ # end
11
+ #
12
+ # module ClassMethods
13
+ # ...
14
+ # end
15
+ # end
16
+ #
17
+ # By using <tt>CoreExt::Concern</tt> the above module could instead be
18
+ # written as:
19
+ #
20
+ # require 'core_ext/concern'
21
+ #
22
+ # module M
23
+ # extend CoreExt::Concern
24
+ #
25
+ # included do
26
+ # scope :disabled, -> { where(disabled: true) }
27
+ # end
28
+ #
29
+ # class_methods do
30
+ # ...
31
+ # end
32
+ # end
33
+ #
34
+ # Moreover, it gracefully handles module dependencies. Given a +Foo+ module
35
+ # and a +Bar+ module which depends on the former, we would typically write the
36
+ # following:
37
+ #
38
+ # module Foo
39
+ # def self.included(base)
40
+ # base.class_eval do
41
+ # def self.method_injected_by_foo
42
+ # ...
43
+ # end
44
+ # end
45
+ # end
46
+ # end
47
+ #
48
+ # module Bar
49
+ # def self.included(base)
50
+ # base.method_injected_by_foo
51
+ # end
52
+ # end
53
+ #
54
+ # class Host
55
+ # include Foo # We need to include this dependency for Bar
56
+ # include Bar # Bar is the module that Host really needs
57
+ # end
58
+ #
59
+ # But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We
60
+ # could try to hide these from +Host+ directly including +Foo+ in +Bar+:
61
+ #
62
+ # module Bar
63
+ # include Foo
64
+ # def self.included(base)
65
+ # base.method_injected_by_foo
66
+ # end
67
+ # end
68
+ #
69
+ # class Host
70
+ # include Bar
71
+ # end
72
+ #
73
+ # Unfortunately this won't work, since when +Foo+ is included, its <tt>base</tt>
74
+ # is the +Bar+ module, not the +Host+ class. With <tt>CoreExt::Concern</tt>,
75
+ # module dependencies are properly resolved:
76
+ #
77
+ # require 'core_ext/concern'
78
+ #
79
+ # module Foo
80
+ # extend CoreExt::Concern
81
+ # included do
82
+ # def self.method_injected_by_foo
83
+ # ...
84
+ # end
85
+ # end
86
+ # end
87
+ #
88
+ # module Bar
89
+ # extend CoreExt::Concern
90
+ # include Foo
91
+ #
92
+ # included do
93
+ # self.method_injected_by_foo
94
+ # end
95
+ # end
96
+ #
97
+ # class Host
98
+ # include Bar # It works, now Bar takes care of its dependencies
99
+ # end
100
+ module Concern
101
+ class MultipleIncludedBlocks < StandardError #:nodoc:
102
+ def initialize
103
+ super "Cannot define multiple 'included' blocks for a Concern"
104
+ end
105
+ end
106
+
107
+ def self.extended(base) #:nodoc:
108
+ base.instance_variable_set(:@_dependencies, [])
109
+ end
110
+
111
+ def append_features(base)
112
+ if base.instance_variable_defined?(:@_dependencies)
113
+ base.instance_variable_get(:@_dependencies) << self
114
+ return false
115
+ else
116
+ return false if base < self
117
+ @_dependencies.each { |dep| base.include(dep) }
118
+ super
119
+ base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
120
+ base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
121
+ end
122
+ end
123
+
124
+ def included(base = nil, &block)
125
+ if base.nil?
126
+ raise MultipleIncludedBlocks if instance_variable_defined?(:@_included_block)
127
+
128
+ @_included_block = block
129
+ else
130
+ super
131
+ end
132
+ end
133
+
134
+ def class_methods(&class_methods_module_definition)
135
+ mod = const_defined?(:ClassMethods, false) ?
136
+ const_get(:ClassMethods) :
137
+ const_set(:ClassMethods, Module.new)
138
+
139
+ mod.module_eval(&class_methods_module_definition)
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,148 @@
1
+ require 'core_ext/concern'
2
+ require 'core_ext/ordered_options'
3
+ require 'core_ext/array/extract_options'
4
+
5
+ module CoreExt
6
+ # Configurable provides a <tt>config</tt> method to store and retrieve
7
+ # configuration options as an <tt>OrderedHash</tt>.
8
+ module Configurable
9
+ extend CoreExt::Concern
10
+
11
+ class Configuration < CoreExt::InheritableOptions
12
+ def compile_methods!
13
+ self.class.compile_methods!(keys)
14
+ end
15
+
16
+ # Compiles reader methods so we don't have to go through method_missing.
17
+ def self.compile_methods!(keys)
18
+ keys.reject { |m| method_defined?(m) }.each do |key|
19
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
20
+ def #{key}; _get(#{key.inspect}); end
21
+ RUBY
22
+ end
23
+ end
24
+ end
25
+
26
+ module ClassMethods
27
+ def config
28
+ @_config ||= if respond_to?(:superclass) && superclass.respond_to?(:config)
29
+ superclass.config.inheritable_copy
30
+ else
31
+ # create a new "anonymous" class that will host the compiled reader methods
32
+ Class.new(Configuration).new
33
+ end
34
+ end
35
+
36
+ def configure
37
+ yield config
38
+ end
39
+
40
+ # Allows you to add shortcut so that you don't have to refer to attribute
41
+ # through config. Also look at the example for config to contrast.
42
+ #
43
+ # Defines both class and instance config accessors.
44
+ #
45
+ # class User
46
+ # include CoreExt::Configurable
47
+ # config_accessor :allowed_access
48
+ # end
49
+ #
50
+ # User.allowed_access # => nil
51
+ # User.allowed_access = false
52
+ # User.allowed_access # => false
53
+ #
54
+ # user = User.new
55
+ # user.allowed_access # => false
56
+ # user.allowed_access = true
57
+ # user.allowed_access # => true
58
+ #
59
+ # User.allowed_access # => false
60
+ #
61
+ # The attribute name must be a valid method name in Ruby.
62
+ #
63
+ # class User
64
+ # include CoreExt::Configurable
65
+ # config_accessor :"1_Badname"
66
+ # end
67
+ # # => NameError: invalid config attribute name
68
+ #
69
+ # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
70
+ # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
71
+ #
72
+ # class User
73
+ # include CoreExt::Configurable
74
+ # config_accessor :allowed_access, instance_reader: false, instance_writer: false
75
+ # end
76
+ #
77
+ # User.allowed_access = false
78
+ # User.allowed_access # => false
79
+ #
80
+ # User.new.allowed_access = true # => NoMethodError
81
+ # User.new.allowed_access # => NoMethodError
82
+ #
83
+ # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
84
+ #
85
+ # class User
86
+ # include CoreExt::Configurable
87
+ # config_accessor :allowed_access, instance_accessor: false
88
+ # end
89
+ #
90
+ # User.allowed_access = false
91
+ # User.allowed_access # => false
92
+ #
93
+ # User.new.allowed_access = true # => NoMethodError
94
+ # User.new.allowed_access # => NoMethodError
95
+ #
96
+ # Also you can pass a block to set up the attribute with a default value.
97
+ #
98
+ # class User
99
+ # include CoreExt::Configurable
100
+ # config_accessor :hair_colors do
101
+ # [:brown, :black, :blonde, :red]
102
+ # end
103
+ # end
104
+ #
105
+ # User.hair_colors # => [:brown, :black, :blonde, :red]
106
+ def config_accessor(*names)
107
+ options = names.extract_options!
108
+
109
+ names.each do |name|
110
+ raise NameError.new('invalid config attribute name') unless name =~ /\A[_A-Za-z]\w*\z/
111
+
112
+ reader, reader_line = "def #{name}; config.#{name}; end", __LINE__
113
+ writer, writer_line = "def #{name}=(value); config.#{name} = value; end", __LINE__
114
+
115
+ singleton_class.class_eval reader, __FILE__, reader_line
116
+ singleton_class.class_eval writer, __FILE__, writer_line
117
+
118
+ unless options[:instance_accessor] == false
119
+ class_eval reader, __FILE__, reader_line unless options[:instance_reader] == false
120
+ class_eval writer, __FILE__, writer_line unless options[:instance_writer] == false
121
+ end
122
+ send("#{name}=", yield) if block_given?
123
+ end
124
+ end
125
+ private :config_accessor
126
+ end
127
+
128
+ # Reads and writes attributes from a configuration <tt>OrderedHash</tt>.
129
+ #
130
+ # require 'active_support/configurable'
131
+ #
132
+ # class User
133
+ # include CoreExt::Configurable
134
+ # end
135
+ #
136
+ # user = User.new
137
+ #
138
+ # user.config.allowed_access = true
139
+ # user.config.level = 1
140
+ #
141
+ # user.config.allowed_access # => true
142
+ # user.config.level # => 1
143
+ def config
144
+ @_config ||= self.class.config.inheritable_copy
145
+ end
146
+ end
147
+ end
148
+
@@ -0,0 +1,8 @@
1
+ require 'core_ext/object/acts_like'
2
+
3
+ class Date
4
+ # Duck-types as a Date-like class. See Object#acts_like?.
5
+ def acts_like_date?
6
+ true
7
+ end
8
+ end
@@ -0,0 +1,12 @@
1
+ require 'date'
2
+
3
+ class Date #:nodoc:
4
+ # No Date is blank:
5
+ #
6
+ # Date.today.blank? # => false
7
+ #
8
+ # @return [false]
9
+ def blank?
10
+ false
11
+ end
12
+ end
@@ -0,0 +1,143 @@
1
+ require 'date'
2
+ require 'core_ext/duration'
3
+ require 'core_ext/object/acts_like'
4
+ require 'core_ext/date/zones'
5
+ require 'core_ext/time/zones'
6
+ require 'core_ext/date_and_time/calculations'
7
+
8
+ class Date
9
+ include DateAndTime::Calculations
10
+
11
+ class << self
12
+ attr_accessor :beginning_of_week_default
13
+
14
+ # Returns the week start (e.g. :monday) for the current request, if this has been set (via Date.beginning_of_week=).
15
+ # If <tt>Date.beginning_of_week</tt> has not been set for the current request, returns the week start specified in <tt>config.beginning_of_week</tt>.
16
+ # If no config.beginning_of_week was specified, returns :monday.
17
+ def beginning_of_week
18
+ Thread.current[:beginning_of_week] || beginning_of_week_default || :monday
19
+ end
20
+
21
+ # Sets <tt>Date.beginning_of_week</tt> to a week start (e.g. :monday) for current request/thread.
22
+ #
23
+ # This method accepts any of the following day symbols:
24
+ # :monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday
25
+ def beginning_of_week=(week_start)
26
+ Thread.current[:beginning_of_week] = find_beginning_of_week!(week_start)
27
+ end
28
+
29
+ # Returns week start day symbol (e.g. :monday), or raises an +ArgumentError+ for invalid day symbol.
30
+ def find_beginning_of_week!(week_start)
31
+ raise ArgumentError, "Invalid beginning of week: #{week_start}" unless ::Date::DAYS_INTO_WEEK.key?(week_start)
32
+ week_start
33
+ end
34
+
35
+ # Returns a new Date representing the date 1 day ago (i.e. yesterday's date).
36
+ def yesterday
37
+ ::Date.current.yesterday
38
+ end
39
+
40
+ # Returns a new Date representing the date 1 day after today (i.e. tomorrow's date).
41
+ def tomorrow
42
+ ::Date.current.tomorrow
43
+ end
44
+
45
+ # Returns Time.zone.today when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns Date.today.
46
+ def current
47
+ ::Time.zone ? ::Time.zone.today : ::Date.today
48
+ end
49
+ end
50
+
51
+ # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
52
+ # and then subtracts the specified number of seconds.
53
+ def ago(seconds)
54
+ in_time_zone.since(-seconds)
55
+ end
56
+
57
+ # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
58
+ # and then adds the specified number of seconds
59
+ def since(seconds)
60
+ in_time_zone.since(seconds)
61
+ end
62
+ alias :in :since
63
+
64
+ # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
65
+ def beginning_of_day
66
+ in_time_zone
67
+ end
68
+ alias :midnight :beginning_of_day
69
+ alias :at_midnight :beginning_of_day
70
+ alias :at_beginning_of_day :beginning_of_day
71
+
72
+ # Converts Date to a Time (or DateTime if necessary) with the time portion set to the middle of the day (12:00)
73
+ def middle_of_day
74
+ in_time_zone.middle_of_day
75
+ end
76
+ alias :midday :middle_of_day
77
+ alias :noon :middle_of_day
78
+ alias :at_midday :middle_of_day
79
+ alias :at_noon :middle_of_day
80
+ alias :at_middle_of_day :middle_of_day
81
+
82
+ # Converts Date to a Time (or DateTime if necessary) with the time portion set to the end of the day (23:59:59)
83
+ def end_of_day
84
+ in_time_zone.end_of_day
85
+ end
86
+ alias :at_end_of_day :end_of_day
87
+
88
+ def plus_with_duration(other) #:nodoc:
89
+ if CoreExt::Duration === other
90
+ other.since(self)
91
+ else
92
+ plus_without_duration(other)
93
+ end
94
+ end
95
+ alias_method :plus_without_duration, :+
96
+ alias_method :+, :plus_with_duration
97
+
98
+ def minus_with_duration(other) #:nodoc:
99
+ if CoreExt::Duration === other
100
+ plus_with_duration(-other)
101
+ else
102
+ minus_without_duration(other)
103
+ end
104
+ end
105
+ alias_method :minus_without_duration, :-
106
+ alias_method :-, :minus_with_duration
107
+
108
+ # Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with
109
+ # any of these keys: <tt>:years</tt>, <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>.
110
+ def advance(options)
111
+ options = options.dup
112
+ d = self
113
+ d = d >> options.delete(:years) * 12 if options[:years]
114
+ d = d >> options.delete(:months) if options[:months]
115
+ d = d + options.delete(:weeks) * 7 if options[:weeks]
116
+ d = d + options.delete(:days) if options[:days]
117
+ d
118
+ end
119
+
120
+ # Returns a new Date where one or more of the elements have been changed according to the +options+ parameter.
121
+ # The +options+ parameter is a hash with a combination of these keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>.
122
+ #
123
+ # Date.new(2007, 5, 12).change(day: 1) # => Date.new(2007, 5, 1)
124
+ # Date.new(2007, 5, 12).change(year: 2005, month: 1) # => Date.new(2005, 1, 12)
125
+ def change(options)
126
+ ::Date.new(
127
+ options.fetch(:year, year),
128
+ options.fetch(:month, month),
129
+ options.fetch(:day, day)
130
+ )
131
+ end
132
+
133
+ # Allow Date to be compared with Time by converting to DateTime and relying on the <=> from there.
134
+ def compare_with_coercion(other)
135
+ if other.is_a?(Time)
136
+ self.to_datetime <=> other
137
+ else
138
+ compare_without_coercion(other)
139
+ end
140
+ end
141
+ alias_method :compare_without_coercion, :<=>
142
+ alias_method :<=>, :compare_with_coercion
143
+ end