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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 87d93ff39aee4506fbf6be9ce91a4c8e607e49aa
4
+ data.tar.gz: f8be51872fac5c526fca972e2e022ee0c31a2cb6
5
+ SHA512:
6
+ metadata.gz: 5ec18c6158abd27e745625af270dadf2e0ba817255dab84ea2a799ddb6b52241061e43d89c083b4398e8c4dfe11a71152c1f3b32b8a78b4e3986769e270959a1
7
+ data.tar.gz: 5091f649d5f221d95b2ce6256fe0e808fbda8c109d0eae0d8c6eab196904d146047683219796a8f795a08a3c29a5c8ccd465794ad4acd50bb464b384d85966ab
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ ## core_ext
2
+
3
+ ActiveSupport's core_ext without ActiveSupport dependency
@@ -0,0 +1,76 @@
1
+ class Array
2
+ # Returns the tail of the array from +position+.
3
+ #
4
+ # %w( a b c d ).from(0) # => ["a", "b", "c", "d"]
5
+ # %w( a b c d ).from(2) # => ["c", "d"]
6
+ # %w( a b c d ).from(10) # => []
7
+ # %w().from(0) # => []
8
+ # %w( a b c d ).from(-2) # => ["c", "d"]
9
+ # %w( a b c ).from(-10) # => []
10
+ def from(position)
11
+ self[position, length] || []
12
+ end
13
+
14
+ # Returns the beginning of the array up to +position+.
15
+ #
16
+ # %w( a b c d ).to(0) # => ["a"]
17
+ # %w( a b c d ).to(2) # => ["a", "b", "c"]
18
+ # %w( a b c d ).to(10) # => ["a", "b", "c", "d"]
19
+ # %w().to(0) # => []
20
+ # %w( a b c d ).to(-2) # => ["a", "b", "c"]
21
+ # %w( a b c ).to(-10) # => []
22
+ def to(position)
23
+ if position >= 0
24
+ take position + 1
25
+ else
26
+ self[0..position]
27
+ end
28
+ end
29
+
30
+ # Returns a copy of the Array without the specified elements.
31
+ #
32
+ # people = ["David", "Rafael", "Aaron", "Todd"]
33
+ # people.without "Aaron", "Todd"
34
+ # => ["David", "Rafael"]
35
+ #
36
+ # Note: This is an optimization of `Enumerable#without` that uses `Array#-`
37
+ # instead of `Array#reject` for performance reasons.
38
+ def without(*elements)
39
+ self - elements
40
+ end
41
+
42
+ # Equal to <tt>self[1]</tt>.
43
+ #
44
+ # %w( a b c d e ).second # => "b"
45
+ def second
46
+ self[1]
47
+ end
48
+
49
+ # Equal to <tt>self[2]</tt>.
50
+ #
51
+ # %w( a b c d e ).third # => "c"
52
+ def third
53
+ self[2]
54
+ end
55
+
56
+ # Equal to <tt>self[3]</tt>.
57
+ #
58
+ # %w( a b c d e ).fourth # => "d"
59
+ def fourth
60
+ self[3]
61
+ end
62
+
63
+ # Equal to <tt>self[4]</tt>.
64
+ #
65
+ # %w( a b c d e ).fifth # => "e"
66
+ def fifth
67
+ self[4]
68
+ end
69
+
70
+ # Equal to <tt>self[41]</tt>. Also known as accessing "the reddit".
71
+ #
72
+ # (1..42).to_a.forty_two # => 42
73
+ def forty_two
74
+ self[41]
75
+ end
76
+ end
@@ -0,0 +1,211 @@
1
+ require 'core_ext/xml_mini'
2
+ require 'core_ext/hash/keys'
3
+ require 'core_ext/string/inflections'
4
+ require 'core_ext/object/to_param'
5
+ require 'core_ext/object/to_query'
6
+
7
+ class Array
8
+ # Converts the array to a comma-separated sentence where the last element is
9
+ # joined by the connector word.
10
+ #
11
+ # You can pass the following options to change the default behavior. If you
12
+ # pass an option key that doesn't exist in the list below, it will raise an
13
+ # <tt>ArgumentError</tt>.
14
+ #
15
+ # ==== Options
16
+ #
17
+ # * <tt>:words_connector</tt> - The sign or word used to join the elements
18
+ # in arrays with two or more elements (default: ", ").
19
+ # * <tt>:two_words_connector</tt> - The sign or word used to join the elements
20
+ # in arrays with two elements (default: " and ").
21
+ # * <tt>:last_word_connector</tt> - The sign or word used to join the last element
22
+ # in arrays with three or more elements (default: ", and ").
23
+ # * <tt>:locale</tt> - If +i18n+ is available, you can set a locale and use
24
+ # the connector options defined on the 'support.array' namespace in the
25
+ # corresponding dictionary file.
26
+ #
27
+ # ==== Examples
28
+ #
29
+ # [].to_sentence # => ""
30
+ # ['one'].to_sentence # => "one"
31
+ # ['one', 'two'].to_sentence # => "one and two"
32
+ # ['one', 'two', 'three'].to_sentence # => "one, two, and three"
33
+ #
34
+ # ['one', 'two'].to_sentence(passing: 'invalid option')
35
+ # # => ArgumentError: Unknown key: :passing. Valid keys are: :words_connector, :two_words_connector, :last_word_connector, :locale
36
+ #
37
+ # ['one', 'two'].to_sentence(two_words_connector: '-')
38
+ # # => "one-two"
39
+ #
40
+ # ['one', 'two', 'three'].to_sentence(words_connector: ' or ', last_word_connector: ' or at least ')
41
+ # # => "one or two or at least three"
42
+ #
43
+ # Using <tt>:locale</tt> option:
44
+ #
45
+ # # Given this locale dictionary:
46
+ # #
47
+ # # es:
48
+ # # support:
49
+ # # array:
50
+ # # words_connector: " o "
51
+ # # two_words_connector: " y "
52
+ # # last_word_connector: " o al menos "
53
+ #
54
+ # ['uno', 'dos'].to_sentence(locale: :es)
55
+ # # => "uno y dos"
56
+ #
57
+ # ['uno', 'dos', 'tres'].to_sentence(locale: :es)
58
+ # # => "uno o dos o al menos tres"
59
+ def to_sentence(options = {})
60
+ options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
61
+
62
+ default_connectors = {
63
+ :words_connector => ', ',
64
+ :two_words_connector => ' and ',
65
+ :last_word_connector => ', and '
66
+ }
67
+ if defined?(I18n)
68
+ i18n_connectors = I18n.translate(:'support.array', locale: options[:locale], default: {})
69
+ default_connectors.merge!(i18n_connectors)
70
+ end
71
+ options = default_connectors.merge!(options)
72
+
73
+ case length
74
+ when 0
75
+ ''
76
+ when 1
77
+ "#{self[0]}"
78
+ when 2
79
+ "#{self[0]}#{options[:two_words_connector]}#{self[1]}"
80
+ else
81
+ "#{self[0...-1].join(options[:words_connector])}#{options[:last_word_connector]}#{self[-1]}"
82
+ end
83
+ end
84
+
85
+ # Extends <tt>Array#to_s</tt> to convert a collection of elements into a
86
+ # comma separated id list if <tt>:db</tt> argument is given as the format.
87
+ #
88
+ # Blog.all.to_formatted_s(:db) # => "1,2,3"
89
+ # Blog.none.to_formatted_s(:db) # => "null"
90
+ # [1,2].to_formatted_s # => "[1, 2]"
91
+ def to_formatted_s(format = :default)
92
+ case format
93
+ when :db
94
+ if empty?
95
+ 'null'
96
+ else
97
+ collect(&:id).join(',')
98
+ end
99
+ else
100
+ to_default_s
101
+ end
102
+ end
103
+ alias_method :to_default_s, :to_s
104
+ alias_method :to_s, :to_formatted_s
105
+
106
+ # Returns a string that represents the array in XML by invoking +to_xml+
107
+ # on each element. Active Record collections delegate their representation
108
+ # in XML to this method.
109
+ #
110
+ # All elements are expected to respond to +to_xml+, if any of them does
111
+ # not then an exception is raised.
112
+ #
113
+ # The root node reflects the class name of the first element in plural
114
+ # if all elements belong to the same type and that's not Hash:
115
+ #
116
+ # customer.projects.to_xml
117
+ #
118
+ # <?xml version="1.0" encoding="UTF-8"?>
119
+ # <projects type="array">
120
+ # <project>
121
+ # <amount type="decimal">20000.0</amount>
122
+ # <customer-id type="integer">1567</customer-id>
123
+ # <deal-date type="date">2008-04-09</deal-date>
124
+ # ...
125
+ # </project>
126
+ # <project>
127
+ # <amount type="decimal">57230.0</amount>
128
+ # <customer-id type="integer">1567</customer-id>
129
+ # <deal-date type="date">2008-04-15</deal-date>
130
+ # ...
131
+ # </project>
132
+ # </projects>
133
+ #
134
+ # Otherwise the root element is "objects":
135
+ #
136
+ # [{ foo: 1, bar: 2}, { baz: 3}].to_xml
137
+ #
138
+ # <?xml version="1.0" encoding="UTF-8"?>
139
+ # <objects type="array">
140
+ # <object>
141
+ # <bar type="integer">2</bar>
142
+ # <foo type="integer">1</foo>
143
+ # </object>
144
+ # <object>
145
+ # <baz type="integer">3</baz>
146
+ # </object>
147
+ # </objects>
148
+ #
149
+ # If the collection is empty the root element is "nil-classes" by default:
150
+ #
151
+ # [].to_xml
152
+ #
153
+ # <?xml version="1.0" encoding="UTF-8"?>
154
+ # <nil-classes type="array"/>
155
+ #
156
+ # To ensure a meaningful root element use the <tt>:root</tt> option:
157
+ #
158
+ # customer_with_no_projects.projects.to_xml(root: 'projects')
159
+ #
160
+ # <?xml version="1.0" encoding="UTF-8"?>
161
+ # <projects type="array"/>
162
+ #
163
+ # By default name of the node for the children of root is <tt>root.singularize</tt>.
164
+ # You can change it with the <tt>:children</tt> option.
165
+ #
166
+ # The +options+ hash is passed downwards:
167
+ #
168
+ # Message.all.to_xml(skip_types: true)
169
+ #
170
+ # <?xml version="1.0" encoding="UTF-8"?>
171
+ # <messages>
172
+ # <message>
173
+ # <created-at>2008-03-07T09:58:18+01:00</created-at>
174
+ # <id>1</id>
175
+ # <name>1</name>
176
+ # <updated-at>2008-03-07T09:58:18+01:00</updated-at>
177
+ # <user-id>1</user-id>
178
+ # </message>
179
+ # </messages>
180
+ #
181
+ def to_xml(options = {})
182
+ require 'core_ext/builder' unless defined?(Builder)
183
+
184
+ options = options.dup
185
+ options[:indent] ||= 2
186
+ options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent])
187
+ options[:root] ||= \
188
+ if first.class != Hash && all? { |e| e.is_a?(first.class) }
189
+ underscored = CoreExt::Inflector.underscore(first.class.name)
190
+ CoreExt::Inflector.pluralize(underscored).tr('/', '_')
191
+ else
192
+ 'objects'
193
+ end
194
+
195
+ builder = options[:builder]
196
+ builder.instruct! unless options.delete(:skip_instruct)
197
+
198
+ root = CoreExt::XmlMini.rename_key(options[:root].to_s, options)
199
+ children = options.delete(:children) || root.singularize
200
+ attributes = options[:skip_types] ? {} : { type: 'array' }
201
+
202
+ if empty?
203
+ builder.tag!(root, attributes)
204
+ else
205
+ builder.tag!(root, attributes) do
206
+ each { |value| CoreExt::XmlMini.to_tag(children, value, options) }
207
+ yield builder if block_given?
208
+ end
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,29 @@
1
+ class Hash
2
+ # By default, only instances of Hash itself are extractable.
3
+ # Subclasses of Hash may implement this method and return
4
+ # true to declare themselves as extractable. If a Hash
5
+ # is extractable, Array#extract_options! pops it from
6
+ # the Array when it is the last element of the Array.
7
+ def extractable_options?
8
+ instance_of?(Hash)
9
+ end
10
+ end
11
+
12
+ class Array
13
+ # Extracts options from a set of arguments. Removes and returns the last
14
+ # element in the array if it's a hash, otherwise returns a blank hash.
15
+ #
16
+ # def options(*args)
17
+ # args.extract_options!
18
+ # end
19
+ #
20
+ # options(1, 2) # => {}
21
+ # options(1, 2, a: :b) # => {:a=>:b}
22
+ def extract_options!
23
+ if last.is_a?(Hash) && last.extractable_options?
24
+ pop
25
+ else
26
+ {}
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,116 @@
1
+ class Array
2
+ # Splits or iterates over the array in groups of size +number+,
3
+ # padding any remaining slots with +fill_with+ unless it is +false+.
4
+ #
5
+ # %w(1 2 3 4 5 6 7 8 9 10).in_groups_of(3) {|group| p group}
6
+ # ["1", "2", "3"]
7
+ # ["4", "5", "6"]
8
+ # ["7", "8", "9"]
9
+ # ["10", nil, nil]
10
+ #
11
+ # %w(1 2 3 4 5).in_groups_of(2, '&nbsp;') {|group| p group}
12
+ # ["1", "2"]
13
+ # ["3", "4"]
14
+ # ["5", "&nbsp;"]
15
+ #
16
+ # %w(1 2 3 4 5).in_groups_of(2, false) {|group| p group}
17
+ # ["1", "2"]
18
+ # ["3", "4"]
19
+ # ["5"]
20
+ def in_groups_of(number, fill_with = nil)
21
+ if number.to_i <= 0
22
+ raise ArgumentError,
23
+ "Group size must be a positive integer, was #{number.inspect}"
24
+ end
25
+
26
+ if fill_with == false
27
+ collection = self
28
+ else
29
+ # size % number gives how many extra we have;
30
+ # subtracting from number gives how many to add;
31
+ # modulo number ensures we don't add group of just fill.
32
+ padding = (number - size % number) % number
33
+ collection = dup.concat(Array.new(padding, fill_with))
34
+ end
35
+
36
+ if block_given?
37
+ collection.each_slice(number) { |slice| yield(slice) }
38
+ else
39
+ collection.each_slice(number).to_a
40
+ end
41
+ end
42
+
43
+ # Splits or iterates over the array in +number+ of groups, padding any
44
+ # remaining slots with +fill_with+ unless it is +false+.
45
+ #
46
+ # %w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group}
47
+ # ["1", "2", "3", "4"]
48
+ # ["5", "6", "7", nil]
49
+ # ["8", "9", "10", nil]
50
+ #
51
+ # %w(1 2 3 4 5 6 7 8 9 10).in_groups(3, '&nbsp;') {|group| p group}
52
+ # ["1", "2", "3", "4"]
53
+ # ["5", "6", "7", "&nbsp;"]
54
+ # ["8", "9", "10", "&nbsp;"]
55
+ #
56
+ # %w(1 2 3 4 5 6 7).in_groups(3, false) {|group| p group}
57
+ # ["1", "2", "3"]
58
+ # ["4", "5"]
59
+ # ["6", "7"]
60
+ def in_groups(number, fill_with = nil)
61
+ # size.div number gives minor group size;
62
+ # size % number gives how many objects need extra accommodation;
63
+ # each group hold either division or division + 1 items.
64
+ division = size.div number
65
+ modulo = size % number
66
+
67
+ # create a new array avoiding dup
68
+ groups = []
69
+ start = 0
70
+
71
+ number.times do |index|
72
+ length = division + (modulo > 0 && modulo > index ? 1 : 0)
73
+ groups << last_group = slice(start, length)
74
+ last_group << fill_with if fill_with != false &&
75
+ modulo > 0 && length == division
76
+ start += length
77
+ end
78
+
79
+ if block_given?
80
+ groups.each { |g| yield(g) }
81
+ else
82
+ groups
83
+ end
84
+ end
85
+
86
+ # Divides the array into one or more subarrays based on a delimiting +value+
87
+ # or the result of an optional block.
88
+ #
89
+ # [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]]
90
+ # (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]]
91
+ def split(value = nil)
92
+ if block_given?
93
+ inject([[]]) do |results, element|
94
+ if yield(element)
95
+ results << []
96
+ else
97
+ results.last << element
98
+ end
99
+
100
+ results
101
+ end
102
+ else
103
+ results, arr = [[]], self.dup
104
+ until arr.empty?
105
+ if (idx = arr.index(value))
106
+ results.last.concat(arr.shift(idx))
107
+ arr.shift
108
+ results << []
109
+ else
110
+ results.last.concat(arr.shift(arr.size))
111
+ end
112
+ end
113
+ results
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,17 @@
1
+ require 'core_ext/array_inquirer'
2
+
3
+ class Array
4
+ # Wraps the array in an +ArrayInquirer+ object, which gives a friendlier way
5
+ # to check its string-like contents.
6
+ #
7
+ # pets = [:cat, :dog].inquiry
8
+ #
9
+ # pets.cat? # => true
10
+ # pets.ferret? # => false
11
+ #
12
+ # pets.any?(:cat, :ferret) # => true
13
+ # pets.any?(:ferret, :alligator) # => false
14
+ def inquiry
15
+ CoreExt::ArrayInquirer.new(self)
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ class Array
2
+ # The human way of thinking about adding stuff to the end of a list is with append.
3
+ alias_method :append, :<<
4
+
5
+ # The human way of thinking about adding stuff to the beginning of a list is with prepend.
6
+ alias_method :prepend, :unshift
7
+ end
@@ -0,0 +1,46 @@
1
+ class Array
2
+ # Wraps its argument in an array unless it is already an array (or array-like).
3
+ #
4
+ # Specifically:
5
+ #
6
+ # * If the argument is +nil+ an empty array is returned.
7
+ # * Otherwise, if the argument responds to +to_ary+ it is invoked, and its result returned.
8
+ # * Otherwise, returns an array with the argument as its single element.
9
+ #
10
+ # Array.wrap(nil) # => []
11
+ # Array.wrap([1, 2, 3]) # => [1, 2, 3]
12
+ # Array.wrap(0) # => [0]
13
+ #
14
+ # This method is similar in purpose to <tt>Kernel#Array</tt>, but there are some differences:
15
+ #
16
+ # * If the argument responds to +to_ary+ the method is invoked. <tt>Kernel#Array</tt>
17
+ # moves on to try +to_a+ if the returned value is +nil+, but <tt>Array.wrap</tt> returns
18
+ # an array with the argument as its single element right away.
19
+ # * If the returned value from +to_ary+ is neither +nil+ nor an +Array+ object, <tt>Kernel#Array</tt>
20
+ # raises an exception, while <tt>Array.wrap</tt> does not, it just returns the value.
21
+ # * It does not call +to_a+ on the argument, if the argument does not respond to +to_ary+
22
+ # it returns an array with the argument as its single element.
23
+ #
24
+ # The last point is easily explained with some enumerables:
25
+ #
26
+ # Array(foo: :bar) # => [[:foo, :bar]]
27
+ # Array.wrap(foo: :bar) # => [{:foo=>:bar}]
28
+ #
29
+ # There's also a related idiom that uses the splat operator:
30
+ #
31
+ # [*object]
32
+ #
33
+ # which returns <tt>[]</tt> for +nil+, but calls to <tt>Array(object)</tt> otherwise.
34
+ #
35
+ # The differences with <tt>Kernel#Array</tt> explained above
36
+ # apply to the rest of <tt>object</tt>s.
37
+ def self.wrap(object)
38
+ if object.nil?
39
+ []
40
+ elsif object.respond_to?(:to_ary)
41
+ object.to_ary || [object]
42
+ else
43
+ [object]
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,7 @@
1
+ require 'core_ext/array/wrap'
2
+ require 'core_ext/array/access'
3
+ require 'core_ext/array/conversions'
4
+ require 'core_ext/array/extract_options'
5
+ require 'core_ext/array/grouping'
6
+ require 'core_ext/array/prepend_and_append'
7
+ require 'core_ext/array/inquiry'
@@ -0,0 +1,44 @@
1
+ module CoreExt
2
+ # Wrapping an array in an +ArrayInquirer+ gives a friendlier way to check
3
+ # its string-like contents:
4
+ #
5
+ # variants = CoreExt::ArrayInquirer.new([:phone, :tablet])
6
+ #
7
+ # variants.phone? # => true
8
+ # variants.tablet? # => true
9
+ # variants.desktop? # => false
10
+ class ArrayInquirer < Array
11
+ # Passes each element of +candidates+ collection to ArrayInquirer collection.
12
+ # The method returns true if at least one element is the same. If +candidates+
13
+ # collection is not given, method returns true.
14
+ #
15
+ # variants = CoreExt::ArrayInquirer.new([:phone, :tablet])
16
+ #
17
+ # variants.any? # => true
18
+ # variants.any?(:phone, :tablet) # => true
19
+ # variants.any?('phone', 'desktop') # => true
20
+ # variants.any?(:desktop, :watch) # => false
21
+ def any?(*candidates, &block)
22
+ if candidates.none?
23
+ super
24
+ else
25
+ candidates.any? do |candidate|
26
+ include?(candidate.to_sym) || include?(candidate.to_s)
27
+ end
28
+ end
29
+ end
30
+
31
+ private
32
+ def respond_to_missing?(name, include_private = false)
33
+ name[-1] == '?'
34
+ end
35
+
36
+ def method_missing(name, *args)
37
+ if name[-1] == '?'
38
+ any?(name[0..-2])
39
+ else
40
+ super
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,14 @@
1
+ require 'benchmark'
2
+
3
+ class << Benchmark
4
+ # Benchmark realtime in milliseconds.
5
+ #
6
+ # Benchmark.realtime { User.all }
7
+ # # => 8.0e-05
8
+ #
9
+ # Benchmark.ms { User.all }
10
+ # # => 0.074
11
+ def ms
12
+ 1000 * realtime { yield }
13
+ end
14
+ end
@@ -0,0 +1,49 @@
1
+ require 'core_ext/benchmark'
2
+ require 'core_ext/hash/keys'
3
+
4
+ module CoreExt
5
+ module Benchmarkable
6
+ # Allows you to measure the execution time of a block in a template and
7
+ # records the result to the log. Wrap this block around expensive operations
8
+ # or possible bottlenecks to get a time reading for the operation. For
9
+ # example, let's say you thought your file processing method was taking too
10
+ # long; you could wrap it in a benchmark block.
11
+ #
12
+ # <% benchmark 'Process data files' do %>
13
+ # <%= expensive_files_operation %>
14
+ # <% end %>
15
+ #
16
+ # That would add something like "Process data files (345.2ms)" to the log,
17
+ # which you can then use to compare timings when optimizing your code.
18
+ #
19
+ # You may give an optional logger level (<tt>:debug</tt>, <tt>:info</tt>,
20
+ # <tt>:warn</tt>, <tt>:error</tt>) as the <tt>:level</tt> option. The
21
+ # default logger level value is <tt>:info</tt>.
22
+ #
23
+ # <% benchmark 'Low-level files', level: :debug do %>
24
+ # <%= lowlevel_files_operation %>
25
+ # <% end %>
26
+ #
27
+ # Finally, you can pass true as the third argument to silence all log
28
+ # activity (other than the timing information) from inside the block. This
29
+ # is great for boiling down a noisy block to just a single statement that
30
+ # produces one log line:
31
+ #
32
+ # <% benchmark 'Process data files', level: :info, silence: true do %>
33
+ # <%= expensive_and_chatty_files_operation %>
34
+ # <% end %>
35
+ def benchmark(message = "Benchmarking", options = {})
36
+ if logger
37
+ options.assert_valid_keys(:level, :silence)
38
+ options[:level] ||= :info
39
+
40
+ result = nil
41
+ ms = Benchmark.ms { result = options[:silence] ? silence { yield } : yield }
42
+ logger.send(options[:level], '%s (%.1fms)' % [ message, ms ])
43
+ result
44
+ else
45
+ yield
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,14 @@
1
+ require 'bigdecimal'
2
+ require 'bigdecimal/util'
3
+
4
+ module CoreExt
5
+ module BigDecimalWithDefaultFormat #:nodoc:
6
+ DEFAULT_STRING_FORMAT = 'F'
7
+
8
+ def to_s(format = nil)
9
+ super(format || DEFAULT_STRING_FORMAT)
10
+ end
11
+ end
12
+ end
13
+
14
+ BigDecimal.prepend(CoreExt::BigDecimalWithDefaultFormat)
@@ -0,0 +1 @@
1
+ require 'core_ext/big_decimal/conversions'
@@ -0,0 +1,6 @@
1
+ begin
2
+ require 'builder'
3
+ rescue LoadError => e
4
+ $stderr.puts "You don't have builder installed in your application. Please add it to your Gemfile and run bundle install"
5
+ raise e
6
+ end