core_ext 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|