shattered_support 0.3.2 → 0.3.3

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.
@@ -0,0 +1,44 @@
1
+ # Extends the class object with class and instance accessors for class attributes,
2
+ # just like the native attr* accessors for instance attributes.
3
+ class Class # :nodoc:
4
+ def cattr_reader(*syms)
5
+ syms.flatten.each do |sym|
6
+ class_eval(<<-EOS, __FILE__, __LINE__)
7
+ unless defined? @@#{sym}
8
+ @@#{sym} = nil
9
+ end
10
+
11
+ def self.#{sym}
12
+ @@#{sym}
13
+ end
14
+
15
+ def #{sym}
16
+ @@#{sym}
17
+ end
18
+ EOS
19
+ end
20
+ end
21
+
22
+ def cattr_writer(*syms)
23
+ syms.flatten.each do |sym|
24
+ class_eval(<<-EOS, __FILE__, __LINE__)
25
+ unless defined? @@#{sym}
26
+ @@#{sym} = nil
27
+ end
28
+
29
+ def self.#{sym}=(obj)
30
+ @@#{sym} = obj
31
+ end
32
+
33
+ def #{sym}=(obj)
34
+ @@#{sym} = obj
35
+ end
36
+ EOS
37
+ end
38
+ end
39
+
40
+ def cattr_accessor(*syms)
41
+ cattr_reader(*syms)
42
+ cattr_writer(*syms)
43
+ end
44
+ end
@@ -0,0 +1,115 @@
1
+ # Retain for backward compatibility. Methods are now included in Class.
2
+ module ClassInheritableAttributes # :nodoc:
3
+ end
4
+
5
+ # Allows attributes to be shared within an inheritance hierarchy, but where each descendant gets a copy of
6
+ # their parents' attributes, instead of just a pointer to the same. This means that the child can add elements
7
+ # to, for example, an array without those additions being shared with either their parent, siblings, or
8
+ # children, which is unlike the regular class-level attributes that are shared across the entire hierarchy.
9
+ class Class # :nodoc:
10
+ def class_inheritable_reader(*syms)
11
+ syms.each do |sym|
12
+ class_eval <<-EOS
13
+ def self.#{sym}
14
+ read_inheritable_attribute(:#{sym})
15
+ end
16
+
17
+ def #{sym}
18
+ self.class.#{sym}
19
+ end
20
+ EOS
21
+ end
22
+ end
23
+
24
+ def class_inheritable_writer(*syms)
25
+ syms.each do |sym|
26
+ class_eval <<-EOS
27
+ def self.#{sym}=(obj)
28
+ write_inheritable_attribute(:#{sym}, obj)
29
+ end
30
+
31
+ def #{sym}=(obj)
32
+ self.class.#{sym} = obj
33
+ end
34
+ EOS
35
+ end
36
+ end
37
+
38
+ def class_inheritable_array_writer(*syms)
39
+ syms.each do |sym|
40
+ class_eval <<-EOS
41
+ def self.#{sym}=(obj)
42
+ write_inheritable_array(:#{sym}, obj)
43
+ end
44
+
45
+ def #{sym}=(obj)
46
+ self.class.#{sym} = obj
47
+ end
48
+ EOS
49
+ end
50
+ end
51
+
52
+ def class_inheritable_hash_writer(*syms)
53
+ syms.each do |sym|
54
+ class_eval <<-EOS
55
+ def self.#{sym}=(obj)
56
+ write_inheritable_hash(:#{sym}, obj)
57
+ end
58
+
59
+ def #{sym}=(obj)
60
+ self.class.#{sym} = obj
61
+ end
62
+ EOS
63
+ end
64
+ end
65
+
66
+ def class_inheritable_accessor(*syms)
67
+ class_inheritable_reader(*syms)
68
+ class_inheritable_writer(*syms)
69
+ end
70
+
71
+ def class_inheritable_array(*syms)
72
+ class_inheritable_reader(*syms)
73
+ class_inheritable_array_writer(*syms)
74
+ end
75
+
76
+ def class_inheritable_hash(*syms)
77
+ class_inheritable_reader(*syms)
78
+ class_inheritable_hash_writer(*syms)
79
+ end
80
+
81
+ def inheritable_attributes
82
+ @inheritable_attributes ||= {}
83
+ end
84
+
85
+ def write_inheritable_attribute(key, value)
86
+ inheritable_attributes[key] = value
87
+ end
88
+
89
+ def write_inheritable_array(key, elements)
90
+ write_inheritable_attribute(key, []) if read_inheritable_attribute(key).nil?
91
+ write_inheritable_attribute(key, read_inheritable_attribute(key) + elements)
92
+ end
93
+
94
+ def write_inheritable_hash(key, hash)
95
+ write_inheritable_attribute(key, {}) if read_inheritable_attribute(key).nil?
96
+ write_inheritable_attribute(key, read_inheritable_attribute(key).merge(hash))
97
+ end
98
+
99
+ def read_inheritable_attribute(key)
100
+ inheritable_attributes[key]
101
+ end
102
+
103
+ def reset_inheritable_attributes
104
+ inheritable_attributes.clear
105
+ end
106
+
107
+ private
108
+ def inherited_with_inheritable_attributes(child)
109
+ inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes)
110
+ child.instance_variable_set('@inheritable_attributes', inheritable_attributes.dup)
111
+ end
112
+
113
+ alias inherited_without_inheritable_attributes inherited
114
+ alias inherited inherited_with_inheritable_attributes
115
+ end
@@ -0,0 +1,24 @@
1
+ class Class #:nodoc:
2
+ def remove_subclasses
3
+ Object.remove_subclasses_of(self)
4
+ end
5
+
6
+ def subclasses
7
+ Object.subclasses_of(self).map { |o| o.to_s }
8
+ end
9
+
10
+ def remove_class(*klasses)
11
+ klasses.flatten.each do |klass|
12
+ # Skip this class if there is nothing bound to this name
13
+ next unless defined?(klass.name)
14
+
15
+ basename = klass.to_s.split("::").last
16
+ parent = klass.parent
17
+
18
+ # Skip this class if it does not match the current one bound to this name
19
+ next unless parent.const_defined?(basename) && klass = parent.const_get(basename)
20
+
21
+ parent.send :remove_const, basename unless parent == klass
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ require File.dirname(__FILE__) + '/class/attribute_accessors'
2
+ require File.dirname(__FILE__) + '/class/inheritable_attributes'
3
+ require File.dirname(__FILE__) + '/class/removal'
@@ -0,0 +1,25 @@
1
+ module ShatteredSupport #:nodoc:
2
+ module CoreExtensions #:nodoc:
3
+ module Dir #:nodoc:
4
+ module Search #:nodoc:
5
+ def self.append_features(base) #:nodoc:
6
+ super
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ # Dir.each_in_path will recursively yield all non-hidden
11
+ # directories from the given path.
12
+ module ClassMethods
13
+ def each_in_path(path, &block)
14
+ return unless ::File.directory? path
15
+ yield path
16
+ foreach(path) do |directory|
17
+ next if directory =~ /\..*/
18
+ each_in_path("#{path}/#{directory}", &block)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,5 @@
1
+ require File.dirname(__FILE__) + '/dir/search'
2
+
3
+ class Dir
4
+ include ShatteredSupport::CoreExtensions::Dir::Search
5
+ end
@@ -0,0 +1,50 @@
1
+ module ShatteredSupport #:nodoc:
2
+ module CoreExtensions #:nodoc:
3
+ module File #:nodoc:
4
+ module Search #:nodoc:
5
+ def self.append_features(base) #:nodoc:
6
+ super
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ # File.each_in_path will recursively look for all files,
12
+ # starting at the given path.
13
+ # It will yield with each result.
14
+ def each_in_path(path)
15
+ ::Dir.each_in_path(path) do |directory|
16
+ ::Dir.foreach( directory ) do |filename|
17
+ resource = directory + "/#{filename}"
18
+ yield(resource) if ::File.file? resource
19
+ end
20
+ end
21
+ end
22
+
23
+ # This finds all files in paths (an array or string)
24
+ # matching the given extensions:
25
+ #
26
+ # Usage:
27
+ # -File.find_by_extensions(SHATTERED_ROOT,"ogg","mp3","wav")
28
+ # -File.find_by_extension(["apps","media","plugins"], "rmaterial")
29
+ def find_by_extensions(paths, *extensions)
30
+ paths = [paths] if paths.is_a? ::String
31
+ # get rid of "." in ".ogg"
32
+ extensions.collect! { |ext| ext[0].chr == "." ? ext[1..-1] : ext }
33
+ reg_exp = /\.(#{extensions.join("|")})$/
34
+ files = []
35
+ paths.each do |path|
36
+ each_in_path(path) do |filename|
37
+ files << filename if filename =~ reg_exp
38
+ end
39
+ end
40
+ return files
41
+ end
42
+ # See File#find_by_extensions
43
+ def find_by_extension(paths, extension)
44
+ find_by_extensions(paths, extension)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,5 @@
1
+ require File.dirname(__FILE__) + '/file/search'
2
+
3
+ class File
4
+ include ShatteredSupport::CoreExtensions::File::Search
5
+ end
@@ -0,0 +1,72 @@
1
+ module ShatteredSupport #:nodoc:
2
+ module CoreExtensions #:nodoc:
3
+ module Numeric #:nodoc:
4
+ # Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years.
5
+ #
6
+ # If you need precise date calculations that doesn't just treat months as 30 days, then have
7
+ # a look at Time#advance.
8
+ #
9
+ # Some of these methods are approximations, Ruby's core
10
+ # Date[http://stdlib.rubyonrails.org/libdoc/date/rdoc/index.html] and
11
+ # Time[http://stdlib.rubyonrails.org/libdoc/time/rdoc/index.html] should be used for precision
12
+ # date and time arithmetic
13
+ module Time
14
+ def seconds
15
+ self
16
+ end
17
+ alias :second :seconds
18
+
19
+ def minutes
20
+ self * 60
21
+ end
22
+ alias :minute :minutes
23
+
24
+ def hours
25
+ self * 60.minutes
26
+ end
27
+ alias :hour :hours
28
+
29
+ def days
30
+ self * 24.hours
31
+ end
32
+ alias :day :days
33
+
34
+ def weeks
35
+ self * 7.days
36
+ end
37
+ alias :week :weeks
38
+
39
+ def fortnights
40
+ self * 2.weeks
41
+ end
42
+ alias :fortnight :fortnights
43
+
44
+ def months
45
+ self * 30.days
46
+ end
47
+ alias :month :months
48
+
49
+ def years
50
+ (self * 365.25.days).to_i
51
+ end
52
+ alias :year :years
53
+
54
+ # Reads best without arguments: 10.minutes.ago
55
+ def ago(time = ::Time.now)
56
+ time - self
57
+ end
58
+
59
+ # Reads best with argument: 10.minutes.until(time)
60
+ alias :until :ago
61
+
62
+ # Reads best with argument: 10.minutes.since(time)
63
+ def since(time = ::Time.now)
64
+ time + self
65
+ end
66
+
67
+ # Reads best without arguments: 10.minutes.from_now
68
+ alias :from_now :since
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,5 @@
1
+ require File.dirname(__FILE__) + '/numeric/time'
2
+
3
+ class Numeric #:nodoc:
4
+ include ShatteredSupport::CoreExtensions::Numeric::Time
5
+ end
@@ -0,0 +1,58 @@
1
+ module ShatteredSupport #:nodoc:
2
+ module CoreExtensions #:nodoc:
3
+ module String #:nodoc:
4
+ # Makes it easier to access parts of a string, such as specific characters and substrings.
5
+ module Access
6
+ # Returns the character at the +position+ treating the string as an array (where 0 is the first character).
7
+ #
8
+ # Examples:
9
+ # "hello".at(0) # => "h"
10
+ # "hello".at(4) # => "o"
11
+ # "hello".at(10) # => nil
12
+ def at(position)
13
+ self[position, 1]
14
+ end
15
+
16
+ # Returns the remaining of the string from the +position+ treating the string as an array (where 0 is the first character).
17
+ #
18
+ # Examples:
19
+ # "hello".from(0) # => "hello"
20
+ # "hello".from(2) # => "llo"
21
+ # "hello".from(10) # => nil
22
+ def from(position)
23
+ self[position..-1]
24
+ end
25
+
26
+ # Returns the beginning of the string up to the +position+ treating the string as an array (where 0 is the first character).
27
+ #
28
+ # Examples:
29
+ # "hello".to(0) # => "h"
30
+ # "hello".to(2) # => "hel"
31
+ # "hello".to(10) # => "hello"
32
+ def to(position)
33
+ self[0..position]
34
+ end
35
+
36
+ # Returns the first character of the string or the first +limit+ characters.
37
+ #
38
+ # Examples:
39
+ # "hello".first # => "h"
40
+ # "hello".first(2) # => "he"
41
+ # "hello".first(10) # => "hello"
42
+ def first(limit = 1)
43
+ self[0..(limit - 1)]
44
+ end
45
+
46
+ # Returns the last character of the string or the last +limit+ characters.
47
+ #
48
+ # Examples:
49
+ # "hello".last # => "o"
50
+ # "hello".last(2) # => "lo"
51
+ # "hello".last(10) # => "hello"
52
+ def last(limit = 1)
53
+ self[(-limit)..-1] || self
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,19 @@
1
+ require 'parsedate'
2
+
3
+ module ShatteredSupport #:nodoc:
4
+ module CoreExtensions #:nodoc:
5
+ module String #:nodoc:
6
+ # Converting strings to other objects
7
+ module Conversions
8
+ # Form can be either :utc (default) or :local.
9
+ def to_time(form = :utc)
10
+ ::Time.send(form, *ParseDate.parsedate(self))
11
+ end
12
+
13
+ def to_date
14
+ ::Date.new(*ParseDate.parsedate(self)[0..2])
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,64 @@
1
+ require File.dirname(__FILE__) + '/../../inflector' unless defined? Inflector
2
+ module ShatteredSupport #:nodoc:
3
+ module CoreExtensions #:nodoc:
4
+ module String #:nodoc:
5
+ # Makes it possible to do "posts".singularize that returns "post" and "MegaCoolClass".underscore that returns "mega_cool_class".
6
+ module Inflections
7
+ def pluralize
8
+ Inflector.pluralize(self)
9
+ end
10
+
11
+ def singularize
12
+ Inflector.singularize(self)
13
+ end
14
+
15
+ def camelize(first_letter = :upper)
16
+ case first_letter
17
+ when :upper then Inflector.camelize(self, true)
18
+ when :lower then Inflector.camelize(self, false)
19
+ end
20
+ end
21
+ alias_method :camelcase, :camelize
22
+
23
+ def titleize
24
+ Inflector.titleize(self)
25
+ end
26
+ alias_method :titlecase, :titleize
27
+
28
+ def underscore
29
+ Inflector.underscore(self)
30
+ end
31
+
32
+ def dasherize
33
+ Inflector.dasherize(self)
34
+ end
35
+
36
+ def demodulize
37
+ Inflector.demodulize(self)
38
+ end
39
+
40
+ def tableize
41
+ Inflector.tableize(self)
42
+ end
43
+
44
+ def classify
45
+ Inflector.classify(self)
46
+ end
47
+
48
+ # Capitalizes the first word and turns underscores into spaces and strips _id, so "employee_salary" becomes "Employee salary"
49
+ # and "author_id" becomes "Author".
50
+ def humanize
51
+ Inflector.humanize(self)
52
+ end
53
+
54
+ def foreign_key(separate_class_name_and_id_with_underscore = true)
55
+ Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
56
+ end
57
+
58
+ def constantize
59
+ Inflector.constantize(self)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,17 @@
1
+ require 'strscan'
2
+
3
+ module ShatteredSupport #:nodoc:
4
+ module CoreExtensions #:nodoc:
5
+ module String #:nodoc:
6
+ # Custom string iterators
7
+ module Iterators
8
+ # Yields a single-character string for each character in the string.
9
+ # When $KCODE = 'UTF8', multi-byte characters are yielded appropriately.
10
+ def each_char
11
+ scanner, char = StringScanner.new(self), /./mu
12
+ loop { yield(scanner.scan(char) || break) }
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ module ShatteredSupport #:nodoc:
2
+ module CoreExtensions #:nodoc:
3
+ module String #:nodoc:
4
+ # Additional string tests.
5
+ module StartsEndsWith
6
+ # Does the string start with the specified +prefix+?
7
+ def starts_with?(prefix)
8
+ prefix = prefix.to_s
9
+ self[0, prefix.length] == prefix
10
+ end
11
+
12
+ # Does the string end with the specified +suffix+?
13
+ def ends_with?(suffix)
14
+ suffix = suffix.to_s
15
+ self[-suffix.length, suffix.length] == suffix
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ require File.dirname(__FILE__) + '/string/inflections'
2
+ require File.dirname(__FILE__) + '/string/conversions'
3
+ require File.dirname(__FILE__) + '/string/access'
4
+ require File.dirname(__FILE__) + '/string/starts_ends_with'
5
+ require File.dirname(__FILE__) + '/string/iterators'
6
+
7
+ class String #:nodoc:
8
+ include ShatteredSupport::CoreExtensions::String::Access
9
+ include ShatteredSupport::CoreExtensions::String::Conversions
10
+ include ShatteredSupport::CoreExtensions::String::Inflections
11
+ include ShatteredSupport::CoreExtensions::String::StartsEndsWith
12
+ include ShatteredSupport::CoreExtensions::String::Iterators
13
+ end
@@ -0,0 +1 @@
1
+ Dir[File.dirname(__FILE__) + "/core_ext/*.rb"].each { |file| require(file) }
@@ -0,0 +1,53 @@
1
+ Inflector.inflections do |inflect|
2
+ inflect.plural(/$/, 's')
3
+ inflect.plural(/s$/i, 's')
4
+ inflect.plural(/(ax|test)is$/i, '\1es')
5
+ inflect.plural(/(octop|vir)us$/i, '\1i')
6
+ inflect.plural(/(alias|status)$/i, '\1es')
7
+ inflect.plural(/(bu)s$/i, '\1ses')
8
+ inflect.plural(/(buffal|tomat)o$/i, '\1oes')
9
+ inflect.plural(/([ti])um$/i, '\1a')
10
+ inflect.plural(/sis$/i, 'ses')
11
+ inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
12
+ inflect.plural(/(hive)$/i, '\1s')
13
+ inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
14
+ inflect.plural(/([^aeiouy]|qu)ies$/i, '\1y')
15
+ inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
16
+ inflect.plural(/(matr|vert|ind)ix|ex$/i, '\1ices')
17
+ inflect.plural(/([m|l])ouse$/i, '\1ice')
18
+ inflect.plural(/^(ox)$/i, '\1en')
19
+ inflect.plural(/(quiz)$/i, '\1zes')
20
+
21
+ inflect.singular(/s$/i, '')
22
+ inflect.singular(/(n)ews$/i, '\1ews')
23
+ inflect.singular(/([ti])a$/i, '\1um')
24
+ inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis')
25
+ inflect.singular(/(^analy)ses$/i, '\1sis')
26
+ inflect.singular(/([^f])ves$/i, '\1fe')
27
+ inflect.singular(/(hive)s$/i, '\1')
28
+ inflect.singular(/(tive)s$/i, '\1')
29
+ inflect.singular(/([lr])ves$/i, '\1f')
30
+ inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')
31
+ inflect.singular(/(s)eries$/i, '\1eries')
32
+ inflect.singular(/(m)ovies$/i, '\1ovie')
33
+ inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
34
+ inflect.singular(/([m|l])ice$/i, '\1ouse')
35
+ inflect.singular(/(bus)es$/i, '\1')
36
+ inflect.singular(/(o)es$/i, '\1')
37
+ inflect.singular(/(shoe)s$/i, '\1')
38
+ inflect.singular(/(cris|ax|test)es$/i, '\1is')
39
+ inflect.singular(/([octop|vir])i$/i, '\1us')
40
+ inflect.singular(/(alias|status)es$/i, '\1')
41
+ inflect.singular(/^(ox)en/i, '\1')
42
+ inflect.singular(/(vert|ind)ices$/i, '\1ex')
43
+ inflect.singular(/(matr)ices$/i, '\1ix')
44
+ inflect.singular(/(quiz)zes$/i, '\1')
45
+
46
+ inflect.irregular('person', 'people')
47
+ inflect.irregular('man', 'men')
48
+ inflect.irregular('child', 'children')
49
+ inflect.irregular('sex', 'sexes')
50
+ inflect.irregular('move', 'moves')
51
+
52
+ inflect.uncountable(%w(equipment information rice money species series fish sheep))
53
+ end