AXElements 1.0.0.alpha11 → 1.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/History.markdown +11 -1
  2. data/README.markdown +10 -8
  3. data/Rakefile +1 -1
  4. data/lib/accessibility/dsl.rb +5 -2
  5. data/lib/accessibility/factory.rb +134 -78
  6. data/lib/accessibility/qualifier.rb +2 -0
  7. data/lib/accessibility/system_info.rb +82 -17
  8. data/lib/accessibility/translator.rb +17 -17
  9. data/lib/accessibility/version.rb +1 -1
  10. data/lib/ax/application.rb +11 -16
  11. data/lib/ax/element.rb +21 -34
  12. data/lib/ax/systemwide.rb +2 -2
  13. data/lib/ax_elements.rb +7 -1
  14. data/lib/ax_elements/active_support_selections.rb +10 -0
  15. data/lib/ax_elements/mri.rb +57 -0
  16. data/lib/ax_elements/nsarray_compat.rb +97 -17
  17. data/rakelib/gem.rake +12 -3
  18. data/rakelib/test.rake +0 -6
  19. data/test/helper.rb +10 -20
  20. data/test/integration/accessibility/test_dsl.rb +6 -14
  21. data/test/integration/accessibility/test_enumerators.rb +0 -1
  22. data/test/integration/accessibility/test_graph.rb +1 -0
  23. data/test/integration/accessibility/test_qualifier.rb +2 -2
  24. data/test/integration/ax/test_application.rb +2 -2
  25. data/test/sanity/accessibility/test_factory.rb +2 -2
  26. data/test/sanity/accessibility/test_pretty_printer.rb +2 -2
  27. data/test/sanity/ax/test_application.rb +1 -1
  28. data/test/sanity/ax/test_element.rb +2 -2
  29. data/test/sanity/ax_elements/test_nsobject_inspect.rb +4 -2
  30. metadata +28 -36
  31. data/ext/accessibility/core/core.c +0 -26
  32. data/lib/accessibility/core.rb +0 -943
  33. data/lib/accessibility/highlighter.rb +0 -86
  34. data/lib/accessibility/statistics.rb +0 -57
  35. data/lib/ax_elements/core_graphics_workaround.rb +0 -7
  36. data/lib/ax_elements/vendor/inflection_data.rb +0 -66
  37. data/lib/ax_elements/vendor/inflections.rb +0 -176
  38. data/lib/ax_elements/vendor/inflector.rb +0 -306
  39. data/lib/minitest/ax_elements.rb +0 -180
  40. data/lib/rspec/expectations/ax_elements.rb +0 -234
  41. data/test/integration/accessibility/test_core.rb +0 -18
  42. data/test/integration/minitest/test_ax_elements.rb +0 -89
  43. data/test/integration/rspec/expectations/test_ax_elements.rb +0 -102
  44. data/test/sanity/accessibility/test_highlighter.rb +0 -56
  45. data/test/sanity/accessibility/test_statistics.rb +0 -57
  46. data/test/sanity/minitest/test_ax_elements.rb +0 -17
  47. data/test/sanity/rspec/expectations/test_ax_elements.rb +0 -15
  48. data/test/test_core.rb +0 -454
