core_ext 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.
- checksums.yaml +7 -0
- data/README.md +3 -0
- data/lib/core_ext/array/access.rb +76 -0
- data/lib/core_ext/array/conversions.rb +211 -0
- data/lib/core_ext/array/extract_options.rb +29 -0
- data/lib/core_ext/array/grouping.rb +116 -0
- data/lib/core_ext/array/inquiry.rb +17 -0
- data/lib/core_ext/array/prepend_and_append.rb +7 -0
- data/lib/core_ext/array/wrap.rb +46 -0
- data/lib/core_ext/array.rb +7 -0
- data/lib/core_ext/array_inquirer.rb +44 -0
- data/lib/core_ext/benchmark.rb +14 -0
- data/lib/core_ext/benchmarkable.rb +49 -0
- data/lib/core_ext/big_decimal/conversions.rb +14 -0
- data/lib/core_ext/big_decimal.rb +1 -0
- data/lib/core_ext/builder.rb +6 -0
- data/lib/core_ext/callbacks.rb +770 -0
- data/lib/core_ext/class/attribute.rb +128 -0
- data/lib/core_ext/class/attribute_accessors.rb +4 -0
- data/lib/core_ext/class/subclasses.rb +42 -0
- data/lib/core_ext/class.rb +2 -0
- data/lib/core_ext/concern.rb +142 -0
- data/lib/core_ext/configurable.rb +148 -0
- data/lib/core_ext/date/acts_like.rb +8 -0
- data/lib/core_ext/date/blank.rb +12 -0
- data/lib/core_ext/date/calculations.rb +143 -0
- data/lib/core_ext/date/conversions.rb +93 -0
- data/lib/core_ext/date/zones.rb +6 -0
- data/lib/core_ext/date.rb +5 -0
- data/lib/core_ext/date_and_time/calculations.rb +328 -0
- data/lib/core_ext/date_and_time/zones.rb +40 -0
- data/lib/core_ext/date_time/acts_like.rb +14 -0
- data/lib/core_ext/date_time/blank.rb +12 -0
- data/lib/core_ext/date_time/calculations.rb +177 -0
- data/lib/core_ext/date_time/conversions.rb +104 -0
- data/lib/core_ext/date_time/zones.rb +6 -0
- data/lib/core_ext/date_time.rb +5 -0
- data/lib/core_ext/deprecation/behaviors.rb +86 -0
- data/lib/core_ext/deprecation/instance_delegator.rb +24 -0
- data/lib/core_ext/deprecation/method_wrappers.rb +70 -0
- data/lib/core_ext/deprecation/proxy_wrappers.rb +149 -0
- data/lib/core_ext/deprecation/reporting.rb +105 -0
- data/lib/core_ext/deprecation.rb +43 -0
- data/lib/core_ext/digest/uuid.rb +51 -0
- data/lib/core_ext/duration.rb +157 -0
- data/lib/core_ext/enumerable.rb +106 -0
- data/lib/core_ext/file/atomic.rb +68 -0
- data/lib/core_ext/file.rb +1 -0
- data/lib/core_ext/hash/compact.rb +20 -0
- data/lib/core_ext/hash/conversions.rb +261 -0
- data/lib/core_ext/hash/deep_merge.rb +38 -0
- data/lib/core_ext/hash/except.rb +22 -0
- data/lib/core_ext/hash/indifferent_access.rb +23 -0
- data/lib/core_ext/hash/keys.rb +170 -0
- data/lib/core_ext/hash/reverse_merge.rb +22 -0
- data/lib/core_ext/hash/slice.rb +48 -0
- data/lib/core_ext/hash/transform_values.rb +29 -0
- data/lib/core_ext/hash.rb +9 -0
- data/lib/core_ext/hash_with_indifferent_access.rb +298 -0
- data/lib/core_ext/inflections.rb +70 -0
- data/lib/core_ext/inflector/inflections.rb +244 -0
- data/lib/core_ext/inflector/methods.rb +381 -0
- data/lib/core_ext/inflector/transliterate.rb +112 -0
- data/lib/core_ext/inflector.rb +7 -0
- data/lib/core_ext/integer/inflections.rb +29 -0
- data/lib/core_ext/integer/multiple.rb +10 -0
- data/lib/core_ext/integer/time.rb +29 -0
- data/lib/core_ext/integer.rb +3 -0
- data/lib/core_ext/json/decoding.rb +67 -0
- data/lib/core_ext/json/encoding.rb +127 -0
- data/lib/core_ext/json.rb +2 -0
- data/lib/core_ext/kernel/agnostics.rb +11 -0
- data/lib/core_ext/kernel/concern.rb +10 -0
- data/lib/core_ext/kernel/reporting.rb +41 -0
- data/lib/core_ext/kernel/singleton_class.rb +6 -0
- data/lib/core_ext/kernel.rb +4 -0
- data/lib/core_ext/load_error.rb +30 -0
- data/lib/core_ext/logger.rb +57 -0
- data/lib/core_ext/logger_silence.rb +24 -0
- data/lib/core_ext/marshal.rb +19 -0
- data/lib/core_ext/module/aliasing.rb +74 -0
- data/lib/core_ext/module/anonymous.rb +28 -0
- data/lib/core_ext/module/attr_internal.rb +36 -0
- data/lib/core_ext/module/attribute_accessors.rb +212 -0
- data/lib/core_ext/module/concerning.rb +135 -0
- data/lib/core_ext/module/delegation.rb +218 -0
- data/lib/core_ext/module/deprecation.rb +23 -0
- data/lib/core_ext/module/introspection.rb +62 -0
- data/lib/core_ext/module/method_transplanting.rb +3 -0
- data/lib/core_ext/module/qualified_const.rb +52 -0
- data/lib/core_ext/module/reachable.rb +8 -0
- data/lib/core_ext/module/remove_method.rb +35 -0
- data/lib/core_ext/module.rb +11 -0
- data/lib/core_ext/multibyte/chars.rb +231 -0
- data/lib/core_ext/multibyte/unicode.rb +388 -0
- data/lib/core_ext/multibyte.rb +21 -0
- data/lib/core_ext/name_error.rb +31 -0
- data/lib/core_ext/numeric/bytes.rb +64 -0
- data/lib/core_ext/numeric/conversions.rb +132 -0
- data/lib/core_ext/numeric/inquiry.rb +26 -0
- data/lib/core_ext/numeric/time.rb +74 -0
- data/lib/core_ext/numeric.rb +4 -0
- data/lib/core_ext/object/acts_like.rb +10 -0
- data/lib/core_ext/object/blank.rb +140 -0
- data/lib/core_ext/object/conversions.rb +4 -0
- data/lib/core_ext/object/deep_dup.rb +53 -0
- data/lib/core_ext/object/duplicable.rb +98 -0
- data/lib/core_ext/object/inclusion.rb +27 -0
- data/lib/core_ext/object/instance_variables.rb +28 -0
- data/lib/core_ext/object/json.rb +199 -0
- data/lib/core_ext/object/to_param.rb +1 -0
- data/lib/core_ext/object/to_query.rb +84 -0
- data/lib/core_ext/object/try.rb +146 -0
- data/lib/core_ext/object/with_options.rb +69 -0
- data/lib/core_ext/object.rb +14 -0
- data/lib/core_ext/option_merger.rb +25 -0
- data/lib/core_ext/ordered_hash.rb +48 -0
- data/lib/core_ext/ordered_options.rb +81 -0
- data/lib/core_ext/range/conversions.rb +34 -0
- data/lib/core_ext/range/each.rb +21 -0
- data/lib/core_ext/range/include_range.rb +23 -0
- data/lib/core_ext/range/overlaps.rb +8 -0
- data/lib/core_ext/range.rb +4 -0
- data/lib/core_ext/regexp.rb +5 -0
- data/lib/core_ext/rescuable.rb +119 -0
- data/lib/core_ext/securerandom.rb +23 -0
- data/lib/core_ext/security_utils.rb +20 -0
- data/lib/core_ext/string/access.rb +104 -0
- data/lib/core_ext/string/behavior.rb +6 -0
- data/lib/core_ext/string/conversions.rb +56 -0
- data/lib/core_ext/string/exclude.rb +11 -0
- data/lib/core_ext/string/filters.rb +102 -0
- data/lib/core_ext/string/indent.rb +43 -0
- data/lib/core_ext/string/inflections.rb +235 -0
- data/lib/core_ext/string/inquiry.rb +13 -0
- data/lib/core_ext/string/multibyte.rb +53 -0
- data/lib/core_ext/string/output_safety.rb +261 -0
- data/lib/core_ext/string/starts_ends_with.rb +4 -0
- data/lib/core_ext/string/strip.rb +23 -0
- data/lib/core_ext/string/zones.rb +14 -0
- data/lib/core_ext/string.rb +13 -0
- data/lib/core_ext/string_inquirer.rb +26 -0
- data/lib/core_ext/tagged_logging.rb +78 -0
- data/lib/core_ext/test_case.rb +88 -0
- data/lib/core_ext/testing/assertions.rb +99 -0
- data/lib/core_ext/testing/autorun.rb +12 -0
- data/lib/core_ext/testing/composite_filter.rb +54 -0
- data/lib/core_ext/testing/constant_lookup.rb +50 -0
- data/lib/core_ext/testing/declarative.rb +26 -0
- data/lib/core_ext/testing/deprecation.rb +36 -0
- data/lib/core_ext/testing/file_fixtures.rb +34 -0
- data/lib/core_ext/testing/isolation.rb +115 -0
- data/lib/core_ext/testing/method_call_assertions.rb +41 -0
- data/lib/core_ext/testing/setup_and_teardown.rb +50 -0
- data/lib/core_ext/testing/stream.rb +42 -0
- data/lib/core_ext/testing/tagged_logging.rb +25 -0
- data/lib/core_ext/testing/time_helpers.rb +134 -0
- data/lib/core_ext/time/acts_like.rb +8 -0
- data/lib/core_ext/time/calculations.rb +284 -0
- data/lib/core_ext/time/conversions.rb +66 -0
- data/lib/core_ext/time/zones.rb +95 -0
- data/lib/core_ext/time.rb +20 -0
- data/lib/core_ext/time_with_zone.rb +503 -0
- data/lib/core_ext/time_zone.rb +464 -0
- data/lib/core_ext/uri.rb +25 -0
- data/lib/core_ext/version.rb +3 -0
- data/lib/core_ext/xml_mini/jdom.rb +181 -0
- data/lib/core_ext/xml_mini/libxml.rb +79 -0
- data/lib/core_ext/xml_mini/libxmlsax.rb +85 -0
- data/lib/core_ext/xml_mini/nokogiri.rb +83 -0
- data/lib/core_ext/xml_mini/nokogirisax.rb +87 -0
- data/lib/core_ext/xml_mini/rexml.rb +130 -0
- data/lib/core_ext/xml_mini.rb +194 -0
- data/lib/core_ext.rb +3 -0
- 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,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,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,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
|