AXElements 0.7.8 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/.yardopts +1 -10
  2. data/README.markdown +7 -14
  3. data/ext/accessibility/key_coder/key_coder.c +7 -0
  4. data/lib/AXElements.rb +0 -2
  5. data/lib/accessibility/core.rb +180 -123
  6. data/lib/accessibility/dsl.rb +310 -191
  7. data/lib/accessibility/enumerators.rb +9 -8
  8. data/lib/accessibility/errors.rb +7 -8
  9. data/lib/accessibility/factory.rb +16 -9
  10. data/lib/accessibility/graph.rb +68 -22
  11. data/lib/accessibility/highlighter.rb +86 -0
  12. data/lib/accessibility/pp_inspector.rb +4 -4
  13. data/lib/accessibility/qualifier.rb +11 -9
  14. data/lib/accessibility/string.rb +12 -4
  15. data/lib/accessibility/translator.rb +19 -10
  16. data/lib/accessibility/version.rb +3 -1
  17. data/lib/accessibility.rb +42 -17
  18. data/lib/ax/application.rb +90 -30
  19. data/lib/ax/button.rb +5 -2
  20. data/lib/ax/element.rb +133 -149
  21. data/lib/ax/pop_up_button.rb +12 -0
  22. data/lib/ax/radio_button.rb +5 -2
  23. data/lib/ax/row.rb +2 -2
  24. data/lib/ax/static_text.rb +5 -2
  25. data/lib/ax/systemwide.rb +24 -12
  26. data/lib/ax_elements/awesome_print.rb +13 -0
  27. data/lib/ax_elements/exception_workaround.rb +5 -0
  28. data/lib/ax_elements/nsarray_compat.rb +1 -0
  29. data/lib/ax_elements.rb +2 -1
  30. data/lib/minitest/ax_elements.rb +60 -4
  31. data/lib/mouse.rb +47 -20
  32. data/lib/rspec/expectations/ax_elements.rb +180 -88
  33. data/rakelib/doc.rake +7 -0
  34. data/test/helper.rb +2 -1
  35. data/test/integration/accessibility/test_dsl.rb +126 -18
  36. data/test/integration/accessibility/test_errors.rb +1 -1
  37. data/test/integration/ax/test_element.rb +17 -0
  38. data/test/integration/minitest/test_ax_elements.rb +33 -38
  39. data/test/integration/rspec/expectations/test_ax_elements.rb +68 -19
  40. data/test/sanity/accessibility/test_core.rb +45 -37
  41. data/test/sanity/accessibility/test_highlighter.rb +56 -0
  42. data/test/sanity/ax/test_application.rb +8 -0
  43. data/test/sanity/ax/test_element.rb +7 -3
  44. data/test/sanity/minitest/test_ax_elements.rb +2 -0
  45. data/test/sanity/rspec/expectations/test_ax_elements.rb +3 -0
  46. data/test/sanity/test_accessibility.rb +9 -0
  47. data/test/sanity/test_mouse.rb +2 -2
  48. metadata +11 -38
  49. data/docs/AccessibilityTips.markdown +0 -119
  50. data/docs/Acting.markdown +0 -340
  51. data/docs/Debugging.markdown +0 -165
  52. data/docs/Inspecting.markdown +0 -261
  53. data/docs/KeyboardEvents.markdown +0 -122
  54. data/docs/NewBehaviour.markdown +0 -151
  55. data/docs/Notifications.markdown +0 -271
  56. data/docs/Searching.markdown +0 -250
  57. data/docs/TestingExtensions.markdown +0 -52
  58. data/docs/images/all_the_buttons.jpg +0 -0
  59. data/docs/images/next_version.png +0 -0
  60. data/docs/images/ui_hierarchy.dot +0 -34
  61. data/docs/images/ui_hierarchy.png +0 -0
  62. data/lib/accessibility/debug.rb +0 -164
  63. data/test/integration/accessibility/test_debug.rb +0 -44
  64. data/test/sanity/accessibility/test_debug.rb +0 -63
@@ -31,8 +31,9 @@ require 'accessibility/version'
31
31
  # still work out ok.
32
32
  # @todo I feel a bit weird having to instantiate a new pointer every
33
33
  # time I want to fetch an attribute. Since allocations are costly,