@@ -1,86 +0,0 @@
1
- framework 'AppKit'
2
- require 'accessibility/version'
3
-
4
- ##
5
- # A screen highlighter for debugging. When you initialize a highligter
6
- # object it will highlight the given bounds on the screen.
7
- #
8
- # Highligter objects can have their colour configured at initialization,
9
- # and can also have a timeout to automatically stop displaying.
10
- #
11
- # @example
12
- #
13
- # h = Accessibility::Highlighter.new(CGRectMake(100,100,100,100))
14
- # # when you are done...
15
- # h.stop
16
- #
17
- class Accessibility::Highlighter < NSWindow
18
-
19
- # @param bounds [CGRect]
20
- # @param opts [Hash]
21
- # @option opts [Number] :timeout
22
- # @option opts [NSColor] :colour (NSColor.magentaColor)
23
- def initialize bounds, opts = {}
24
- colour = opts[:colour] || opts[:color] || NSColor.magentaColor
25
-
26
- bounds.flip! # we assume the rect is in the other co-ordinate system
27
-
28
- initWithContentRect bounds,
29
- styleMask: NSBorderlessWindowMask,
30
- backing: NSBackingStoreBuffered,
31
- defer: true
32
- setOpaque false
33
- setAlphaValue 0.20
34
- setLevel NSStatusWindowLevel
35
- setBackgroundColor colour
36
- setIgnoresMouseEvents true
37
- setFrame bounds, display: false
38
- makeKeyAndOrderFront NSApp
39
-
40
- if opts.has_key? :timeout
41
- Dispatch::Queue.new(queue_id).after opts[:timeout] do
42
- self.stop
43
- end
44
- end
45
- end
46
-
47
- ##
48
- # Tell the highlighter to stop displaying.
49
- #
50
- # @return [self]
51
- def stop
52
- close
53
- end
54
-
55
-
56
- private
57
-
58
- def queue_id
59
- "com.marketcircle.axelements.window_killer_#{hash}"
60
- end
61
- end
62
-
63
-
64
- ##
65
- # AXElements extensions to `CGRect`.
66
- class CGRect
67
- ##
68
- # Treats the rect as belonging to one co-ordinate system and then
69
- # converts it to the other system.
70
- #
71
- # This is useful because accessibility API's expect to work with
72
- # the flipped co-ordinate system (origin in top left), but AppKit
73
- # prefers to use the cartesian co-ordinate system (origin in bottom
74
- # left).
75
- #
76
- # @return [CGRect]
77
- def flip!
78
- screen_height = NSMaxY(NSScreen.mainScreen.frame)
79
- origin.y = screen_height - NSMaxY(self)
80
- self
81
- end
82
- end
83
-
84
-
85
- # Initialize the shared application so that windows can be created
86
- NSApplication.sharedApplication
@@ -1,57 +0,0 @@
1
- require 'accessibility/version'
2
-
3
- class Accessibility::Statistics
4
-
5
- def initialize
6
- @stats = Hash.new do |h,k| h[k] = 0 end
7
- @q = Dispatch::Queue.new "com.marketcircle.axelements.stats"
8
- end
9
-
10
- def increment key
11
- @q.async do
12
- @stats[key] += 1
13
- end
14
- end
15
-
16
- def to_s
17
- @q.sync do # must be synchronized
18
- set_max_length
19
- @out = output_header << output_body << "\n"
20
- end
21
- @out
22
- end
23
-
24
-
25
- private
26
-
27
- def set_max_length
28
- @max_key_len = @stats.keys.map(&:length).max
29
- @max_val_len = @stats.values.max.to_s.length
30
- end
31
-
32
- def dot key, val
33
- length = 4
34
- length += @max_key_len - key.length
35
- length += @max_val_len - val.to_s.length
36
- "." * length
37
- end
38
-
39
- def output_header
40
- <<-EOS
41
- ######################
42
- # AX Call Statistics #
43
- ######################
44
- EOS
45
- end
46
-
47
- def output_body
48
- pairs = @stats.to_a.sort { |x,y| y.last <=> x.last }
49
- pairs.map do |key, val|
50
- key.to_s << dot(key,val) << val.to_s
51
- end.join("\n")
52
- end
53
-
54
- end
55
-
56
- # @return [Accessibility::Statistics]
57
- STATS = Accessibility::Statistics.new
@@ -1,7 +0,0 @@
1
- framework 'Cocoa'
2
-
3
- MOUNTAIN_LION_APPKIT_VERSION ||= 1187
4
- if NSAppKitVersionNumber >= MOUNTAIN_LION_APPKIT_VERSION
5
- framework '/System/Library/Frameworks/CoreGraphics.framework'
6
- end
7
-
@@ -1,66 +0,0 @@
1
- module Accessibility
2
- Inflector.inflections do |inflect|
3
- inflect.plural(/$/, 's')
4
- inflect.plural(/s$/i, 's')
5
- inflect.plural(/(ax|test)is$/i, '\1es')
6
- inflect.plural(/(octop|vir)us$/i, '\1i')
7
- inflect.plural(/(octop|vir)i$/i, '\1i')
8
- inflect.plural(/(alias|status)$/i, '\1es')
9
- inflect.plural(/(bu)s$/i, '\1ses')
10
- inflect.plural(/(buffal|tomat)o$/i, '\1oes')
11
- inflect.plural(/([ti])um$/i, '\1a')
12
- inflect.plural(/([ti])a$/i, '\1a')
13
- inflect.plural(/sis$/i, 'ses')
14
- inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
15
- inflect.plural(/(hive)$/i, '\1s')
16
- inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
17
- inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
18
- inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
19
- inflect.plural(/(m|l)ouse$/i, '\1ice')
20
- inflect.plural(/(m|l)ice$/i, '\1ice')
21
- inflect.plural(/^(ox)$/i, '\1en')
22
- inflect.plural(/^(oxen)$/i, '\1')
23
- inflect.plural(/(quiz)$/i, '\1zes')
24
-
25
- inflect.singular(/s$/i, '')
26
- inflect.singular(/(n)ews$/i, '\1ews')
27
- inflect.singular(/([ti])a$/i, '\1um')
28
- inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis')
29
- inflect.singular(/(^analy)ses$/i, '\1sis')
30
- inflect.singular(/([^f])ves$/i, '\1fe')
31
- inflect.singular(/(hive)s$/i, '\1')
32
- inflect.singular(/(tive)s$/i, '\1')
33
- inflect.singular(/([lr])ves$/i, '\1f')
34
- inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')
35
- inflect.singular(/(s)eries$/i, '\1eries')
36
- inflect.singular(/(m)ovies$/i, '\1ovie')
37
- inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
38
- inflect.singular(/(m|l)ice$/i, '\1ouse')
39
- inflect.singular(/(bus)es$/i, '\1')
40
- inflect.singular(/(o)es$/i, '\1')
41
- inflect.singular(/(shoe)s$/i, '\1')
42
- inflect.singular(/(cris|ax|test)es$/i, '\1is')
43
- inflect.singular(/(octop|vir)i$/i, '\1us')
44
- inflect.singular(/(alias|status)es$/i, '\1')
45
- inflect.singular(/^(ox)en/i, '\1')
46
- inflect.singular(/(vert|ind)ices$/i, '\1ex')
47
- inflect.singular(/(matr)ices$/i, '\1ix')
48
- inflect.singular(/(quiz)zes$/i, '\1')
49
- inflect.singular(/(database)s$/i, '\1')
50
-
51
- inflect.irregular('person', 'people')
52
- inflect.irregular('man', 'men')
53
- inflect.irregular('child', 'children')
54
- inflect.irregular('sex', 'sexes')
55
- inflect.irregular('move', 'moves')
56
- inflect.irregular('cow', 'kine')
57
- inflect.irregular('zombie', 'zombies')
58
-
59
- inflect.uncountable(%w(equipment information rice money species series fish sheep jeans))
60
-
61
- # Related to accessibility
62
- inflect.acronym('UI')
63
- inflect.acronym('RTF')
64
- inflect.acronym('URL')
65
- end
66
- end
@@ -1,176 +0,0 @@
1
- module Accessibility
2
- module Inflector
3
- # A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
4
- # inflection rules. Examples:
5
- #
6
- # ActiveSupport::Inflector.inflections do |inflect|
7
- # inflect.plural /^(ox)$/i, '\1\2en'
8
- # inflect.singular /^(ox)en/i, '\1'
9
- #
10
- # inflect.irregular 'octopus', 'octopi'
11
- #
12
- # inflect.uncountable "equipment"
13
- # end
14
- #
15
- # New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
16
- # pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
17
- # already have been loaded.
18
- class Inflections
19
- ##
20
- # Singleton instance of the inflections database.
21
- #
22
- # @return [Accessibility::Inflector::Inflections]
23
- def self.instance
24
- @__instance__ ||= new
25
- end
26
-
27
- attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex
28
-
29
- def initialize
30
- @plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], [], [], {}, /(?=a)b/
31
- end
32
-
33
- # Specifies a new acronym. An acronym must be specified as it will appear in a camelized string. An underscore
34
- # string that contains the acronym will retain the acronym when passed to `camelize`, `humanize`, or `titleize`.
35
- # A camelized string that contains the acronym will maintain the acronym when titleized or humanized, and will
36
- # convert the acronym into a non-delimited single lowercase word when passed to +underscore+.
37
- #
38
- # Examples:
39
- # acronym 'HTML'
40
- # titleize 'html' #=> 'HTML'
41
- # camelize 'html' #=> 'HTML'
42
- # underscore 'MyHTML' #=> 'my_html'
43
- #
44
- # The acronym, however, must occur as a delimited unit and not be part of another word for conversions to recognize it:
45
- #
46
- # acronym 'HTTP'
47
- # camelize 'my_http_delimited' #=> 'MyHTTPDelimited'
48
- # camelize 'https' #=> 'Https', not 'HTTPs'
49
- # underscore 'HTTPS' #=> 'http_s', not 'https'
50
- #
51
- # acronym 'HTTPS'
52
- # camelize 'https' #=> 'HTTPS'
53
- # underscore 'HTTPS' #=> 'https'
54
- #
55
- # Note: Acronyms that are passed to `pluralize` will no longer be recognized, since the acronym will not occur as
56
- # a delimited unit in the pluralized result. To work around this, you must specify the pluralized form as an
57
- # acronym as well:
58
- #
59
- # acronym 'API'
60
- # camelize(pluralize('api')) #=> 'Apis'
61
- #
62
- # acronym 'APIs'
63
- # camelize(pluralize('api')) #=> 'APIs'
64
- #
65
- # `acronym` may be used to specify any word that contains an acronym or otherwise needs to maintain a non-standard
66
- # capitalization. The only restriction is that the word must begin with a capital letter.
67
- #
68
- # Examples:
69
- # acronym 'RESTful'
70
- # underscore 'RESTful' #=> 'restful'
71
- # underscore 'RESTfulController' #=> 'restful_controller'
72
- # titleize 'RESTfulController' #=> 'RESTful Controller'
73
- # camelize 'restful' #=> 'RESTful'
74
- # camelize 'restful_controller' #=> 'RESTfulController'
75
- #
76
- # acronym 'McDonald'
77
- # underscore 'McDonald' #=> 'mcdonald'
78
- # camelize 'mcdonald' #=> 'McDonald'
79
- def acronym(word)
80
- @acronyms[word.downcase] = word
81
- @acronym_regex = /#{@acronyms.values.join("|")}/
82
- end
83
-
84
- # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
85
- # The replacement should always be a string that may include references to the matched data from the rule.
86
- def plural(rule, replacement)
87
- @uncountables.delete(rule) if rule.is_a?(String)
88
- @uncountables.delete(replacement)
89
- @plurals.insert(0, [rule, replacement])
90
- end
91
-
92
- # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
93
- # The replacement should always be a string that may include references to the matched data from the rule.
94
- def singular(rule, replacement)
95
- @uncountables.delete(rule) if rule.is_a?(String)
96
- @uncountables.delete(replacement)
97
- @singulars.insert(0, [rule, replacement])
98
- end
99
-
100
- # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
101
- # for strings, not regular expressions. You simply pass the irregular in singular and plural form.
102
- #
103
- # Examples:
104
- # irregular 'octopus', 'octopi'
105
- # irregular 'person', 'people'
106
- def irregular(singular, plural)
107
- @uncountables.delete(singular)
108
- @uncountables.delete(plural)
109
- if singular[0,1].upcase == plural[0,1].upcase
110
- plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
111
- plural(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + plural[1..-1])
112
- singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
113
- else
114
- plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
115
- plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
116
- plural(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
117
- plural(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
118
- singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1])
119
- singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1])
120
- end
121
- end
122
-
123
- # Add uncountable words that shouldn't be attempted inflected.
124
- #
125
- # Examples:
126
- # uncountable "money"
127
- # uncountable "money", "information"
128
- # uncountable %w( money information rice )
129
- def uncountable(*words)
130
- (@uncountables << words).flatten!
131
- end
132
-
133
- # Specifies a humanized form of a string by a regular expression rule or by a string mapping.
134
- # When using a regular expression based replacement, the normal humanize formatting is called after the replacement.
135
- # When a string is used, the human form should be specified as desired (example: 'The name', not 'the_name')
136
- #
137
- # Examples:
138
- # human /_cnt$/i, '\1_count'
139
- # human "legacy_col_person_name", "Name"
140
- def human(rule, replacement)
141
- @humans.insert(0, [rule, replacement])
142
- end
143
-
144
- # Clears the loaded inflections within a given scope (default is <tt>:all</tt>).
145
- # Give the scope as a symbol of the inflection type, the options are: <tt>:plurals</tt>,
146
- # <tt>:singulars</tt>, <tt>:uncountables</tt>, <tt>:humans</tt>.
147
- #
148
- # Examples:
149
- # clear :all
150
- # clear :plurals
151
- def clear(scope = :all)
152
- case scope
153
- when :all
154
- @plurals, @singulars, @uncountables, @humans = [], [], [], []
155
- else
156
- instance_variable_set "@#{scope}", []
157
- end
158
- end
159
- end
160
-
161
- # Yields a singleton instance of Inflector::Inflections so you can specify additional
162
- # inflector rules.
163
- #
164
- # Example:
165
- # ActiveSupport::Inflector.inflections do |inflect|
166
- # inflect.uncountable "rails"
167
- # end
168
- def inflections
169
- if block_given?
170
- yield Inflections.instance
171
- else
172
- Inflections.instance
173
- end
174
- end
175
- end
176
- end
@@ -1,306 +0,0 @@
1
- module Accessibility
2
- ##
3
- # The Inflector is code borrowed from Active Support.
4
- #
5
- # The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
6
- # and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept
7
- # in inflections.rb.
8
- #
9
- # The Rails core team has stated patches for the inflections library will not be accepted
10
- # in order to avoid breaking legacy applications which may be relying on errant inflections.
11
- # If you discover an incorrect inflection and require it for your application, you'll need
12
- # to correct it yourself (explained below).
13
- module Inflector
14
- extend self
15
-
16
- # Returns the plural form of the word in the string.
17
- #
18
- # Examples:
19
- # "post".pluralize # => "posts"
20
- # "octopus".pluralize # => "octopi"
21
- # "sheep".pluralize # => "sheep"
22
- # "words".pluralize # => "words"
23
- # "CamelOctopus".pluralize # => "CamelOctopi"
24
- def pluralize(word)
25
- apply_inflections(word, inflections.plurals)
26
- end
27
-
28
- # The reverse of +pluralize+, returns the singular form of a word in a string.
29
- #
30
- # Examples:
31
- # "posts".singularize # => "post"
32
- # "octopi".singularize # => "octopus"
33
- # "sheep".singularize # => "sheep"
34
- # "word".singularize # => "word"
35
- # "CamelOctopi".singularize # => "CamelOctopus"
36
- def singularize(word)
37
- apply_inflections(word, inflections.singulars)
38
- end
39
-
40
- # By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+
41
- # is set to <tt>:lower</tt> then +camelize+ produces lowerCamelCase.
42
- #
43
- # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
44
- #
45
- # Examples:
46
- # "active_record".camelize # => "ActiveRecord"
47
- # "active_record".camelize(:lower) # => "activeRecord"
48
- # "active_record/errors".camelize # => "ActiveRecord::Errors"
49
- # "active_record/errors".camelize(:lower) # => "activeRecord::Errors"
50
- #
51
- # As a rule of thumb you can think of +camelize+ as the inverse of +underscore+,
52
- # though there are cases where that does not hold:
53
- #
54
- # "SSLError".underscore.camelize # => "SslError"
55
- def camelize(term, uppercase_first_letter = true)
56
- string = term.to_s
57
- if uppercase_first_letter
58
- string = string.sub(/^[a-z\d]*/) { inflections.acronyms[$&] || $&.capitalize }
59
- else
60
- string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase }
61
- end
62
- string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }.gsub('/', '::')
63
- end
64
-
65
- # Makes an underscored, lowercase form from the expression in the string.
66
- #
67
- # Changes '::' to '/' to convert namespaces to paths.
68
- #
69
- # Examples:
70
- # "ActiveRecord".underscore # => "active_record"
71
- # "ActiveRecord::Errors".underscore # => active_record/errors
72
- #
73
- # As a rule of thumb you can think of +underscore+ as the inverse of +camelize+,
74
- # though there are cases where that does not hold:
75
- #
76
- # "SSLError".underscore.camelize # => "SslError"
77
- def underscore(camel_cased_word)
78
- word = camel_cased_word.to_s.dup
79
- word.gsub!(/::/, '/')
80
- word.gsub!(/(?:([A-Za-z\d])|^)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
81
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
82
- word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
83
- word.tr!("-", "_")
84
- word.downcase!
85
- word
86
- end
87
-
88
- # Capitalizes the first word and turns underscores into spaces and strips a
89
- # trailing "_id", if any. Like +titleize+, this is meant for creating pretty output.
90
- #
91
- # Examples:
92
- # "employee_salary" # => "Employee salary"
93
- # "author_id" # => "Author"
94
- def humanize(lower_case_and_underscored_word)
95
- result = lower_case_and_underscored_word.to_s.dup
96
- inflections.humans.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
97
- result.gsub!(/_id$/, "")
98
- result.gsub(/(_)?([a-z\d]*)/i) { "#{$1 && ' '}#{inflections.acronyms[$2] || $2.downcase}" }.gsub(/^\w/) { $&.upcase }
99
- end
100
-
101
- # Capitalizes all the words and replaces some characters in the string to create
102
- # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
103
- # used in the Rails internals.
104
- #
105
- # +titleize+ is also aliased as as +titlecase+.
106
- #
107
- # Examples:
108
- # "man from the boondocks".titleize # => "Man From The Boondocks"
109
- # "x-men: the last stand".titleize # => "X Men: The Last Stand"
110
- # "TheManWithoutAPast".titleize # => "The Man Without A Past"
111
- # "raiders_of_the_lost_ark".titleize # => "Raiders Of The Lost Ark"
112
- def titleize(word)
113
- humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize }
114
- end
115
-
116
- # Create the name of a table like Rails does for models to table names. This method
117
- # uses the +pluralize+ method on the last word in the string.
118
- #
119
- # Examples
120
- # "RawScaledScorer".tableize # => "raw_scaled_scorers"
121
- # "egg_and_ham".tableize # => "egg_and_hams"
122
- # "fancyCategory".tableize # => "fancy_categories"
123
- def tableize(class_name)
124
- pluralize(underscore(class_name))
125
- end
126
-
127
- # Create a class name from a plural table name like Rails does for table names to models.
128
- # Note that this returns a string and not a Class. (To convert to an actual class
129
- # follow +classify+ with +constantize+.)
130
- #
131
- # Examples:
132
- # "egg_and_hams".classify # => "EggAndHam"
133
- # "posts".classify # => "Post"
134
- #
135
- # Singular names are not handled correctly:
136
- # "business".classify # => "Busines"
137
- def classify(table_name)
138
- # strip out any leading schema name
139
- camelize(singularize(table_name.to_s.sub(/.*\./, '')))
140
- end
141
-
142
- # Replaces underscores with dashes in the string.
143
- #
144
- # Example:
145
- # "puni_puni" # => "puni-puni"
146
- def dasherize(underscored_word)
147
- underscored_word.gsub(/_/, '-')
148
- end
149
-
150
- # Removes the module part from the expression in the string:
151
- #
152
- # "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections"
153
- # "Inflections".demodulize # => "Inflections"
154
- #
155
- # See also +deconstantize+.
156
- def demodulize(path)
157
- path = path.to_s
158
- if i = path.rindex('::')
159
- path[(i+2)..-1]
160
- else
161
- path
162
- end
163
- end
164
-
165
- # Removes the rightmost segment from the constant expression in the string:
166
- #
167
- # "Net::HTTP".deconstantize # => "Net"
168
- # "::Net::HTTP".deconstantize # => "::Net"
169
- # "String".deconstantize # => ""
170
- # "::String".deconstantize # => ""
171
- # "".deconstantize # => ""
172
- #
173
- # See also +demodulize+.
174
- def deconstantize(path)
175
- path.to_s[0...(path.rindex('::') || 0)] # implementation based on the one in facets' Module#spacename
176
- end
177
-
178
- # Creates a foreign key name from a class name.
179
- # +separate_class_name_and_id_with_underscore+ sets whether
180
- # the method should put '_' between the name and 'id'.
181
- #
182
- # Examples:
183
- # "Message".foreign_key # => "message_id"
184
- # "Message".foreign_key(false) # => "messageid"
185
- # "Admin::Post".foreign_key # => "post_id"
186
- def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
187
- underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
188
- end
189
-
190
- # Tries to find a constant with the name specified in the argument string:
191
- #
192
- # "Module".constantize # => Module
193
- # "Test::Unit".constantize # => Test::Unit
194
- #
195
- # The name is assumed to be the one of a top-level constant, no matter whether
196
- # it starts with "::" or not. No lexical context is taken into account:
197
- #
198
- # C = 'outside'
199
- # module M
200
- # C = 'inside'
201
- # C # => 'inside'
202
- # "C".constantize # => 'outside', same as ::C
203
- # end
204
- #
205
- # NameError is raised when the name is not in CamelCase or the constant is
206
- # unknown.
207
- def constantize(camel_cased_word) #:nodoc:
208
- names = camel_cased_word.split('::')
209
- names.shift if names.empty? || names.first.empty?
210
-
211
- constant = Object
212
- names.each do |name|
213
- constant = constant.const_defined?(name, false) ? constant.const_get(name) : constant.const_missing(name)
214
- end
215
- constant
216
- end
217
-
218
- # Tries to find a constant with the name specified in the argument string:
219
- #
220
- # "Module".safe_constantize # => Module
221
- # "Test::Unit".safe_constantize # => Test::Unit
222
- #
223
- # The name is assumed to be the one of a top-level constant, no matter whether
224
- # it starts with "::" or not. No lexical context is taken into account:
225
- #
226
- # C = 'outside'
227
- # module M
228
- # C = 'inside'
229
- # C # => 'inside'
230
- # "C".safe_constantize # => 'outside', same as ::C
231
- # end
232
- #
233
- # nil is returned when the name is not in CamelCase or the constant (or part of it) is
234
- # unknown.
235
- #
236
- # "blargle".safe_constantize # => nil
237
- # "UnknownModule".safe_constantize # => nil
238
- # "UnknownModule::Foo::Bar".safe_constantize # => nil
239
- #
240
- def safe_constantize(camel_cased_word)
241
- begin
242
- constantize(camel_cased_word)
243
- rescue NameError => e
244
- raise unless e.message =~ /uninitialized constant #{const_regexp(camel_cased_word)}$/ ||
245
- e.name.to_s == camel_cased_word.to_s
246
- rescue ArgumentError => e
247
- raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
248
- end
249
- end
250
-
251
- # Turns a number into an ordinal string used to denote the position in an
252
- # ordered sequence such as 1st, 2nd, 3rd, 4th.
253
- #
254
- # Examples:
255
- # ordinalize(1) # => "1st"
256
- # ordinalize(2) # => "2nd"
257
- # ordinalize(1002) # => "1002nd"
258
- # ordinalize(1003) # => "1003rd"
259
- # ordinalize(-11) # => "-11th"
260
- # ordinalize(-1021) # => "-1021st"
261
- def ordinalize(number)
262
- if (11..13).include?(number.to_i.abs % 100)
263
- "#{number}th"
264
- else
265
- case number.to_i.abs % 10
266
- when 1; "#{number}st"
267
- when 2; "#{number}nd"
268
- when 3; "#{number}rd"
269
- else "#{number}th"
270
- end
271
- end
272
- end
273
-
274
- private
275
-
276
- # Mount a regular expression that will match part by part of the constant.
277
- # For instance, Foo::Bar::Baz will generate Foo(::Bar(::Baz)?)?
278
- def const_regexp(camel_cased_word) #:nodoc:
279
- parts = camel_cased_word.split("::")
280
- last = parts.pop
281
-
282
- parts.reverse.inject(last) do |acc, part|
283
- part.empty? ? acc : "#{part}(::#{acc})?"
284
- end
285
- end
286
-
287
- # Applies inflection rules for +singularize+ and +pluralize+.
288
- #
289
- # Examples:
290
- # apply_inflections("post", inflections.plurals) # => "posts"
291
- # apply_inflections("posts", inflections.singulars) # => "post"
292
- def apply_inflections(word, rules)
293
- result = word.to_s.dup
294
-
295
- if word.empty? || inflections.uncountables.any? { |inflection| result =~ /\b#{inflection}\Z/i }
296
- result
297
- else
298
- rules.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
299
- result
300
- end
301
- end
302
- end
303
- end
304
-
305
- require 'ax_elements/vendor/inflections'
306
- require 'ax_elements/vendor/inflection_data'