AXElements 1.0.0.alpha11 → 1.0.0.beta

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.
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'