34
- # it hurts performance a lot when it comes to searches. I wonder if
35
- # it would pay off to have a pool of pointers...
34
+ # and searching requires _many_ AXAPI calls, it hurts performance
35
+ # a lot when it comes to searches. I wonder if it would pay off
36
+ # to have a pool of pointers...
36
37
  #
37
38
  # Core abstraction layer that that interacts with OS X Accessibility
38
39
  # APIs (AXAPI). This is actually just a mixin for `AXUIElementRef` objects
@@ -85,8 +86,11 @@ module Accessibility::Core
85
86
  # will be unwrapped for you, if you expect to get a {CFRange} you
86
87
  # will be given a {Range} instead.
87
88
  #
88
- # As a convention, if the backing element is no longer alive then
89
- # you will receive `nil` for any attribute.
89
+ # As a convention, if the backing element is no longer alive, or
90
+ # the attribute does not exist, or a system failure occurs then
91
+ # you will receive `nil` for any attribute except for
92
+ # `KAXChildrenAttribute`. `KAXChildrenAttribute` will always return
93
+ # an array.
90
94
  #
91
95
  # @example
92
96
  # attribute KAXTitleAttribute # => "HotCocoa Demo"
@@ -94,14 +98,15 @@ module Accessibility::Core
94
98
  # attribute KAXParentAttribute # => #<AXUIElementRef>
95
99
  # attribute KAXNoValueAttribute # => nil
96
100
  #
97
- # @param [String] name an attribute constant
101
+ # @param [String]
98
102
  def attribute name
99
103
  ptr = Pointer.new :id
100
104
  case code = AXUIElementCopyAttributeValue(self, name, ptr)
101
105
  when 0
102
106
  ptr.value.to_ruby
103
107
  when KAXErrorNoValue, KAXErrorAttributeUnsupported,
104
- KAXErrorFailure, KAXErrorInvalidUIElement then
108
+ KAXErrorFailure, KAXErrorInvalidUIElement,
109
+ KAXErrorIllegalArgument then
105
110
  name == KAXChildrenAttribute ? [] : nil
106
111
  else
107
112
  handle_error code, name
@@ -109,13 +114,14 @@ module Accessibility::Core
109
114
  end
110
115
 
111
116
  ##
112
- # Shortcut for getting the `KAXRoleAttribute`.
117
+ # Shortcut for getting the `KAXRoleAttribute`. Remember that
118
+ # dead elements may return `nil` for their role.
113
119
  #
114
120
  # @example
115
121
  #
116
122
  # role # => KAXWindowRole
117
123
  #
118
- # @return [String]
124
+ # @return [String,nil]
119
125
  def role
120
126
  attribute KAXRoleAttribute
121
127
  end
@@ -136,7 +142,9 @@ module Accessibility::Core
136
142
  end
137
143
 
138
144
  ##
139
- # Shortcut for getting the `KAXChildrenAttribute`.
145
+ # Shortcut for getting the `KAXChildrenAttribute`. This is guaranteed
146
+ # to alwayl return an array or raise an exception, unless the object
147
+ # you are querying is behaving maliciously.
140
148
  #
141
149
  # @example
142
150
  #
@@ -159,6 +167,33 @@ module Accessibility::Core
159
167
  attribute KAXValueAttribute
160
168
  end
161
169
 
170
+ ##
171
+ # Get the process identifier (PID) of the application that the element
172
+ # belongs to.
173
+ #
174
+ # This method will return `0` if the element is dead or if the receiver
175
+ # is the the system wide element.
176
+ #
177
+ # @example
178
+ #
179
+ # pid # => 12345
180
+ # system_wide.pid # => 0
181
+ #
182
+ # @return [Fixnum]
183
+ def pid
184
+ @pid ||= (
185
+ ptr = Pointer.new :int
186
+ case code = AXUIElementGetPid(self, ptr)
187
+ when 0
188
+ ptr.value
189
+ when KAXErrorInvalidUIElement
190
+ self == system_wide ? 0 : handle_error(code)
191
+ else
192
+ handle_error code
193
+ end
194
+ )
195
+ end
196
+
162
197
  ##
163
198
  # Get the size of the array for attributes that would return an array.
164
199
  # When performance matters, this is much faster than getting the array
@@ -172,7 +207,7 @@ module Accessibility::Core
172
207
  # size_of KAXChildrenAttribute # => 19
173
208
  # size_of KAXRowsAttribute # => 100
174
209
  #
175
- # @param [String] name an attribute constant
210
+ # @param [String]
176
211
  # @return [Number]
