shattered_support 0.3.2 → 0.3.3

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