AXElements 0.6.0beta2 → 0.7.5

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 (107) hide show
  1. data/.yardopts +1 -2
  2. data/README.markdown +152 -88
  3. data/Rakefile +8 -103
  4. data/docs/Debugging.markdown +9 -2
  5. data/docs/KeyboardEvents.markdown +114 -49
  6. data/docs/Setting.markdown +1 -0
  7. data/docs/images/next_version.png +0 -0
  8. data/ext/accessibility/key_coder/extconf.rb +22 -0
  9. data/ext/accessibility/key_coder/key_coder.c +113 -0
  10. data/lib/AXElements.rb +2 -0
  11. data/lib/accessibility/core.rb +897 -0
  12. data/lib/accessibility/debug.rb +168 -0
  13. data/lib/accessibility/dsl.rb +697 -0
  14. data/lib/accessibility/enumerators.rb +104 -0
  15. data/lib/accessibility/errors.rb +32 -0
  16. data/lib/accessibility/factory.rb +153 -0
  17. data/lib/accessibility/graph.rb +150 -0
  18. data/lib/{ax_elements/inspector.rb → accessibility/pp_inspector.rb} +39 -28
  19. data/lib/accessibility/qualifier.rb +158 -0
  20. data/lib/accessibility/string.rb +494 -0
  21. data/lib/accessibility/translator.rb +178 -0
  22. data/lib/accessibility/version.rb +7 -0
  23. data/lib/accessibility.rb +79 -0
  24. data/lib/ax/application.rb +234 -0
  25. data/lib/{ax_elements/elements → ax}/button.rb +2 -0
  26. data/lib/ax/element.rb +518 -0
  27. data/lib/{ax_elements/elements → ax}/radio_button.rb +2 -0
  28. data/lib/ax/row.rb +37 -0
  29. data/lib/{ax_elements/elements → ax}/static_text.rb +2 -0
  30. data/lib/ax/systemwide.rb +86 -0
  31. data/lib/ax_elements/awesome_print.rb +25 -0
  32. data/lib/ax_elements/exception_workaround.rb +8 -0
  33. data/lib/ax_elements/nsarray_compat.rb +64 -0
  34. data/lib/ax_elements/vendor/inflection_data.rb +65 -0
  35. data/lib/ax_elements/vendor/inflections.rb +172 -0
  36. data/lib/ax_elements/vendor/inflector.rb +306 -0
  37. data/lib/ax_elements.rb +14 -25
  38. data/lib/minitest/ax_elements.rb +112 -12
  39. data/lib/mouse.rb +72 -46
  40. data/lib/rspec/expectations/ax_elements.rb +133 -6
  41. data/rakelib/doc.rake +13 -0
  42. data/rakelib/ext.rake +61 -0
  43. data/rakelib/gem.rake +28 -0
  44. data/rakelib/test.rake +53 -0
  45. data/test/helper.rb +11 -97
  46. data/test/integration/accessibility/test_core.rb +18 -0
  47. data/test/integration/accessibility/test_debug.rb +44 -0
  48. data/test/integration/accessibility/test_dsl.rb +225 -0
  49. data/test/integration/accessibility/test_enumerators.rb +122 -0
  50. data/test/integration/accessibility/test_errors.rb +38 -0
  51. data/test/integration/accessibility/test_notifications.rb +22 -0
  52. data/test/integration/accessibility/test_qualifier.rb +148 -0
  53. data/test/integration/ax/test_application.rb +56 -0
  54. data/test/integration/ax/test_element.rb +46 -0
  55. data/test/integration/ax/test_row.rb +23 -0
  56. data/test/integration/ax_elements/test_nsarray_compat.rb +43 -0
  57. data/test/integration/minitest/test_ax_elements.rb +98 -0
  58. data/test/integration/rspec/expectations/test_ax_elements.rb +58 -0
  59. data/test/integration/test_mouse.rb +35 -0
  60. data/test/sanity/accessibility/test_core.rb +553 -0
  61. data/test/sanity/accessibility/test_debug.rb +63 -0
  62. data/test/sanity/accessibility/test_dsl.rb +75 -0
  63. data/test/sanity/accessibility/test_errors.rb +10 -0
  64. data/test/sanity/accessibility/test_factory.rb +88 -0
  65. data/test/sanity/accessibility/test_pp_inspector.rb +110 -0
  66. data/test/sanity/accessibility/test_qualifier.rb +13 -0
  67. data/test/sanity/accessibility/test_string.rb +238 -0
  68. data/test/sanity/accessibility/test_translator.rb +145 -0
  69. data/test/sanity/ax/test_application.rb +90 -0
  70. data/test/sanity/ax/test_element.rb +80 -0
  71. data/test/sanity/ax/test_systemwide.rb +66 -0
  72. data/test/sanity/ax_elements/test_nsarray_compat.rb +16 -0
  73. data/test/sanity/ax_elements/test_nsobject_inspect.rb +11 -0
  74. data/test/sanity/minitest/test_ax_elements.rb +15 -0
  75. data/test/sanity/rspec/expectations/test_ax_elements.rb +12 -0
  76. data/test/sanity/test_ax_elements.rb +10 -0
  77. data/test/sanity/test_mouse.rb +19 -0
  78. metadata +111 -93
  79. data/LICENSE.txt +0 -25
  80. data/ext/key_coder/extconf.rb +0 -6
  81. data/ext/key_coder/key_coder.m +0 -77
  82. data/lib/ax_elements/accessibility/enumerators.rb +0 -104
  83. data/lib/ax_elements/accessibility/graph.rb +0 -118
  84. data/lib/ax_elements/accessibility/language.rb +0 -347
  85. data/lib/ax_elements/accessibility/qualifier.rb +0 -73
  86. data/lib/ax_elements/accessibility.rb +0 -166
  87. data/lib/ax_elements/core.rb +0 -541
  88. data/lib/ax_elements/element.rb +0 -593
  89. data/lib/ax_elements/elements/application.rb +0 -88
  90. data/lib/ax_elements/elements/row.rb +0 -30
  91. data/lib/ax_elements/elements/systemwide.rb +0 -46
  92. data/lib/ax_elements/macruby_extensions.rb +0 -255
  93. data/lib/ax_elements/notification.rb +0 -37
  94. data/lib/ax_elements/version.rb +0 -9
  95. data/test/elements/test_application.rb +0 -72
  96. data/test/elements/test_row.rb +0 -27
  97. data/test/elements/test_systemwide.rb +0 -38
  98. data/test/test_accessibility.rb +0 -127
  99. data/test/test_blankness.rb +0 -26
  100. data/test/test_core.rb +0 -448
  101. data/test/test_element.rb +0 -939
  102. data/test/test_enumerators.rb +0 -81
  103. data/test/test_inspector.rb +0 -130
  104. data/test/test_language.rb +0 -157
  105. data/test/test_macruby_extensions.rb +0 -303
  106. data/test/test_mouse.rb +0 -5
  107. data/test/test_search_semantics.rb +0 -143