177
212
  def size_of name
178
213
  ptr = Pointer.new :long_long
@@ -188,14 +223,18 @@ module Accessibility::Core
188
223
  end
189
224
 
190
225
  ##
191
- # Returns whether or not an attribute is writable.
226
+ # Returns whether or not an attribute is writable. You should check
227
+ # writability before trying call {#set} for the attribute.
228
+ #
229
+ # In case of internal error or if the element dies, this method will
230
+ # return `false`.
192
231
  #
193
232
  # @example
194
233
  #
195
234
  # writable? KAXSizeAttribute # => true
196
235
  # writable? KAXTitleAttribute # => false
197
236
  #
198
- # @param [String] name an attribute constant
237
+ # @param [String]
199
238
  def writable? name
200
239
  ptr = Pointer.new :bool
201
240
  case code = AXUIElementIsAttributeSettable(self, name, ptr)
@@ -210,24 +249,24 @@ module Accessibility::Core
210
249
  end
211
250
 
212
251
  ##
213
- # @note This method does not check writability of the attribute
214
- # you are setting. If you need to check, use {#writable?}
215
- # first.
216
- #
217
- # Set the given value to the given attribute. You do not need to
252
+ # Set the given value for the given attribute. You do not need to
218
253
  # worry about wrapping objects first, `Range` objects will also
219
254
  # be automatically converted into `CFRange` objects and then
220
255
  # wrapped.
221
256
  #
222
- # Unlike when reading attributes, writing to a dead element will
223
- # raise an exception.
257
+ # This method does not check writability of the attribute you are
258
+ # setting. If you need to check, use {#writable?} first to check.
259
+ #
260
+ # Unlike when reading attributes, writing to a dead element, and
261
+ # other error conditions, will raise an exception.
224
262
  #
225
263
  # @example
264
+ #
226
265
  # set KAXValueAttribute, "hi" # => "hi"
227
266
  # set KAXSizeAttribute, [250,250] # => [250,250]
228
267
  # set KAXVisibleRangeAttribute, 0..-3 # => 0..-3
229
268
  #
230
- # @param [String] name an attribute constant
269
+ # @param [String]
231
270
  def set name, value
232
271
  code = AXUIElementSetAttributeValue(self, name, value.to_ax)
233
272
  if code.zero?
@@ -248,6 +287,9 @@ module Accessibility::Core
248
287
  # Most elements do not have parameterized attributes, but the ones
249
288
  # that do, have many.
250
289
  #
290
+ # Similar to {#attributes}, this method will also return an empty
291
+ # array if the element is dead.
292
+ #
251
293
  # @example
252
294
  #
253
295
  # parameterized_attributes # => ["AXStringForRange", ...]
@@ -266,20 +308,24 @@ module Accessibility::Core
266
308
  end
267
309
 
268
310
  ##
269
- # Fetch the given pramaeterized attribute value using the given parameter.
270
- # Only `AXUIElementRef` objects will be given raw, `Boxed` objects will be
271
- # unwrapped for you automatically and `CFRange` objects will be turned into
272
- # `Range` objects. Similarly, you do not need to worry about wrapping the
273
- # parameter as that will be done for you.
311
+ # Fetch the given pramaeterized attribute value for the given parameter.
312
+ # Only `AXUIElementRef` objects will be returned in their raw form,
313
+ # {Boxed} objects will be unwrapped for you automatically and {CFRange}
314
+ # objects will be turned into {Range} objects. Similarly, you do not
315
+ # need to worry about wrapping the parameter as that will be done for you.
316
+ #
317
+ # As a convention, if the backing element is no longer alive, or the
318
+ # attribute does not exist, or a system failure occurs then you will
319
+ # receive `nil`.
274
320
  #
275
321
  # @example
276
322
  #
277
- # attribute KAXStringForRangeParameterizedAttribute, for_param: 1..10
323
+ # parameterized_attribute KAXStringForRangeParameterizedAttribute, 1..10
278
324
  # # => "ello, worl"
279
325
  #
280
- # @param [String] attr an attribute constant
281
- # @param [Object] param
282
- def attribute name, for_parameter: param
326
+ # @param [String]
327
+ # @param [Object]
328
+ def parameterized_attribute name, param
283
329
  ptr = Pointer.new :id
284
330
  case code = AXUIElementCopyParameterizedAttributeValue(self, name, param.to_ax, ptr)
285
331
  when 0
