motion-support 0.1.0 → 0.2.0
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.
- data/.gitignore +3 -0
- data/Gemfile +1 -1
- data/README.md +231 -2
- data/Rakefile +3 -3
- data/app/app_delegate.rb +0 -2
- data/examples/Inflector/.gitignore +16 -0
- data/examples/Inflector/Gemfile +5 -0
- data/examples/Inflector/Rakefile +13 -0
- data/examples/Inflector/app/app_delegate.rb +8 -0
- data/examples/Inflector/app/inflections.rb +5 -0
- data/examples/Inflector/app/inflector_view_controller.rb +120 -0
- data/examples/Inflector/app/words_view_controller.rb +101 -0
- data/examples/Inflector/resources/Default-568h@2x.png +0 -0
- data/examples/Inflector/spec/main_spec.rb +9 -0
- data/lib/motion-support/core_ext/array.rb +13 -0
- data/lib/motion-support/core_ext/class.rb +8 -0
- data/lib/motion-support/core_ext/hash.rb +16 -0
- data/lib/motion-support/core_ext/integer.rb +9 -0
- data/lib/motion-support/core_ext/module.rb +14 -0
- data/lib/motion-support/core_ext/numeric.rb +8 -0
- data/lib/motion-support/core_ext/object.rb +14 -0
- data/lib/motion-support/core_ext/range.rb +8 -0
- data/lib/motion-support/core_ext/string.rb +14 -0
- data/lib/motion-support/core_ext/time.rb +19 -0
- data/lib/motion-support/core_ext.rb +3 -0
- data/lib/motion-support/inflector.rb +12 -156
- data/lib/motion-support.rb +5 -5
- data/motion/_stdlib/cgi.rb +22 -0
- data/motion/_stdlib/date.rb +77 -0
- data/motion/_stdlib/time.rb +19 -0
- data/motion/core_ext/array/access.rb +28 -0
- data/motion/core_ext/array/conversions.rb +86 -0
- data/motion/core_ext/array/extract_options.rb +11 -0
- data/motion/core_ext/array/grouping.rb +99 -0
- data/motion/core_ext/array/prepend_and_append.rb +7 -0
- data/motion/core_ext/array/wrap.rb +45 -0
- data/{lib/motion-support → motion/core_ext}/array.rb +0 -4
- data/motion/core_ext/class/attribute.rb +119 -0
- data/motion/core_ext/class/attribute_accessors.rb +168 -0
- data/motion/core_ext/date/acts_like.rb +8 -0
- data/motion/core_ext/date/calculations.rb +117 -0
- data/motion/core_ext/date/conversions.rb +56 -0
- data/motion/core_ext/date_and_time/calculations.rb +232 -0
- data/motion/core_ext/enumerable.rb +90 -0
- data/motion/core_ext/hash/deep_merge.rb +27 -0
- data/motion/core_ext/hash/except.rb +15 -0
- data/motion/core_ext/hash/indifferent_access.rb +19 -0
- data/motion/core_ext/hash/keys.rb +138 -0
- data/motion/core_ext/hash/reverse_merge.rb +22 -0
- data/motion/core_ext/hash/slice.rb +40 -0
- data/motion/core_ext/integer/inflections.rb +27 -0
- data/motion/core_ext/integer/multiple.rb +10 -0
- data/motion/core_ext/integer/time.rb +41 -0
- data/{lib/motion-support → motion/core_ext}/metaclass.rb +0 -0
- data/motion/core_ext/module/aliasing.rb +69 -0
- data/motion/core_ext/module/anonymous.rb +19 -0
- data/motion/core_ext/module/attr_internal.rb +38 -0
- data/motion/core_ext/module/attribute_accessors.rb +64 -0
- data/motion/core_ext/module/delegation.rb +175 -0
- data/motion/core_ext/module/introspection.rb +60 -0
- data/motion/core_ext/module/reachable.rb +5 -0
- data/motion/core_ext/module/remove_method.rb +12 -0
- data/motion/core_ext/ns_dictionary.rb +11 -0
- data/motion/core_ext/numeric/bytes.rb +44 -0
- data/motion/core_ext/numeric/conversions.rb +7 -0
- data/motion/core_ext/numeric/time.rb +75 -0
- data/motion/core_ext/object/acts_like.rb +10 -0
- data/motion/core_ext/object/blank.rb +105 -0
- data/motion/core_ext/object/deep_dup.rb +44 -0
- data/motion/core_ext/object/duplicable.rb +83 -0
- data/motion/core_ext/object/instance_variables.rb +28 -0
- data/motion/core_ext/object/to_param.rb +58 -0
- data/motion/core_ext/object/to_query.rb +26 -0
- data/motion/core_ext/object/try.rb +78 -0
- data/motion/core_ext/range/include_range.rb +23 -0
- data/motion/core_ext/range/overlaps.rb +8 -0
- data/motion/core_ext/regexp.rb +5 -0
- data/motion/core_ext/string/access.rb +104 -0
- data/motion/core_ext/string/behavior.rb +6 -0
- data/motion/core_ext/string/exclude.rb +11 -0
- data/motion/core_ext/string/filters.rb +55 -0
- data/motion/core_ext/string/indent.rb +43 -0
- data/motion/core_ext/string/inflections.rb +195 -0
- data/motion/core_ext/string/starts_ends_with.rb +4 -0
- data/motion/core_ext/string/strip.rb +24 -0
- data/motion/core_ext/time/acts_like.rb +8 -0
- data/motion/core_ext/time/calculations.rb +215 -0
- data/motion/core_ext/time/conversions.rb +52 -0
- data/motion/duration.rb +104 -0
- data/motion/hash_with_indifferent_access.rb +251 -0
- data/motion/inflections.rb +67 -0
- data/motion/inflector/inflections.rb +203 -0
- data/motion/inflector/methods.rb +321 -0
- data/{lib/motion-support → motion}/logger.rb +0 -0
- data/{lib/motion-support → motion}/version.rb +1 -1
- data/motion-support.gemspec +2 -2
- data/spec/motion-support/_helpers/constantize_test_cases.rb +75 -0
- data/spec/motion-support/_helpers/inflector_test_cases.rb +313 -0
- data/spec/motion-support/core_ext/array/access_spec.rb +29 -0
- data/spec/motion-support/core_ext/array/conversion_spec.rb +60 -0
- data/spec/motion-support/core_ext/array/extract_options_spec.rb +15 -0
- data/spec/motion-support/core_ext/array/grouping_spec.rb +85 -0
- data/spec/motion-support/core_ext/array/prepend_and_append_spec.rb +25 -0
- data/spec/motion-support/core_ext/array/wrap_spec.rb +19 -0
- data/spec/motion-support/{array_spec.rb → core_ext/array_spec.rb} +0 -5
- data/spec/motion-support/core_ext/class/attribute_accessor_spec.rb +127 -0
- data/spec/motion-support/core_ext/class/attribute_spec.rb +92 -0
- data/spec/motion-support/core_ext/date/acts_like_spec.rb +11 -0
- data/spec/motion-support/core_ext/date/calculation_spec.rb +186 -0
- data/spec/motion-support/core_ext/date/conversion_spec.rb +18 -0
- data/spec/motion-support/core_ext/date_and_time/calculation_spec.rb +336 -0
- data/spec/motion-support/core_ext/enumerable_spec.rb +130 -0
- data/spec/motion-support/core_ext/hash/deep_merge_spec.rb +32 -0
- data/spec/motion-support/core_ext/hash/except_spec.rb +43 -0
- data/spec/motion-support/core_ext/hash/key_spec.rb +230 -0
- data/spec/motion-support/core_ext/hash/reverse_merge_spec.rb +26 -0
- data/spec/motion-support/core_ext/hash/slice_spec.rb +61 -0
- data/spec/motion-support/core_ext/integer/inflection_spec.rb +23 -0
- data/spec/motion-support/core_ext/integer/multiple_spec.rb +19 -0
- data/spec/motion-support/{metaclass_spec.rb → core_ext/metaclass_spec.rb} +0 -0
- data/spec/motion-support/core_ext/module/aliasing_spec.rb +143 -0
- data/spec/motion-support/core_ext/module/anonymous_spec.rb +29 -0
- data/spec/motion-support/core_ext/module/attr_internal_spec.rb +104 -0
- data/spec/motion-support/core_ext/module/attribute_accessor_spec.rb +86 -0
- data/spec/motion-support/core_ext/module/delegation_spec.rb +136 -0
- data/spec/motion-support/core_ext/module/introspection_spec.rb +70 -0
- data/spec/motion-support/core_ext/module/reachable_spec.rb +61 -0
- data/spec/motion-support/core_ext/module/remove_method_spec.rb +25 -0
- data/spec/motion-support/core_ext/numeric/bytes_spec.rb +43 -0
- data/spec/motion-support/core_ext/object/acts_like_spec.rb +21 -0
- data/spec/motion-support/core_ext/object/blank_spec.rb +54 -0
- data/spec/motion-support/core_ext/object/deep_dup_spec.rb +54 -0
- data/spec/motion-support/core_ext/object/duplicable_spec.rb +31 -0
- data/spec/motion-support/core_ext/object/instance_variable_spec.rb +19 -0
- data/spec/motion-support/core_ext/object/to_param_spec.rb +75 -0
- data/spec/motion-support/core_ext/object/to_query_spec.rb +37 -0
- data/spec/motion-support/core_ext/object/try_spec.rb +92 -0
- data/spec/motion-support/core_ext/range/include_range_spec.rb +31 -0
- data/spec/motion-support/core_ext/range/overlap_spec.rb +43 -0
- data/spec/motion-support/core_ext/regexp_spec.rb +7 -0
- data/spec/motion-support/core_ext/string/access_spec.rb +53 -0
- data/spec/motion-support/core_ext/string/behavior_spec.rb +7 -0
- data/spec/motion-support/core_ext/string/exclude_spec.rb +8 -0
- data/spec/motion-support/core_ext/string/filter_spec.rb +48 -0
- data/spec/motion-support/core_ext/string/indent_spec.rb +56 -0
- data/spec/motion-support/core_ext/string/inflection_spec.rb +142 -0
- data/spec/motion-support/core_ext/string/starts_end_with_spec.rb +14 -0
- data/spec/motion-support/core_ext/string/strip_spec.rb +34 -0
- data/spec/motion-support/core_ext/string_spec.rb +88 -0
- data/spec/motion-support/core_ext/time/acts_like_spec.rb +11 -0
- data/spec/motion-support/core_ext/time/calculation_spec.rb +201 -0
- data/spec/motion-support/core_ext/time/conversion_spec.rb +54 -0
- data/spec/motion-support/duration_spec.rb +107 -0
- data/spec/motion-support/hash_with_indifferent_access_spec.rb +605 -0
- data/spec/motion-support/inflector_spec.rb +474 -35
- data/spec/motion-support/ns_dictionary_spec.rb +29 -0
- metadata +212 -35
- data/lib/motion-support/cattr_accessor.rb +0 -19
- data/lib/motion-support/class_inheritable_accessor.rb +0 -23
- data/lib/motion-support/class_inheritable_array.rb +0 -29
- data/lib/motion-support/hash.rb +0 -31
- data/lib/motion-support/nilclass.rb +0 -5
- data/lib/motion-support/object.rb +0 -17
- data/lib/motion-support/string.rb +0 -71
- data/spec/motion-support/cattr_accessor_spec.rb +0 -49
- data/spec/motion-support/class_inheritable_accessor_spec.rb +0 -49
- data/spec/motion-support/class_inheritable_array_spec.rb +0 -61
- data/spec/motion-support/hash_spec.rb +0 -31
- data/spec/motion-support/nilclass_spec.rb +0 -5
- data/spec/motion-support/object_spec.rb +0 -43
- data/spec/motion-support/string_spec.rb +0 -145
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
class Object
|
|
2
|
+
# Alias of <tt>to_s</tt>.
|
|
3
|
+
def to_param
|
|
4
|
+
to_s
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
class NilClass
|
|
9
|
+
# Returns +self+.
|
|
10
|
+
def to_param
|
|
11
|
+
self
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class TrueClass
|
|
16
|
+
# Returns +self+.
|
|
17
|
+
def to_param
|
|
18
|
+
self
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class FalseClass
|
|
23
|
+
# Returns +self+.
|
|
24
|
+
def to_param
|
|
25
|
+
self
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class Array
|
|
30
|
+
# Calls <tt>to_param</tt> on all its elements and joins the result with
|
|
31
|
+
# slashes. This is used by <tt>url_for</tt> in Action Pack.
|
|
32
|
+
def to_param
|
|
33
|
+
collect { |e| e.to_param }.join '/'
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class Hash
|
|
38
|
+
# Returns a string representation of the receiver suitable for use as a URL
|
|
39
|
+
# query string:
|
|
40
|
+
#
|
|
41
|
+
# {name: 'David', nationality: 'Danish'}.to_param
|
|
42
|
+
# # => "name=David&nationality=Danish"
|
|
43
|
+
#
|
|
44
|
+
# An optional namespace can be passed to enclose the param names:
|
|
45
|
+
#
|
|
46
|
+
# {name: 'David', nationality: 'Danish'}.to_param('user')
|
|
47
|
+
# # => "user[name]=David&user[nationality]=Danish"
|
|
48
|
+
#
|
|
49
|
+
# The string pairs "key=value" that conform the query string
|
|
50
|
+
# are sorted lexicographically in ascending order.
|
|
51
|
+
#
|
|
52
|
+
# This method is also aliased as +to_query+.
|
|
53
|
+
def to_param(namespace = nil)
|
|
54
|
+
collect do |key, value|
|
|
55
|
+
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
|
|
56
|
+
end.sort * '&'
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
motion_require 'to_param'
|
|
2
|
+
|
|
3
|
+
class Object
|
|
4
|
+
# Converts an object into a string suitable for use as a URL query string, using the given <tt>key</tt> as the
|
|
5
|
+
# param name.
|
|
6
|
+
#
|
|
7
|
+
# Note: This method is defined as a default implementation for all Objects for Hash#to_query to work.
|
|
8
|
+
def to_query(key)
|
|
9
|
+
"#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class Array
|
|
14
|
+
# Converts an array into a string suitable for use as a URL query string,
|
|
15
|
+
# using the given +key+ as the param name.
|
|
16
|
+
#
|
|
17
|
+
# ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
|
|
18
|
+
def to_query(key)
|
|
19
|
+
prefix = "#{key}[]"
|
|
20
|
+
collect { |value| value.to_query(prefix) }.join '&'
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class Hash
|
|
25
|
+
alias_method :to_query, :to_param
|
|
26
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
class Object
|
|
2
|
+
# Invokes the public method whose name goes as first argument just like
|
|
3
|
+
# +public_send+ does, except that if the receiver does not respond to it the
|
|
4
|
+
# call returns +nil+ rather than raising an exception.
|
|
5
|
+
#
|
|
6
|
+
# This method is defined to be able to write
|
|
7
|
+
#
|
|
8
|
+
# @person.try(:name)
|
|
9
|
+
#
|
|
10
|
+
# instead of
|
|
11
|
+
#
|
|
12
|
+
# @person ? @person.name : nil
|
|
13
|
+
#
|
|
14
|
+
# +try+ returns +nil+ when called on +nil+ regardless of whether it responds
|
|
15
|
+
# to the method:
|
|
16
|
+
#
|
|
17
|
+
# nil.try(:to_i) # => nil, rather than 0
|
|
18
|
+
#
|
|
19
|
+
# Arguments and blocks are forwarded to the method if invoked:
|
|
20
|
+
#
|
|
21
|
+
# @posts.try(:each_slice, 2) do |a, b|
|
|
22
|
+
# ...
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# The number of arguments in the signature must match. If the object responds
|
|
26
|
+
# to the method the call is attempted and +ArgumentError+ is still raised
|
|
27
|
+
# otherwise.
|
|
28
|
+
#
|
|
29
|
+
# If +try+ is called without arguments it yields the receiver to a given
|
|
30
|
+
# block unless it is +nil+:
|
|
31
|
+
#
|
|
32
|
+
# @person.try do |p|
|
|
33
|
+
# ...
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
# Please also note that +try+ is defined on +Object+, therefore it won't work
|
|
37
|
+
# with instances of classes that do not have +Object+ among their ancestors,
|
|
38
|
+
# like direct subclasses of +BasicObject+. For example, using +try+ with
|
|
39
|
+
# +SimpleDelegator+ will delegate +try+ to the target instead of calling it on
|
|
40
|
+
# delegator itself.
|
|
41
|
+
def try(*a, &b)
|
|
42
|
+
if a.empty? && block_given?
|
|
43
|
+
yield self
|
|
44
|
+
else
|
|
45
|
+
public_send(*a, &b) if respond_to?(a.first)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Same as #try, but will raise a NoMethodError exception if the receiving is not nil and
|
|
50
|
+
# does not implemented the tried method.
|
|
51
|
+
def try!(*a, &b)
|
|
52
|
+
if a.empty? && block_given?
|
|
53
|
+
yield self
|
|
54
|
+
else
|
|
55
|
+
public_send(*a, &b)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class NilClass
|
|
61
|
+
# Calling +try+ on +nil+ always returns +nil+.
|
|
62
|
+
# It becomes specially helpful when navigating through associations that may return +nil+.
|
|
63
|
+
#
|
|
64
|
+
# nil.try(:name) # => nil
|
|
65
|
+
#
|
|
66
|
+
# Without +try+
|
|
67
|
+
# @person && !@person.children.blank? && @person.children.first.name
|
|
68
|
+
#
|
|
69
|
+
# With +try+
|
|
70
|
+
# @person.try(:children).try(:first).try(:name)
|
|
71
|
+
def try(*args)
|
|
72
|
+
nil
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def try!(*args)
|
|
76
|
+
nil
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
motion_require "../module/aliasing"
|
|
2
|
+
|
|
3
|
+
class Range
|
|
4
|
+
# Extends the default Range#include? to support range comparisons.
|
|
5
|
+
# (1..5).include?(1..5) # => true
|
|
6
|
+
# (1..5).include?(2..3) # => true
|
|
7
|
+
# (1..5).include?(2..6) # => false
|
|
8
|
+
#
|
|
9
|
+
# The native Range#include? behavior is untouched.
|
|
10
|
+
# ('a'..'f').include?('c') # => true
|
|
11
|
+
# (5..9).include?(11) # => false
|
|
12
|
+
def include_with_range?(value)
|
|
13
|
+
if value.is_a?(::Range)
|
|
14
|
+
# 1...10 includes 1..9 but it does not include 1..10.
|
|
15
|
+
operator = exclude_end? && !value.exclude_end? ? :< : :<=
|
|
16
|
+
include_without_range?(value.first) && value.last.send(operator, last)
|
|
17
|
+
else
|
|
18
|
+
include_without_range?(value)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
alias_method_chain :include?, :range
|
|
23
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
class String
|
|
2
|
+
# If you pass a single Fixnum, returns a substring of one character at that
|
|
3
|
+
# position. The first character of the string is at position 0, the next at
|
|
4
|
+
# position 1, and so on. If a range is supplied, a substring containing
|
|
5
|
+
# characters at offsets given by the range is returned. In both cases, if an
|
|
6
|
+
# offset is negative, it is counted from the end of the string. Returns nil
|
|
7
|
+
# if the initial offset falls outside the string. Returns an empty string if
|
|
8
|
+
# the beginning of the range is greater than the end of the string.
|
|
9
|
+
#
|
|
10
|
+
# str = "hello"
|
|
11
|
+
# str.at(0) #=> "h"
|
|
12
|
+
# str.at(1..3) #=> "ell"
|
|
13
|
+
# str.at(-2) #=> "l"
|
|
14
|
+
# str.at(-2..-1) #=> "lo"
|
|
15
|
+
# str.at(5) #=> nil
|
|
16
|
+
# str.at(5..-1) #=> ""
|
|
17
|
+
#
|
|
18
|
+
# If a Regexp is given, the matching portion of the string is returned.
|
|
19
|
+
# If a String is given, that given string is returned if it occurs in
|
|
20
|
+
# the string. In both cases, nil is returned if there is no match.
|
|
21
|
+
#
|
|
22
|
+
# str = "hello"
|
|
23
|
+
# str.at(/lo/) #=> "lo"
|
|
24
|
+
# str.at(/ol/) #=> nil
|
|
25
|
+
# str.at("lo") #=> "lo"
|
|
26
|
+
# str.at("ol") #=> nil
|
|
27
|
+
def at(position)
|
|
28
|
+
self[position]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Returns a substring from the given position to the end of the string.
|
|
32
|
+
# If the position is negative, it is counted from the end of the string.
|
|
33
|
+
#
|
|
34
|
+
# str = "hello"
|
|
35
|
+
# str.from(0) #=> "hello"
|
|
36
|
+
# str.from(3) #=> "lo"
|
|
37
|
+
# str.from(-2) #=> "lo"
|
|
38
|
+
#
|
|
39
|
+
# You can mix it with +to+ method and do fun things like:
|
|
40
|
+
#
|
|
41
|
+
# str = "hello"
|
|
42
|
+
# str.from(0).to(-1) #=> "hello"
|
|
43
|
+
# str.from(1).to(-2) #=> "ell"
|
|
44
|
+
def from(position)
|
|
45
|
+
self[position..-1]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Returns a substring from the beginning of the string to the given position.
|
|
49
|
+
# If the position is negative, it is counted from the end of the string.
|
|
50
|
+
#
|
|
51
|
+
# str = "hello"
|
|
52
|
+
# str.to(0) #=> "h"
|
|
53
|
+
# str.to(3) #=> "hell"
|
|
54
|
+
# str.to(-2) #=> "hell"
|
|
55
|
+
#
|
|
56
|
+
# You can mix it with +from+ method and do fun things like:
|
|
57
|
+
#
|
|
58
|
+
# str = "hello"
|
|
59
|
+
# str.from(0).to(-1) #=> "hello"
|
|
60
|
+
# str.from(1).to(-2) #=> "ell"
|
|
61
|
+
def to(position)
|
|
62
|
+
self[0..position]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Returns the first character. If a limit is supplied, returns a substring
|
|
66
|
+
# from the beginning of the string until it reaches the limit value. If the
|
|
67
|
+
# given limit is greater than or equal to the string length, returns self.
|
|
68
|
+
#
|
|
69
|
+
# str = "hello"
|
|
70
|
+
# str.first #=> "h"
|
|
71
|
+
# str.first(1) #=> "h"
|
|
72
|
+
# str.first(2) #=> "he"
|
|
73
|
+
# str.first(0) #=> ""
|
|
74
|
+
# str.first(6) #=> "hello"
|
|
75
|
+
def first(limit = 1)
|
|
76
|
+
if limit == 0
|
|
77
|
+
''
|
|
78
|
+
elsif limit >= size
|
|
79
|
+
self
|
|
80
|
+
else
|
|
81
|
+
to(limit - 1)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Returns the last character of the string. If a limit is supplied, returns a substring
|
|
86
|
+
# from the end of the string until it reaches the limit value (counting backwards). If
|
|
87
|
+
# the given limit is greater than or equal to the string length, returns self.
|
|
88
|
+
#
|
|
89
|
+
# str = "hello"
|
|
90
|
+
# str.last #=> "o"
|
|
91
|
+
# str.last(1) #=> "o"
|
|
92
|
+
# str.last(2) #=> "lo"
|
|
93
|
+
# str.last(0) #=> ""
|
|
94
|
+
# str.last(6) #=> "hello"
|
|
95
|
+
def last(limit = 1)
|
|
96
|
+
if limit == 0
|
|
97
|
+
''
|
|
98
|
+
elsif limit >= size
|
|
99
|
+
self
|
|
100
|
+
else
|
|
101
|
+
from(-limit)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class String
|
|
2
|
+
# The inverse of <tt>String#include?</tt>. Returns true if the string
|
|
3
|
+
# does not include the other string.
|
|
4
|
+
#
|
|
5
|
+
# "hello".exclude? "lo" #=> false
|
|
6
|
+
# "hello".exclude? "ol" #=> true
|
|
7
|
+
# "hello".exclude? ?h #=> false
|
|
8
|
+
def exclude?(string)
|
|
9
|
+
!include?(string)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
class String
|
|
2
|
+
# Returns the string, first removing all whitespace on both ends of
|
|
3
|
+
# the string, and then changing remaining consecutive whitespace
|
|
4
|
+
# groups into one space each.
|
|
5
|
+
#
|
|
6
|
+
# Note that it handles both ASCII and Unicode whitespace like mongolian vowel separator (U+180E).
|
|
7
|
+
#
|
|
8
|
+
# %{ Multi-line
|
|
9
|
+
# string }.squish # => "Multi-line string"
|
|
10
|
+
# " foo bar \n \t boo".squish # => "foo bar boo"
|
|
11
|
+
def squish
|
|
12
|
+
dup.squish!
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Performs a destructive squish. See String#squish.
|
|
16
|
+
def squish!
|
|
17
|
+
gsub!(/\A[[:space:]]+/, '')
|
|
18
|
+
gsub!(/[[:space:]]+\z/, '')
|
|
19
|
+
gsub!(/[[:space:]]+/, ' ')
|
|
20
|
+
self
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>:
|
|
24
|
+
#
|
|
25
|
+
# 'Once upon a time in a world far far away'.truncate(27)
|
|
26
|
+
# # => "Once upon a time in a wo..."
|
|
27
|
+
#
|
|
28
|
+
# Pass a string or regexp <tt>:separator</tt> to truncate +text+ at a natural break:
|
|
29
|
+
#
|
|
30
|
+
# 'Once upon a time in a world far far away'.truncate(27, separator: ' ')
|
|
31
|
+
# # => "Once upon a time in a..."
|
|
32
|
+
#
|
|
33
|
+
# 'Once upon a time in a world far far away'.truncate(27, separator: /\s/)
|
|
34
|
+
# # => "Once upon a time in a..."
|
|
35
|
+
#
|
|
36
|
+
# The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...")
|
|
37
|
+
# for a total length not exceeding <tt>length</tt>:
|
|
38
|
+
#
|
|
39
|
+
# 'And they found that many people were sleeping better.'.truncate(25, omission: '... (continued)')
|
|
40
|
+
# # => "And they f... (continued)"
|
|
41
|
+
def truncate(truncate_at, options = {})
|
|
42
|
+
return dup unless length > truncate_at
|
|
43
|
+
|
|
44
|
+
options[:omission] ||= '...'
|
|
45
|
+
length_with_room_for_omission = truncate_at - options[:omission].length
|
|
46
|
+
stop = \
|
|
47
|
+
if options[:separator]
|
|
48
|
+
rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission
|
|
49
|
+
else
|
|
50
|
+
length_with_room_for_omission
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
self[0...stop] + options[:omission]
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
class String
|
|
2
|
+
# Same as +indent+, except it indents the receiver in-place.
|
|
3
|
+
#
|
|
4
|
+
# Returns the indented string, or +nil+ if there was nothing to indent.
|
|
5
|
+
def indent!(amount, indent_string=nil, indent_empty_lines=false)
|
|
6
|
+
indent_string = indent_string || self[/^[ \t]/] || ' '
|
|
7
|
+
re = indent_empty_lines ? /^/ : /^(?!$)/
|
|
8
|
+
gsub!(re, indent_string * amount)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Indents the lines in the receiver:
|
|
12
|
+
#
|
|
13
|
+
# <<EOS.indent(2)
|
|
14
|
+
# def some_method
|
|
15
|
+
# some_code
|
|
16
|
+
# end
|
|
17
|
+
# EOS
|
|
18
|
+
# # =>
|
|
19
|
+
# def some_method
|
|
20
|
+
# some_code
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# The second argument, +indent_string+, specifies which indent string to
|
|
24
|
+
# use. The default is +nil+, which tells the method to make a guess by
|
|
25
|
+
# peeking at the first indented line, and fallback to a space if there is
|
|
26
|
+
# none.
|
|
27
|
+
#
|
|
28
|
+
# " foo".indent(2) # => " foo"
|
|
29
|
+
# "foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar"
|
|
30
|
+
# "foo".indent(2, "\t") # => "\t\tfoo"
|
|
31
|
+
#
|
|
32
|
+
# While +indent_string+ is typically one space or tab, it may be any string.
|
|
33
|
+
#
|
|
34
|
+
# The third argument, +indent_empty_lines+, is a flag that says whether
|
|
35
|
+
# empty lines should be indented. Default is false.
|
|
36
|
+
#
|
|
37
|
+
# "foo\n\nbar".indent(2) # => " foo\n\n bar"
|
|
38
|
+
# "foo\n\nbar".indent(2, nil, true) # => " foo\n \n bar"
|
|
39
|
+
#
|
|
40
|
+
def indent(amount, indent_string=nil, indent_empty_lines=false)
|
|
41
|
+
dup.tap {|_| _.indent!(amount, indent_string, indent_empty_lines)}
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# String inflections define new methods on the String class to transform names for different purposes.
|
|
2
|
+
# For instance, you can figure out the name of a table from the name of a class.
|
|
3
|
+
#
|
|
4
|
+
# 'ScaleScore'.tableize # => "scale_scores"
|
|
5
|
+
#
|
|
6
|
+
class String
|
|
7
|
+
# Returns the plural form of the word in the string.
|
|
8
|
+
#
|
|
9
|
+
# If the optional parameter +count+ is specified,
|
|
10
|
+
# the singular form will be returned if <tt>count == 1</tt>.
|
|
11
|
+
# For any other value of +count+ the plural will be returned.
|
|
12
|
+
#
|
|
13
|
+
# 'post'.pluralize # => "posts"
|
|
14
|
+
# 'octopus'.pluralize # => "octopi"
|
|
15
|
+
# 'sheep'.pluralize # => "sheep"
|
|
16
|
+
# 'words'.pluralize # => "words"
|
|
17
|
+
# 'the blue mailman'.pluralize # => "the blue mailmen"
|
|
18
|
+
# 'CamelOctopus'.pluralize # => "CamelOctopi"
|
|
19
|
+
# 'apple'.pluralize(1) # => "apple"
|
|
20
|
+
# 'apple'.pluralize(2) # => "apples"
|
|
21
|
+
def pluralize(count = nil)
|
|
22
|
+
if count == 1
|
|
23
|
+
self
|
|
24
|
+
else
|
|
25
|
+
MotionSupport::Inflector.pluralize(self)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# The reverse of +pluralize+, returns the singular form of a word in a string.
|
|
30
|
+
#
|
|
31
|
+
# 'posts'.singularize # => "post"
|
|
32
|
+
# 'octopi'.singularize # => "octopus"
|
|
33
|
+
# 'sheep'.singularize # => "sheep"
|
|
34
|
+
# 'word'.singularize # => "word"
|
|
35
|
+
# 'the blue mailmen'.singularize # => "the blue mailman"
|
|
36
|
+
# 'CamelOctopi'.singularize # => "CamelOctopus"
|
|
37
|
+
def singularize
|
|
38
|
+
MotionSupport::Inflector.singularize(self)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# +constantize+ tries to find a declared constant with the name specified
|
|
42
|
+
# in the string. It raises a NameError when the name is not in CamelCase
|
|
43
|
+
# or is not initialized. See MotionSupport::Inflector.constantize
|
|
44
|
+
#
|
|
45
|
+
# 'Module'.constantize # => Module
|
|
46
|
+
# 'Class'.constantize # => Class
|
|
47
|
+
# 'blargle'.constantize # => NameError: wrong constant name blargle
|
|
48
|
+
def constantize
|
|
49
|
+
MotionSupport::Inflector.constantize(self)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# +safe_constantize+ tries to find a declared constant with the name specified
|
|
53
|
+
# in the string. It returns nil when the name is not in CamelCase
|
|
54
|
+
# or is not initialized. See MotionSupport::Inflector.safe_constantize
|
|
55
|
+
#
|
|
56
|
+
# 'Module'.safe_constantize # => Module
|
|
57
|
+
# 'Class'.safe_constantize # => Class
|
|
58
|
+
# 'blargle'.safe_constantize # => nil
|
|
59
|
+
def safe_constantize
|
|
60
|
+
MotionSupport::Inflector.safe_constantize(self)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
|
|
64
|
+
# is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
|
|
65
|
+
#
|
|
66
|
+
# +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
|
|
67
|
+
#
|
|
68
|
+
# 'active_record'.camelize # => "ActiveRecord"
|
|
69
|
+
# 'active_record'.camelize(:lower) # => "activeRecord"
|
|
70
|
+
# 'active_record/errors'.camelize # => "ActiveRecord::Errors"
|
|
71
|
+
# 'active_record/errors'.camelize(:lower) # => "activeRecord::Errors"
|
|
72
|
+
def camelize(first_letter = :upper)
|
|
73
|
+
case first_letter
|
|
74
|
+
when :upper
|
|
75
|
+
MotionSupport::Inflector.camelize(self, true)
|
|
76
|
+
when :lower
|
|
77
|
+
MotionSupport::Inflector.camelize(self, false)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
alias_method :camelcase, :camelize
|
|
81
|
+
|
|
82
|
+
# Capitalizes all the words and replaces some characters in the string to create
|
|
83
|
+
# a nicer looking title. +titleize+ is meant for creating pretty output. It is not
|
|
84
|
+
# used in the Rails internals.
|
|
85
|
+
#
|
|
86
|
+
# +titleize+ is also aliased as +titlecase+.
|
|
87
|
+
#
|
|
88
|
+
# 'man from the boondocks'.titleize # => "Man From The Boondocks"
|
|
89
|
+
# 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
|
|
90
|
+
def titleize
|
|
91
|
+
MotionSupport::Inflector.titleize(self)
|
|
92
|
+
end
|
|
93
|
+
alias_method :titlecase, :titleize
|
|
94
|
+
|
|
95
|
+
# The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
|
|
96
|
+
#
|
|
97
|
+
# +underscore+ will also change '::' to '/' to convert namespaces to paths.
|
|
98
|
+
#
|
|
99
|
+
# 'ActiveModel'.underscore # => "active_model"
|
|
100
|
+
# 'ActiveModel::Errors'.underscore # => "active_model/errors"
|
|
101
|
+
def underscore
|
|
102
|
+
MotionSupport::Inflector.underscore(self)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Replaces underscores with dashes in the string.
|
|
106
|
+
#
|
|
107
|
+
# 'puni_puni'.dasherize # => "puni-puni"
|
|
108
|
+
def dasherize
|
|
109
|
+
MotionSupport::Inflector.dasherize(self)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Removes the module part from the constant expression in the string.
|
|
113
|
+
#
|
|
114
|
+
# 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
|
|
115
|
+
# 'Inflections'.demodulize # => "Inflections"
|
|
116
|
+
#
|
|
117
|
+
# See also +deconstantize+.
|
|
118
|
+
def demodulize
|
|
119
|
+
MotionSupport::Inflector.demodulize(self)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Removes the rightmost segment from the constant expression in the string.
|
|
123
|
+
#
|
|
124
|
+
# 'Net::HTTP'.deconstantize # => "Net"
|
|
125
|
+
# '::Net::HTTP'.deconstantize # => "::Net"
|
|
126
|
+
# 'String'.deconstantize # => ""
|
|
127
|
+
# '::String'.deconstantize # => ""
|
|
128
|
+
# ''.deconstantize # => ""
|
|
129
|
+
#
|
|
130
|
+
# See also +demodulize+.
|
|
131
|
+
def deconstantize
|
|
132
|
+
MotionSupport::Inflector.deconstantize(self)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
|
|
136
|
+
#
|
|
137
|
+
# class Person
|
|
138
|
+
# def to_param
|
|
139
|
+
# "#{id}-#{name.parameterize}"
|
|
140
|
+
# end
|
|
141
|
+
# end
|
|
142
|
+
#
|
|
143
|
+
# @person = Person.find(1)
|
|
144
|
+
# # => #<Person id: 1, name: "Donald E. Knuth">
|
|
145
|
+
#
|
|
146
|
+
# <%= link_to(@person.name, person_path) %>
|
|
147
|
+
# # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
|
|
148
|
+
def parameterize(sep = '-')
|
|
149
|
+
MotionSupport::Inflector.parameterize(self, sep)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Creates the name of a table like Rails does for models to table names. This method
|
|
153
|
+
# uses the +pluralize+ method on the last word in the string.
|
|
154
|
+
#
|
|
155
|
+
# 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
|
|
156
|
+
# 'egg_and_ham'.tableize # => "egg_and_hams"
|
|
157
|
+
# 'fancyCategory'.tableize # => "fancy_categories"
|
|
158
|
+
def tableize
|
|
159
|
+
MotionSupport::Inflector.tableize(self)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Create a class name from a plural table name like Rails does for table names to models.
|
|
163
|
+
# Note that this returns a string and not a class. (To convert to an actual class
|
|
164
|
+
# follow +classify+ with +constantize+.)
|
|
165
|
+
#
|
|
166
|
+
# 'egg_and_hams'.classify # => "EggAndHam"
|
|
167
|
+
# 'posts'.classify # => "Post"
|
|
168
|
+
#
|
|
169
|
+
# Singular names are not handled correctly.
|
|
170
|
+
#
|
|
171
|
+
# 'business'.classify # => "Busines"
|
|
172
|
+
def classify
|
|
173
|
+
MotionSupport::Inflector.classify(self)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Capitalizes the first word, turns underscores into spaces, and strips '_id'.
|
|
177
|
+
# Like +titleize+, this is meant for creating pretty output.
|
|
178
|
+
#
|
|
179
|
+
# 'employee_salary'.humanize # => "Employee salary"
|
|
180
|
+
# 'author_id'.humanize # => "Author"
|
|
181
|
+
def humanize
|
|
182
|
+
MotionSupport::Inflector.humanize(self)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Creates a foreign key name from a class name.
|
|
186
|
+
# +separate_class_name_and_id_with_underscore+ sets whether
|
|
187
|
+
# the method should put '_' between the name and 'id'.
|
|
188
|
+
#
|
|
189
|
+
# 'Message'.foreign_key # => "message_id"
|
|
190
|
+
# 'Message'.foreign_key(false) # => "messageid"
|
|
191
|
+
# 'Admin::Post'.foreign_key # => "post_id"
|
|
192
|
+
def foreign_key(separate_class_name_and_id_with_underscore = true)
|
|
193
|
+
MotionSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
class String
|
|
2
|
+
# Strips indentation in heredocs.
|
|
3
|
+
#
|
|
4
|
+
# For example in
|
|
5
|
+
#
|
|
6
|
+
# if options[:usage]
|
|
7
|
+
# puts <<-USAGE.strip_heredoc
|
|
8
|
+
# This command does such and such.
|
|
9
|
+
#
|
|
10
|
+
# Supported options are:
|
|
11
|
+
# -h This message
|
|
12
|
+
# ...
|
|
13
|
+
# USAGE
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# the user would see the usage message aligned against the left margin.
|
|
17
|
+
#
|
|
18
|
+
# Technically, it looks for the least indented line in the whole string, and removes
|
|
19
|
+
# that amount of leading whitespace.
|
|
20
|
+
def strip_heredoc
|
|
21
|
+
indent = scan(/^[ \t]*(?=\S)/).min.try(:size) || 0
|
|
22
|
+
gsub(/^[ \t]{#{indent}}/, '')
|
|
23
|
+
end
|
|
24
|
+
end
|