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,84 @@
1
+ require 'cgi'
2
+
3
+ class Object
4
+ # Alias of <tt>to_s</tt>.
5
+ def to_param
6
+ to_s
7
+ end
8
+
9
+ # Converts an object into a string suitable for use as a URL query string,
10
+ # using the given <tt>key</tt> as the param name.
11
+ def to_query(key)
12
+ "#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
13
+ end
14
+ end
15
+
16
+ class NilClass
17
+ # Returns +self+.
18
+ def to_param
19
+ self
20
+ end
21
+ end
22
+
23
+ class TrueClass
24
+ # Returns +self+.
25
+ def to_param
26
+ self
27
+ end
28
+ end
29
+
30
+ class FalseClass
31
+ # Returns +self+.
32
+ def to_param
33
+ self
34
+ end
35
+ end
36
+
37
+ class Array
38
+ # Calls <tt>to_param</tt> on all its elements and joins the result with
39
+ # slashes. This is used by <tt>url_for</tt> in Action Pack.
40
+ def to_param
41
+ collect(&:to_param).join '/'
42
+ end
43
+
44
+ # Converts an array into a string suitable for use as a URL query string,
45
+ # using the given +key+ as the param name.
46
+ #
47
+ # ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
48
+ def to_query(key)
49
+ prefix = "#{key}[]"
50
+
51
+ if empty?
52
+ nil.to_query(prefix)
53
+ else
54
+ collect { |value| value.to_query(prefix) }.join '&'
55
+ end
56
+ end
57
+ end
58
+
59
+ class Hash
60
+ # Returns a string representation of the receiver suitable for use as a URL
61
+ # query string:
62
+ #
63
+ # {name: 'David', nationality: 'Danish'}.to_query
64
+ # # => "name=David&nationality=Danish"
65
+ #
66
+ # An optional namespace can be passed to enclose key names:
67
+ #
68
+ # {name: 'David', nationality: 'Danish'}.to_query('user')
69
+ # # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
70
+ #
71
+ # The string pairs "key=value" that conform the query string
72
+ # are sorted lexicographically in ascending order.
73
+ #
74
+ # This method is also aliased as +to_param+.
75
+ def to_query(namespace = nil)
76
+ collect do |key, value|
77
+ unless (value.is_a?(Hash) || value.is_a?(Array)) && value.empty?
78
+ value.to_query(namespace ? "#{namespace}[#{key}]" : key)
79
+ end
80
+ end.compact.sort! * '&'
81
+ end
82
+
83
+ alias_method :to_param, :to_query
84
+ end
@@ -0,0 +1,146 @@
1
+ require 'delegate'
2
+
3
+ module CoreExt
4
+ module Tryable #:nodoc:
5
+ def try(*a, &b)
6
+ try!(*a, &b) if a.empty? || respond_to?(a.first)
7
+ end
8
+
9
+ def try!(*a, &b)
10
+ if a.empty? && block_given?
11
+ if b.arity == 0
12
+ instance_eval(&b)
13
+ else
14
+ yield self
15
+ end
16
+ else
17
+ public_send(*a, &b)
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ class Object
24
+ include CoreExt::Tryable
25
+
26
+ ##
27
+ # :method: try
28
+ #
29
+ # :call-seq:
30
+ # try(*a, &b)
31
+ #
32
+ # Invokes the public method whose name goes as first argument just like
33
+ # +public_send+ does, except that if the receiver does not respond to it the
34
+ # call returns +nil+ rather than raising an exception.
35
+ #
36
+ # This method is defined to be able to write
37
+ #
38
+ # @person.try(:name)
39
+ #
40
+ # instead of
41
+ #
42
+ # @person.name if @person
43
+ #
44
+ # +try+ calls can be chained:
45
+ #
46
+ # @person.try(:spouse).try(:name)
47
+ #
48
+ # instead of
49
+ #
50
+ # @person.spouse.name if @person && @person.spouse
51
+ #
52
+ # +try+ will also return +nil+ if the receiver does not respond to the method:
53
+ #
54
+ # @person.try(:non_existing_method) # => nil
55
+ #
56
+ # instead of
57
+ #
58
+ # @person.non_existing_method if @person.respond_to?(:non_existing_method) # => nil
59
+ #
60
+ # +try+ returns +nil+ when called on +nil+ regardless of whether it responds
61
+ # to the method:
62
+ #
63
+ # nil.try(:to_i) # => nil, rather than 0
64
+ #
65
+ # Arguments and blocks are forwarded to the method if invoked:
66
+ #
67
+ # @posts.try(:each_slice, 2) do |a, b|
68
+ # ...
69
+ # end
70
+ #
71
+ # The number of arguments in the signature must match. If the object responds
72
+ # to the method the call is attempted and +ArgumentError+ is still raised
73
+ # in case of argument mismatch.
74
+ #
75
+ # If +try+ is called without arguments it yields the receiver to a given
76
+ # block unless it is +nil+:
77
+ #
78
+ # @person.try do |p|
79
+ # ...
80
+ # end
81
+ #
82
+ # You can also call try with a block without accepting an argument, and the block
83
+ # will be instance_eval'ed instead:
84
+ #
85
+ # @person.try { upcase.truncate(50) }
86
+ #
87
+ # Please also note that +try+ is defined on +Object+. Therefore, it won't work
88
+ # with instances of classes that do not have +Object+ among their ancestors,
89
+ # like direct subclasses of +BasicObject+.
90
+
91
+ ##
92
+ # :method: try!
93
+ #
94
+ # :call-seq:
95
+ # try!(*a, &b)
96
+ #
97
+ # Same as #try, but raises a +NoMethodError+ exception if the receiver is
98
+ # not +nil+ and does not implement the tried method.
99
+ #
100
+ # "a".try!(:upcase) # => "A"
101
+ # nil.try!(:upcase) # => nil
102
+ # 123.try!(:upcase) # => NoMethodError: undefined method `upcase' for 123:Fixnum
103
+ end
104
+
105
+ class Delegator
106
+ include CoreExt::Tryable
107
+
108
+ ##
109
+ # :method: try
110
+ #
111
+ # :call-seq:
112
+ # try(a*, &b)
113
+ #
114
+ # See Object#try
115
+
116
+ ##
117
+ # :method: try!
118
+ #
119
+ # :call-seq:
120
+ # try!(a*, &b)
121
+ #
122
+ # See Object#try!
123
+ end
124
+
125
+ class NilClass
126
+ # Calling +try+ on +nil+ always returns +nil+.
127
+ # It becomes especially helpful when navigating through associations that may return +nil+.
128
+ #
129
+ # nil.try(:name) # => nil
130
+ #
131
+ # Without +try+
132
+ # @person && @person.children.any? && @person.children.first.name
133
+ #
134
+ # With +try+
135
+ # @person.try(:children).try(:first).try(:name)
136
+ def try(*args)
137
+ nil
138
+ end
139
+
140
+ # Calling +try!+ on +nil+ always returns +nil+.
141
+ #
142
+ # nil.try!(:name) # => nil
143
+ def try!(*args)
144
+ nil
145
+ end
146
+ end
@@ -0,0 +1,69 @@
1
+ require 'core_ext/option_merger'
2
+
3
+ class Object
4
+ # An elegant way to factor duplication out of options passed to a series of
5
+ # method calls. Each method called in the block, with the block variable as
6
+ # the receiver, will have its options merged with the default +options+ hash
7
+ # provided. Each method called on the block variable must take an options
8
+ # hash as its final argument.
9
+ #
10
+ # Without <tt>with_options</tt>, this code contains duplication:
11
+ #
12
+ # class Account < ActiveRecord::Base
13
+ # has_many :customers, dependent: :destroy
14
+ # has_many :products, dependent: :destroy
15
+ # has_many :invoices, dependent: :destroy
16
+ # has_many :expenses, dependent: :destroy
17
+ # end
18
+ #
19
+ # Using <tt>with_options</tt>, we can remove the duplication:
20
+ #
21
+ # class Account < ActiveRecord::Base
22
+ # with_options dependent: :destroy do |assoc|
23
+ # assoc.has_many :customers
24
+ # assoc.has_many :products
25
+ # assoc.has_many :invoices
26
+ # assoc.has_many :expenses
27
+ # end
28
+ # end
29
+ #
30
+ # It can also be used with an explicit receiver:
31
+ #
32
+ # I18n.with_options locale: user.locale, scope: 'newsletter' do |i18n|
33
+ # subject i18n.t :subject
34
+ # body i18n.t :body, user_name: user.name
35
+ # end
36
+ #
37
+ # When you don't pass an explicit receiver, it executes the whole block
38
+ # in merging options context:
39
+ #
40
+ # class Account < ActiveRecord::Base
41
+ # with_options dependent: :destroy do
42
+ # has_many :customers
43
+ # has_many :products
44
+ # has_many :invoices
45
+ # has_many :expenses
46
+ # end
47
+ # end
48
+ #
49
+ # <tt>with_options</tt> can also be nested since the call is forwarded to its receiver.
50
+ #
51
+ # NOTE: Each nesting level will merge inherited defaults in addition to their own.
52
+ #
53
+ # class Post < ActiveRecord::Base
54
+ # with_options if: :persisted?, length: { minimum: 50 } do
55
+ # validates :content, if: -> { content.present? }
56
+ # end
57
+ # end
58
+ #
59
+ # The code is equivalent to:
60
+ #
61
+ # validates :content, length: { minimum: 50 }, if: -> { content.present? }
62
+ #
63
+ # Hence the inherited default for `if` key is ignored.
64
+ #
65
+ def with_options(options, &block)
66
+ option_merger = CoreExt::OptionMerger.new(self, options)
67
+ block.arity.zero? ? option_merger.instance_eval(&block) : block.call(option_merger)
68
+ end
69
+ end
@@ -0,0 +1,14 @@
1
+ require 'core_ext/object/acts_like'
2
+ require 'core_ext/object/blank'
3
+ require 'core_ext/object/duplicable'
4
+ require 'core_ext/object/deep_dup'
5
+ require 'core_ext/object/try'
6
+ require 'core_ext/object/inclusion'
7
+
8
+ require 'core_ext/object/conversions'
9
+ require 'core_ext/object/instance_variables'
10
+
11
+ require 'core_ext/object/json'
12
+ require 'core_ext/object/to_param'
13
+ require 'core_ext/object/to_query'
14
+ require 'core_ext/object/with_options'
@@ -0,0 +1,25 @@
1
+ require 'core_ext/hash/deep_merge'
2
+
3
+ module CoreExt
4
+ class OptionMerger #:nodoc:
5
+ instance_methods.each do |method|
6
+ undef_method(method) if method !~ /^(__|instance_eval|class|object_id)/
7
+ end
8
+
9
+ def initialize(context, options)
10
+ @context, @options = context, options
11
+ end
12
+
13
+ private
14
+ def method_missing(method, *arguments, &block)
15
+ if arguments.first.is_a?(Proc)
16
+ proc = arguments.pop
17
+ arguments << lambda { |*args| @options.deep_merge(proc.call(*args)) }
18
+ else
19
+ arguments << (arguments.last.respond_to?(:to_hash) ? @options.deep_merge(arguments.pop) : @options.dup)
20
+ end
21
+
22
+ @context.__send__(method, *arguments, &block)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,48 @@
1
+ require 'yaml'
2
+
3
+ YAML.add_builtin_type("omap") do |type, val|
4
+ CoreExt::OrderedHash[val.map{ |v| v.to_a.first }]
5
+ end
6
+
7
+ module CoreExt
8
+ # <tt>CoreExt::OrderedHash</tt> implements a hash that preserves
9
+ # insertion order.
10
+ #
11
+ # oh = CoreExt::OrderedHash.new
12
+ # oh[:a] = 1
13
+ # oh[:b] = 2
14
+ # oh.keys # => [:a, :b], this order is guaranteed
15
+ #
16
+ # Also, maps the +omap+ feature for YAML files
17
+ # (See http://yaml.org/type/omap.html) to support ordered items
18
+ # when loading from yaml.
19
+ #
20
+ # <tt>CoreExt::OrderedHash</tt> is namespaced to prevent conflicts
21
+ # with other implementations.
22
+ class OrderedHash < ::Hash
23
+ def to_yaml_type
24
+ "!tag:yaml.org,2002:omap"
25
+ end
26
+
27
+ def encode_with(coder)
28
+ coder.represent_seq '!omap', map { |k,v| { k => v } }
29
+ end
30
+
31
+ def select(*args, &block)
32
+ dup.tap { |hash| hash.select!(*args, &block) }
33
+ end
34
+
35
+ def reject(*args, &block)
36
+ dup.tap { |hash| hash.reject!(*args, &block) }
37
+ end
38
+
39
+ def nested_under_indifferent_access
40
+ self
41
+ end
42
+
43
+ # Returns true to make sure that this hash is extractable via <tt>Array#extract_options!</tt>
44
+ def extractable_options?
45
+ true
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,81 @@
1
+ module CoreExt
2
+ # Usually key value pairs are handled something like this:
3
+ #
4
+ # h = {}
5
+ # h[:boy] = 'John'
6
+ # h[:girl] = 'Mary'
7
+ # h[:boy] # => 'John'
8
+ # h[:girl] # => 'Mary'
9
+ # h[:dog] # => nil
10
+ #
11
+ # Using +OrderedOptions+, the above code could be reduced to:
12
+ #
13
+ # h = CoreExt::OrderedOptions.new
14
+ # h.boy = 'John'
15
+ # h.girl = 'Mary'
16
+ # h.boy # => 'John'
17
+ # h.girl # => 'Mary'
18
+ # h.dog # => nil
19
+ #
20
+ # To raise an exception when the value is blank, append a
21
+ # bang to the key name, like:
22
+ #
23
+ # h.dog! # => raises KeyError: key not found: :dog
24
+ #
25
+ class OrderedOptions < Hash
26
+ alias_method :_get, :[] # preserve the original #[] method
27
+ protected :_get # make it protected
28
+
29
+ def []=(key, value)
30
+ super(key.to_sym, value)
31
+ end
32
+
33
+ def [](key)
34
+ super(key.to_sym)
35
+ end
36
+
37
+ def method_missing(name, *args)
38
+ name_string = name.to_s
39
+ if name_string.chomp!('=')
40
+ self[name_string] = args.first
41
+ else
42
+ bangs = name_string.chomp!('!')
43
+
44
+ if bangs
45
+ fetch(name_string.to_sym).presence || raise(KeyError.new("#{name_string} is blank."))
46
+ else
47
+ self[name_string]
48
+ end
49
+ end
50
+ end
51
+
52
+ def respond_to_missing?(name, include_private)
53
+ true
54
+ end
55
+ end
56
+
57
+ # +InheritableOptions+ provides a constructor to build an +OrderedOptions+
58
+ # hash inherited from another hash.
59
+ #
60
+ # Use this if you already have some hash and you want to create a new one based on it.
61
+ #
62
+ # h = CoreExt::InheritableOptions.new({ girl: 'Mary', boy: 'John' })
63
+ # h.girl # => 'Mary'
64
+ # h.boy # => 'John'
65
+ class InheritableOptions < OrderedOptions
66
+ def initialize(parent = nil)
67
+ if parent.kind_of?(OrderedOptions)
68
+ # use the faster _get when dealing with OrderedOptions
69
+ super() { |h,k| parent._get(k) }
70
+ elsif parent
71
+ super() { |h,k| parent[k] }
72
+ else
73
+ super()
74
+ end
75
+ end
76
+
77
+ def inheritable_copy
78
+ self.class.new(self)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,34 @@
1
+ class Range
2
+ RANGE_FORMATS = {
3
+ :db => Proc.new { |start, stop| "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'" }
4
+ }
5
+
6
+ # Convert range to a formatted string. See RANGE_FORMATS for predefined formats.
7
+ #
8
+ # This method is aliased to <tt>to_s</tt>.
9
+ #
10
+ # range = (1..100) # => 1..100
11
+ #
12
+ # range.to_formatted_s # => "1..100"
13
+ # range.to_s # => "1..100"
14
+ #
15
+ # range.to_formatted_s(:db) # => "BETWEEN '1' AND '100'"
16
+ # range.to_s(:db) # => "BETWEEN '1' AND '100'"
17
+ #
18
+ # == Adding your own range formats to to_formatted_s
19
+ # You can add your own formats to the Range::RANGE_FORMATS hash.
20
+ # Use the format name as the hash key and a Proc instance.
21
+ #
22
+ # # config/initializers/range_formats.rb
23
+ # Range::RANGE_FORMATS[:short] = ->(start, stop) { "Between #{start.to_s(:db)} and #{stop.to_s(:db)}" }
24
+ def to_formatted_s(format = :default)
25
+ if formatter = RANGE_FORMATS[format]
26
+ formatter.call(first, last)
27
+ else
28
+ to_default_s
29
+ end
30
+ end
31
+
32
+ alias_method :to_default_s, :to_s
33
+ alias_method :to_s, :to_formatted_s
34
+ end
@@ -0,0 +1,21 @@
1
+ module CoreExt
2
+ module EachTimeWithZone #:nodoc:
3
+ def each(&block)
4
+ ensure_iteration_allowed
5
+ super
6
+ end
7
+
8
+ def step(n = 1, &block)
9
+ ensure_iteration_allowed
10
+ super
11
+ end
12
+
13
+ private
14
+
15
+ def ensure_iteration_allowed
16
+ raise TypeError, "can't iterate from #{first.class}" if first.is_a?(Time)
17
+ end
18
+ end
19
+ end
20
+
21
+ Range.prepend(CoreExt::EachTimeWithZone)
@@ -0,0 +1,23 @@
1
+ module CoreExt
2
+ module IncludeWithRange #:nodoc:
3
+ # Extends the default Range#include? to support range comparisons.
4
+ # (1..5).include?(1..5) # => true
5
+ # (1..5).include?(2..3) # => true
6
+ # (1..5).include?(2..6) # => false
7
+ #
8
+ # The native Range#include? behavior is untouched.
9
+ # ('a'..'f').include?('c') # => true
10
+ # (5..9).include?(11) # => false
11
+ def include?(value)
12
+ if value.is_a?(::Range)
13
+ # 1...10 includes 1..9 but it does not include 1..10.
14
+ operator = exclude_end? && !value.exclude_end? ? :< : :<=
15
+ super(value.first) && value.last.send(operator, last)
16
+ else
17
+ super
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ Range.prepend(CoreExt::IncludeWithRange)
@@ -0,0 +1,8 @@
1
+ class Range
2
+ # Compare two ranges and see if they overlap each other
3
+ # (1..5).overlaps?(4..6) # => true
4
+ # (1..5).overlaps?(7..9) # => false
5
+ def overlaps?(other)
6
+ cover?(other.first) || other.cover?(first)
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ require 'core_ext/range/conversions'
2
+ require 'core_ext/range/include_range'
3
+ require 'core_ext/range/overlaps'
4
+ require 'core_ext/range/each'
@@ -0,0 +1,5 @@
1
+ class Regexp #:nodoc:
2
+ def multiline?
3
+ options & MULTILINE == MULTILINE
4
+ end
5
+ end
@@ -0,0 +1,119 @@
1
+ require 'core_ext/concern'
2
+ require 'core_ext/class/attribute'
3
+ require 'core_ext/string/inflections'
4
+ require 'core_ext/array/extract_options'
5
+
6
+ module CoreExt
7
+ # Rescuable module adds support for easier exception handling.
8
+ module Rescuable
9
+ extend Concern
10
+
11
+ included do
12
+ class_attribute :rescue_handlers
13
+ self.rescue_handlers = []
14
+ end
15
+
16
+ module ClassMethods
17
+ # Rescue exceptions raised in controller actions.
18
+ #
19
+ # <tt>rescue_from</tt> receives a series of exception classes or class
20
+ # names, and a trailing <tt>:with</tt> option with the name of a method
21
+ # or a Proc object to be called to handle them. Alternatively a block can
22
+ # be given.
23
+ #
24
+ # Handlers that take one argument will be called with the exception, so
25
+ # that the exception can be inspected when dealing with it.
26
+ #
27
+ # Handlers are inherited. They are searched from right to left, from
28
+ # bottom to top, and up the hierarchy. The handler of the first class for
29
+ # which <tt>exception.is_a?(klass)</tt> holds true is the one invoked, if
30
+ # any.
31
+ #
32
+ # class ApplicationController < ActionController::Base
33
+ # rescue_from User::NotAuthorized, with: :deny_access # self defined exception
34
+ # rescue_from ActiveRecord::RecordInvalid, with: :show_errors
35
+ #
36
+ # rescue_from 'MyAppError::Base' do |exception|
37
+ # render xml: exception, status: 500
38
+ # end
39
+ #
40
+ # protected
41
+ # def deny_access
42
+ # ...
43
+ # end
44
+ #
45
+ # def show_errors(exception)
46
+ # exception.record.new_record? ? ...
47
+ # end
48
+ # end
49
+ #
50
+ # Exceptions raised inside exception handlers are not propagated up.
51
+ def rescue_from(*klasses, &block)
52
+ options = klasses.extract_options!
53
+
54
+ unless options.has_key?(:with)
55
+ if block_given?
56
+ options[:with] = block
57
+ else
58
+ raise ArgumentError, "Need a handler. Supply an options hash that has a :with key as the last argument."
59
+ end
60
+ end
61
+
62
+ klasses.each do |klass|
63
+ key = if klass.is_a?(Module) && klass.respond_to?(:===)
64
+ klass.name
65
+ elsif klass.is_a?(String)
66
+ klass
67
+ else
68
+ raise ArgumentError, "#{klass} is neither an Exception nor a String"
69
+ end
70
+
71
+ # Put the new handler at the end because the list is read in reverse.
72
+ self.rescue_handlers += [[key, options[:with]]]
73
+ end
74
+ end
75
+ end
76
+
77
+ # Tries to rescue the exception by looking up and calling a registered handler.
78
+ def rescue_with_handler(exception)
79
+ if handler = handler_for_rescue(exception)
80
+ handler.arity != 0 ? handler.call(exception) : handler.call
81
+ true # don't rely on the return value of the handler
82
+ end
83
+ end
84
+
85
+ def handler_for_rescue(exception)
86
+ # We go from right to left because pairs are pushed onto rescue_handlers
87
+ # as rescue_from declarations are found.
88
+ _, rescuer = self.class.rescue_handlers.reverse.detect do |klass_name, handler|
89
+ # The purpose of allowing strings in rescue_from is to support the
90
+ # declaration of handler associations for exception classes whose
91
+ # definition is yet unknown.
92
+ #
93
+ # Since this loop needs the constants it would be inconsistent to
94
+ # assume they should exist at this point. An early raised exception
95
+ # could trigger some other handler and the array could include
96
+ # precisely a string whose corresponding constant has not yet been
97
+ # seen. This is why we are tolerant to unknown constants.
98
+ #
99
+ # Note that this tolerance only matters if the exception was given as
100
+ # a string, otherwise a NameError will be raised by the interpreter
101
+ # itself when rescue_from CONSTANT is executed.
102
+ klass = self.class.const_get(klass_name) rescue nil
103
+ klass ||= (klass_name.constantize rescue nil)
104
+ klass === exception if klass
105
+ end
106
+
107
+ case rescuer
108
+ when Symbol
109
+ method(rescuer)
110
+ when Proc
111
+ if rescuer.arity == 0
112
+ Proc.new { instance_exec(&rescuer) }
113
+ else
114
+ Proc.new { |_exception| instance_exec(_exception, &rescuer) }
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end