@@ -297,8 +343,8 @@ module Accessibility::Core
297
343
 
298
344
  ##
299
345
  # Get the list of actions that the element can perform. If an element
300
- # does not have actions, then an empty list will be returned.
301
- # Dead elements will also return an empty array.
346
+ # does not have actions, then an empty list will be returned. Dead
347
+ # elements will also return an empty array.
302
348
  #
303
349
  # @example
304
350
  #
@@ -318,7 +364,8 @@ module Accessibility::Core
318
364
 
319
365
  ##
320
366
  # Ask an element to perform the given action. This method will always
321
- # return true or raise an exception. Actions should never fail.
367
+ # return true or raise an exception. Actions should never fail, but
368
+ # there are some extreme edge cases (e.g. out of memory, etc.).
322
369
  #
323
370
  # Unlike when reading attributes, performing an action on a dead element
324
371
  # will raise an exception.
@@ -327,7 +374,7 @@ module Accessibility::Core
327
374
  #
328
375
  # perform KAXPressAction # => true
329
376
  #
330
- # @param [String] action an action constant
377
+ # @param [String]
331
378
  # @return [Boolean]
332
379
  def perform action
333
380
  code = AXUIElementPerformAction(self, action)
@@ -351,7 +398,7 @@ module Accessibility::Core
351
398
  # keyup).
352
399
  #
353
400
  # You can learn more about keyboard events from the
354
- # {file:docs/KeyboardEvents.markdown Keyboard Events} documentation.
401
+ # [Keyboard Events documentation](http://github.com/Marketcircle/AXElements/wiki/Keyboarding).
355
402
  #
356
403
  # @example
357
404
  #
@@ -365,14 +412,12 @@ module Accessibility::Core
365
412
  events.each do |event|
366
413
  code = AXUIElementPostKeyboardEvent(self, 0, *event)
367
414
  handle_error code unless code.zero?
368
- sleep KEY_RATE
415
+ sleep @@key_rate
369
416
  end
370
417
  sleep 0.1 # in many cases, UI is not done updating right away
371
418
  end
372
419
 
373
420
  ##
374
- # @todo Make this runtime configurable.
375
- #
376
421
  # The delay between key presses. The default value is `0.01`, which
377
422
  # should be about 50 characters per second (down and up are separate
378
423
  # events).
@@ -382,12 +427,15 @@ module Accessibility::Core
382
427
  # were tried, but were way too big.
383
428
  #
384
429
  # @return [Number]
385
- KEY_RATE = case ENV['KEY_RATE']
386
- when 'VERY_SLOW' then 0.9
387
- when 'SLOW' then 0.09
388
- when nil then 0.009
389
- else ENV['KEY_RATE'].to_f
390
- end
430
+ def key_rate
431
+ @@key_rate
432
+ end
433
+ @@key_rate = case ENV['KEY_RATE']
434
+ when 'VERY_SLOW' then 0.9
435
+ when 'SLOW' then 0.09
436
+ when nil then 0.009
437
+ else ENV['KEY_RATE'].to_f
438
+ end
391
439
 
392
440
 
393
441
  # @group Element Hierarchy Entry Points
@@ -401,13 +449,16 @@ module Accessibility::Core
401
449
  # system (origin is in the top-left, increasing downward and to the right
402
450
  # as if reading a book in English).
403
451
  #
404
- # If more than one element is at the position then the
405
- # z-order of the elements will be used to determine which is
406
- # "on top".
452
+ # If more than one element is at the position then the z-order of the
453
+ # elements will be used to determine which is "on top".
454
+ #
455
+ # This method will safely return `nil` if there is no UI element at the
456
+ # give point.
407
457
  #
408
458
  # @example
409
459
  #
410
460
  # element_at [453, 200] # table
461
+ # element_at CGPoint.new(453, 200) # table
411
462
  #
412
463
  # @param [#to_point]
413
464
  # @return [AXUIElementRef,nil]
@@ -426,15 +477,15 @@ module Accessibility::Core
426
477
  end
427
478
 
428
479
  ##
429
- # Get the application accessibility object/token for an application
430
- # given the process identifier (PID) for that application.
480
+ # Get the application object object/token for an application given the
481
+ # process identifier (PID) for that application.
431
482
  #
432
483
  # @example
433
484
  #
434
485
  # app = application_for 54743 # => #<AXUIElementRefx00000000>
435
486
  # CFShow(app)
436
487
  #