@@ -0,0 +1,64 @@
1
+ require 'ax/element'
2
+ require 'accessibility/translator'
3
+
4
+ ##
5
+ # An old hack on arrays that allows you to map a single method across
6
+ # an array of {AX::Element} objects more succinctly than
7
+ # `Symbol#to_proc`.
8
+ #
9
+ # I've always been on the fence as to whether this was a good idea or
10
+ # not, but at this point there is too much code written that depends
11
+ # on this and so I will just keep it around for backwards compatability.
12
+ module Accessibility::NSArrayCompat
13
+
14
+ ##
15
+ # Equivalent to `#at(1)`
16
+ def second
17
+ at(1)
18
+ end
19
+
20
+ ##
21
+ # Equivalent to `#at(2)`
22
+ def third
23
+ at(2)
24
+ end
25
+
26
+ ##
27
+ # @note Debatably bad idea. Maintained for backwards compatibility.
28
+ #
29
+ # If the array contains {AX::Element} objects and the elements respond
30
+ # to the method name then the method will be mapped across the array.
31
+ # In this case, you can artificially pluralize the attribute name and
32
+ # the lookup will singularize the method name for you.
33
+ #
34
+ # @example
35
+ #
36
+ # rows = outline.rows # :rows is already an attribute # edge case
37
+ # fields = rows.text_fields # you want the AX::TextField from each row
38
+ # fields.values # grab the values
39
+ #
40
+ # outline.rows.text_fields.values # all at once
41
+ #
42
+ def method_missing method, *args
43
+ smethod = TRANSLATOR.singularize(method.chomp('?'))
44
+ map do |x|
45
+ if !x.kind_of? AX::Element then super
46
+ elsif x.respond_to? method then x.send method, *args
47
+ else x.send smethod, *args
48
+ end
49
+ end
50
+ end
51
+
52
+
53
+ private
54
+
55
+ # @private
56
+ TRANSLATOR = Accessibility::Translator.instance
57
+
58
+ end
59
+
60
+
61
+ # AXElements extensions for `NSArray`.
62
+ class NSArray
63
+ include Accessibility::NSArrayCompat
64
+ end
@@ -0,0 +1,65 @@
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
+ end
65
+ end
@@ -0,0 +1,172 @@
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
+ def self.instance
20
+ @__instance__ ||= new
21
+ end
22
+
23
+ attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex
24
+
25
+ def initialize
26
+ @plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], [], [], {}, /(?=a)b/
27
+ end
28
+
29
+ # Specifies a new acronym. An acronym must be specified as it will appear in a camelized string. An underscore
30
+ # string that contains the acronym will retain the acronym when passed to `camelize`, `humanize`, or `titleize`.
31
+ # A camelized string that contains the acronym will maintain the acronym when titleized or humanized, and will
32
+ # convert the acronym into a non-delimited single lowercase word when passed to +underscore+.
33
+ #
34
+ # Examples:
35
+ # acronym 'HTML'
36
+ # titleize 'html' #=> 'HTML'
37
+ # camelize 'html' #=> 'HTML'
38
+ # underscore 'MyHTML' #=> 'my_html'
39
+ #
40
+ # The acronym, however, must occur as a delimited unit and not be part of another word for conversions to recognize it:
41
+ #
42
+ # acronym 'HTTP'
43
+ # camelize 'my_http_delimited' #=> 'MyHTTPDelimited'
44
+ # camelize 'https' #=> 'Https', not 'HTTPs'
45
+ # underscore 'HTTPS' #=> 'http_s', not 'https'
46
+ #
47
+ # acronym 'HTTPS'
48
+ # camelize 'https' #=> 'HTTPS'
49
+ # underscore 'HTTPS' #=> 'https'
50
+ #
51
+ # Note: Acronyms that are passed to `pluralize` will no longer be recognized, since the acronym will not occur as
52
+ # a delimited unit in the pluralized result. To work around this, you must specify the pluralized form as an
53
+ # acronym as well:
54
+ #
55
+ # acronym 'API'
56
+ # camelize(pluralize('api')) #=> 'Apis'
57
+ #
58
+ # acronym 'APIs'
59
+ # camelize(pluralize('api')) #=> 'APIs'
60
+ #
61
+ # `acronym` may be used to specify any word that contains an acronym or otherwise needs to maintain a non-standard
62
+ # capitalization. The only restriction is that the word must begin with a capital letter.
63
+ #
64
+ # Examples:
65
+ # acronym 'RESTful'
66
+ # underscore 'RESTful' #=> 'restful'
67
+ # underscore 'RESTfulController' #=> 'restful_controller'
68
+ # titleize 'RESTfulController' #=> 'RESTful Controller'
69
+ # camelize 'restful' #=> 'RESTful'
70
+ # camelize 'restful_controller' #=> 'RESTfulController'
71
+ #
72
+ # acronym 'McDonald'
73
+ # underscore 'McDonald' #=> 'mcdonald'
74
+ # camelize 'mcdonald' #=> 'McDonald'
75
+ def acronym(word)
76
+ @acronyms[word.downcase] = word
77
+ @acronym_regex = /#{@acronyms.values.join("|")}/
78
+ end
79
+
80
+ # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
81
+ # The replacement should always be a string that may include references to the matched data from the rule.
82
+ def plural(rule, replacement)
83
+ @uncountables.delete(rule) if rule.is_a?(String)
84
+ @uncountables.delete(replacement)
85
+ @plurals.insert(0, [rule, replacement])
86
+ end
87
+
88
+ # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
89
+ # The replacement should always be a string that may include references to the matched data from the rule.
90
+ def singular(rule, replacement)
91
+ @uncountables.delete(rule) if rule.is_a?(String)
92
+ @uncountables.delete(replacement)
93
+ @singulars.insert(0, [rule, replacement])
94
+ end
95
+
96
+ # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
97
+ # for strings, not regular expressions. You simply pass the irregular in singular and plural form.
98
+ #
99
+ # Examples:
100
+ # irregular 'octopus', 'octopi'
101
+ # irregular 'person', 'people'
102
+ def irregular(singular, plural)
103
+ @uncountables.delete(singular)
104
+ @uncountables.delete(plural)
105
+ if singular[0,1].upcase == plural[0,1].upcase
106
+ plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
107
+ plural(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + plural[1..-1])
108
+ singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
109
+ else
110
+ plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
111
+ plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
112
+ plural(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
113
+ plural(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
114
+ singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1])
115
+ singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1])
116
+ end
117
+ end
118
+
119
+ # Add uncountable words that shouldn't be attempted inflected.
120
+ #
121
+ # Examples:
122
+ # uncountable "money"
123
+ # uncountable "money", "information"
124
+ # uncountable %w( money information rice )
125
+ def uncountable(*words)
126
+ (@uncountables << words).flatten!
127
+ end
128
+
129
+ # Specifies a humanized form of a string by a regular expression rule or by a string mapping.
130
+ # When using a regular expression based replacement, the normal humanize formatting is called after the replacement.
131
+ # When a string is used, the human form should be specified as desired (example: 'The name', not 'the_name')
132
+ #
133
+ # Examples:
134
+ # human /_cnt$/i, '\1_count'
135
+ # human "legacy_col_person_name", "Name"
136
+ def human(rule, replacement)
137
+ @humans.insert(0, [rule, replacement])
138
+ end
139
+
140
+ # Clears the loaded inflections within a given scope (default is <tt>:all</tt>).
141
+ # Give the scope as a symbol of the inflection type, the options are: <tt>:plurals</tt>,
142
+ # <tt>:singulars</tt>, <tt>:uncountables</tt>, <tt>:humans</tt>.
143
+ #
144
+ # Examples:
145
+ # clear :all
146
+ # clear :plurals
147
+ def clear(scope = :all)
148
+ case scope
149
+ when :all
150
+ @plurals, @singulars, @uncountables, @humans = [], [], [], []
151
+ else
152
+ instance_variable_set "@#{scope}", []
153
+ end
154
+ end
155
+ end
156
+
157
+ # Yields a singleton instance of Inflector::Inflections so you can specify additional
158
+ # inflector rules.
159
+ #
160
+ # Example:
161
+ # ActiveSupport::Inflector.inflections do |inflect|
162
+ # inflect.uncountable "rails"
163
+ # end
164
+ def inflections
165
+ if block_given?
166
+ yield Inflections.instance
167
+ else
168
+ Inflections.instance
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,306 @@
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'
data/lib/ax_elements.rb CHANGED
@@ -1,30 +1,19 @@
1
- framework 'Cocoa'
2
-
3
- # check that the Accessibility APIs are enabled and are available to MacRuby
4
- begin
5
- unless AXAPIEnabled()
6
- raise RuntimeError, <<-EOS
7
- Universal Access is disabled on this machine. Please enable it in the System Preferences.
8
- EOS
9
- end
10
- rescue NoMethodError
11
- raise NotImplementedError, <<-EOS
12
- You need to install the latest BridgeSupport preview for AXElements to work.
13
- EOS
14
- end
15
-
16
- require 'ax_elements/macruby_extensions'
17
- require 'ax_elements/version'
18
- require 'ax_elements/core'
19
- require 'ax_elements/element'
20
-
21
- require 'ax_elements/accessibility/language'
22
-
23
- # Mix the language methods in to the TopLevel
24
- include Accessibility::Language
1
+ # Mix the language methods into the TopLevel
2
+ require 'accessibility/dsl'
3
+ include Accessibility::DSL
25
4
 
26
5
  ##
27
6
  # The Mac OS X dock application.
28
7
  #
29
8
  # @return [AX::Application]
30
- AX::DOCK = Accessibility.application_with_bundle_identifier 'com.apple.dock'
9
+ AX::DOCK = app_with_bundle_identifier 'com.apple.dock'
10
+
11
+ # Load explicitly defined elements that are optional
12
+ require 'ax/button'
13
+ require 'ax/radio_button'
14
+ require 'ax/row'
15
+ require 'ax/static_text'
16
+
17
+ # Misc things that we need to load
18
+ require 'ax_elements/nsarray_compat'
19
+ # require 'ax_elements/exception_workaround' # disable for now