437
- # @param [Fixnum]
488
+ # @param [Number]
438
489
  # @return [AXUIElementRef]
439
490
  def application_for pid
440
491
  spin_run_loop
@@ -505,17 +556,19 @@ module Accessibility::Core
505
556
 
506
557
  ##
507
558
  # @todo Should passing around a context be supported?
559
+ # @todo If a block is passed instead of an observer, then implicitly create
560
+ # the observer.
508
561
  #
509
562
  # Register a notification observer for a specific event.
510
563
  #
511
564
  # @example
512
565
  #
513
- # register observer, to_receive: KAXWindowCreatedNotification
566
+ # register observer, KAXWindowCreatedNotification
514
567
  #
515
568
  # @param [AXObserverRef]
516
569
  # @param [String]
517
570
  # @return [Boolean]
518
- def register observer, to_receive: notif
571
+ def register observer, notif
519
572
  case code = AXObserverAddNotification(observer, self, notif, nil)
520
573
  when 0 then true
521
574
  else handle_error code, notif, observer, nil, nil
@@ -525,10 +578,14 @@ module Accessibility::Core
525
578
  ##
526
579
  # Unregister a notification that has been previously setup.
527
580
  #
581
+ # @example
582
+ #
583
+ # unregister observer, KAXWindowCreatedNotification
584
+ #
528
585
  # @param [AXObserverRef]
529
586
  # @param [String]
530
587
  # @return [Boolean]
531
- def unregister observer, from_receiving: notif
588
+ def unregister observer, notif
532
589
  case code = AXObserverRemoveNotification(observer, self, notif)
533
590
  when 0 then true
534
591
  else handle_error code, notif, observer, nil, nil
@@ -538,43 +595,6 @@ module Accessibility::Core
538
595
 
539
596
  # @group Misc.
540
597
 
541
- ##
542
- # Ask whether or not AXAPI is enabled.
543
- #
544
- # @example
545
- #
546
- # enabled? # => true
547
- #
548
- # # After unchecking "Enable access for assistive devices" in System Prefs
549
- # enabled? # => false
550
- #
551
- def enabled?
552
- AXAPIEnabled()
553
- end
554
-
555
- ##
556
- # Get the process identifier (PID) of the application that the element
557
- # belongs to.
558
- #
559
- # @example
560
- #
561
- # pid # => 12345
562
- #
563
- # @return [Fixnum]
564
- def pid
565
- @pid ||= (
566
- ptr = Pointer.new :int
567
- case code = AXUIElementGetPid(self, ptr)
568
- when 0
569
- ptr.value
570
- when KAXErrorInvalidUIElement
571
- self == system_wide ? 0 : handle_error(code)
572
- else
573
- handle_error code
574
- end
575
- )
576
- end
577
-
578
598
  ##
579
599
  # Create a new reference to the system wide object. This is very useful when
580
600
  # working with the system wide object as caching the system wide reference
@@ -590,7 +610,8 @@ module Accessibility::Core
590
610
  end
591
611
 
592
612
  ##
593
- # Returns the application reference that the element belongs to.
613
+ # Returns the application reference for the application that the receiver
614
+ # belongs to.
594
615
  #
595
616
  # @return [AXUIElementRef]
596
617
  def application
@@ -605,7 +626,7 @@ module Accessibility::Core
605
626
  #
606
627
  # spin_run_loop # not much to it
607
628
  #
608
- # @return [self] returns the receiver
629
+ # @return [void]
609
630
  def spin_run_loop
610
631
  NSRunLoop.currentRunLoop.runUntilDate Time.now
611
632
  end
@@ -619,8 +640,8 @@ module Accessibility::Core
619
640
  #
620
641
  # Setting the global timeout to `0` seconds will reset the timeout value
621
642
  # to the system default. Apple does not appear to have publicly documented
622
- # what the system default is though, so I can't tell you what that value
623
- # is.
643
+ # what the system default is and there is no API to check what the value
644
+ # is set to, so I can't tell you what the current value is.
624
645
  #
625
646
  # @param [Number]
626
647
  # @return [Number]
@@ -647,18 +668,21 @@ module Accessibility::Core
647
668
  raise klass, msg, caller(1)
648
669
  end
649
670
 
671
+ # @return [String]
650
672
  def handle_failure *args
651
673
  "A system failure occurred with #{inspect}, stopping to be safe"
652
674
  end
653
675
 
676
+ # @todo this is a pretty crappy way of handling the various illegal
677
+ # argument cases
678
+ # @return [String]
654
679
  def handle_illegal_argument *args
655
680
  case args.size
656
681
  when 0
657
682
  "#{inspect} is not an AXUIElementRef"
658
683
  when 1
659
- "Either the element #{inspect} " +
660
- "or the attribute/action/callback #{args.first.inspect} " +
661
- "is not a legal argument"
684
+ "Either the element #{inspect} or the attribute/action/callback " +
685
+ "#{args.first.inspect} is not a legal argument"
662
686
  when 2
663
687
  "You can't get/set #{args.first.inspect} with/to " +
664
688
  "#{args[1].inspect} for #{inspect}"
@@ -666,70 +690,76 @@ module Accessibility::Core
666
690
  "The point #{args.first.to_point.inspect} is not a valid point, " +
667
691
  "or #{inspect} is not an AXUIElementRef"
668
692
  when 4
669
- "Either the observer #{args[1].inspect}, " +
670
- "the element #{inspect}, or " +
671
- "the notification #{args.first.inspect} " +
672
- "is not a legitimate argument"
693
+ "Either the observer #{args[1].inspect}, the element #{inspect}, " +
694
+ "or the notification #{args.first.inspect} is not a legitimate argument"
673
695
  end
674
696
  end
675
697
 
698
+ # @return [String]
676
699
  def handle_invalid_element *args
677
700
  "#{inspect} is no longer a valid reference"
678
701
  end
679
702
 
703
+ # @return [String]
680
704
  def handle_invalid_observer *args
681
- "#{args[1].inspect} is no longer a valid observer for " +
682
- "#{inspect} or was never valid"
705
+ "#{args[1].inspect} is no longer a valid observer for #{inspect} or was never valid"
683
706
  end
684
707
 
685
708
  # @param [AXUIElementRef]
709
+ # @return [String]
686
710
  def handle_cannot_complete *args
687
711
  spin_run_loop
688
712
  app = NSRunningApplication.runningApplicationWithProcessIdentifier pid
689
713
  if app
690
- "An unspecified error occurred using #{inspect} with AXAPI" +
691
- ", maybe a timeout :("
714
+ "An unspecified error occurred using #{inspect} with AXAPI, maybe a timeout :("
692
715
  else
693
716
  "Application for pid=#{pid} is no longer running. Maybe it crashed?"
694
717
  end
695
718
  end
696
719
 
720
+ # @return [String]
697
721
  def handle_attr_unsupported *args
698
722
  "#{inspect} does not have a #{args.first.inspect} attribute"
699
723
  end
700
724
 
725
+ # @return [String]
701
726
  def handle_action_unsupported *args
702
727
  "#{inspect} does not have a #{args.first.inspect} action"
703
728
  end
704
729
 
730
+ # @return [String]
705
731
  def handle_notif_unsupported *args
706
732
  "#{inspect} does not support the #{args.first.inspect} notification"
707
733
  end
708
734
 
735
+ # @return [String]
709
736
  def handle_not_implemented *args
710
737
  "The program that owns #{inspect} does not work with AXAPI properly"
711
738
  end
712
739
 
713
740
  # @todo Does this really neeed to raise an exception? Seems
714
741
  # like a warning would be sufficient.
742
+ # @return [String]
715
743
  def handle_notif_registered *args
716
- "You have already registered to hear about #{args[0].inspect} " +
717
- "from #{inspect}"
744
+ "You have already registered to hear about #{args[0].inspect} from #{inspect}"
718
745
  end
719
746
 
747
+ # @return [String]
720
748
  def handle_notif_not_registered *args
721
- "You have not registered to hear about #{args[0].inspect} " +
722
- "from #{inspect}"
749
+ "You have not registered to hear about #{args[0].inspect} from #{inspect}"
723
750
  end
724
751
 
752
+ # @return [String]
725
753
  def handle_api_disabled *args
726
- 'AXAPI has been disabled'
754
+ "AXAPI has been disabled"
727
755
  end
728
756
 
757
+ # @return [String]
729
758
  def handle_param_attr_unsupported *args
730
759
  "#{inspect} does not have a #{args[0].inspect} parameterized attribute"
731
760
  end
732
761
 
762
+ # @return [String]
733
763
  def handle_not_enough_precision
734
764
  'AXAPI said there was not enough precision ¯\(°_o)/¯'
735
765
  end
@@ -738,24 +768,18 @@ module Accessibility::Core
738
768
 
739
769
 
740
770
  ##
741
- # @private
742
- #
743
771
  # `Pointer` type encoding for `CFArrayRef` objects.
744
772
  #
745
773
  # @return [String]
746
774
  ARRAY = '^{__CFArray}'
747
775
 
748
776
  ##
749
- # @private
750
- #
751
777
  # `Pointer` type encoding for `AXUIElementRef` objects.
752
778
  #
753
779
  # @return [String]
754
780
  ELEMENT = '^{__AXUIElement}'
755
781
 
756
782
  ##
757
- # @private
758
- #
759
783
  # `Pointer` type encoding for `AXObserverRef` objects.
760
784
  #
761
785
  # @return [String]
@@ -821,7 +845,7 @@ module Accessibility::ValueUnwrapper
821
845
  end
822
846
  end
823
847
 
824
- # hack to find the proper class
848
+ # hack to find the __NSCFType class
825
849
  klass = AXUIElementCreateSystemWide().class
826
850
  klass.send :include, Accessibility::Core
827
851
  klass.send :include, Accessibility::ValueUnwrapper
@@ -838,7 +862,7 @@ class Boxed
838
862
  #
839
863
  # @return [Number]
840
864
  def self.ax_value
841
- raise NotImplementedError, "#{self.class} cannot be wraped"
865
+ raise NotImplementedError, "#{inspect}:#{self.class} cannot be wraped"
842
866
  end
843
867
 
844
868
  ##
@@ -848,8 +872,8 @@ class Boxed
848
872
  #
849
873
  # @example
850
874
  #
851
- # CGPointMake(12, 34).to_ax # => #<AXValueRef:0x455678e2>
852
- # CGSizeMake(56, 78).to_ax # => #<AXValueRef:0x555678e2>
875
+ # CGPoint.new(12, 34).to_ax # => #<AXValueRef:0x455678e2>
876
+ # CGSize.new(56, 78).to_ax # => #<AXValueRef:0x555678e2>
853
877
  #
854
878
  # @return [AXValueRef]
855
879
  def to_ax
@@ -861,18 +885,34 @@ class Boxed
861
885
  end
862
886
 
863
887
  # AXElements extensions for `CFRange`.
864
- class << CFRange; def ax_value; KAXValueCFRangeType; end end
888
+ class << CFRange
889
+ # (see Boxed.ax_value)
890
+ def ax_value; KAXValueCFRangeType end
891
+ end
865
892
  # AXElements extensions for `CGSize`.
866
- class << CGSize; def ax_value; KAXValueCGSizeType; end end
893
+ class << CGSize
894
+ # (see Boxed.ax_value)
895
+ def ax_value; KAXValueCGSizeType end
896
+ end
867
897
  # AXElements extensions for `CGRect`.
868
- class << CGRect; def ax_value; KAXValueCGRectType; end end
898
+ class << CGRect
899
+ # (see Boxed.ax_value)
900
+ def ax_value; KAXValueCGRectType end
901
+ end
869
902
  # AXElements extensions for `CGPoint`.
870
- class << CGPoint; def ax_value; KAXValueCGPointType; end end
903
+ class << CGPoint
904
+ # (see Boxed.ax_value)
905
+ def ax_value; KAXValueCGPointType end
906
+ end
871
907
 
872
908
 
873
909
  # AXElements extensions for `NSObject`.
874
910
  class NSObject
911
+ ##
912
+ # Return an object safe for passing to AXAPI.
875
913
  def to_ax; self end
914
+ ##
915
+ # Return a usable object from an AXAPI pointer.
876
916
  def to_ruby; self end
877
917
  end
878
918
 
@@ -914,3 +954,20 @@ class CGPoint
914
954
  # @return [CGPoint]
915
955
  def to_point; self end
916
956
  end
957
+
958
+
959
+ ##
960
+ # AXElements extensions for `NSURL`.
961
+ class NSString
962
+ ##
963
+ # Create an NSURL using the receiver as the initialization string.
964
+ # If the receiver is not a valid URL then `nil` will be returned.
965
+ #
966
+ # This exists because of
967
+ # [rdar://11207662](http://openradar.appspot.com/11207662).
968
+ #
969
+ # @return [NSURL,nil]
970
+ def to_url
971
+ NSURL.URLWithString self
972
+ end
973
+ end