vapir-common 1.7.2 → 1.8.0

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.
@@ -0,0 +1,378 @@
1
+ require 'vapir-common/container'
2
+
3
+ module Vapir
4
+ # this module is for methods that should go on both common element modules (ie, TextField) as well
5
+ # as browser-specific element classes (ie, Firefox::TextField).
6
+ module ElementClassAndModuleMethods
7
+ # takes an element_object (JsshObject or WIN32OLE), and finds the most specific class
8
+ # that is < self whose specifiers match it. Returns an instance of that class using the given
9
+ # element_object.
10
+ #
11
+ # second argument, extra, is passed as the 'extra' argument to the Element constructor (see its documentation).
12
+ #
13
+ # if you give a different how/what (third and fourth arguments, optional), then those are passed
14
+ # to the Element constructor.
15
+ def factory(element_object, extra={}, *howwhat)
16
+ curr_klass=self
17
+ # since this gets included in the Element modules, too, check where we are
18
+ unless self.is_a?(Class) && self < Vapir::Element
19
+ raise TypeError, "factory was called on #{self} (#{self.class}), which is not a Class that is < Element"
20
+ end
21
+ how, what = *case howwhat.length
22
+ when 0
23
+ [:element_object, element_object]
24
+ when 2
25
+ howwhat
26
+ else
27
+ raise ArgumentError, "There should be either no how/what arguments, or two (one how, one what); got #{howwhat.length}: #{howwhat.inspect}"
28
+ end
29
+ ObjectSpace.each_object(Class) do |klass|
30
+ if klass < curr_klass
31
+ Vapir::ElementObjectCandidates.match_candidates([element_object], klass.specifiers, klass.all_dom_attr_aliases) do |match|
32
+ curr_klass=klass
33
+ break
34
+ end
35
+ end
36
+ end
37
+ curr_klass.new(how, what, extra.merge(:element_object => element_object, :locate => false))
38
+ end
39
+
40
+ # takes any number of arguments, where each argument is either:
41
+ # - a symbol or strings representing a method that is the same in ruby and on the dom
42
+ # - or a hash of key/value pairs where each key is a dom attribute, and each value
43
+ # is a is a corresponding ruby method name or list of ruby method names.
44
+ def dom_attr(*dom_attrs)
45
+ dom_attrs.each do |arg|
46
+ hash=arg.is_a?(Hash) ? arg : arg.is_a?(Symbol) || arg.is_a?(String) ? {arg => arg} : raise(ArgumentError, "don't know what to do with arg #{arg.inspect} (#{arg.class})")
47
+ hash.each_pair do |dom_attr, ruby_method_names|
48
+ ruby_method_names= ruby_method_names.is_a?(Array) ? ruby_method_names : [ruby_method_names]
49
+ class_array_append 'dom_attrs', dom_attr
50
+ ruby_method_names.each do |ruby_method_name|
51
+ dom_attr_locate_alias(dom_attr, ruby_method_name)
52
+ define_method ruby_method_name do
53
+ method_from_element_object(dom_attr)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ # creates aliases for locating by
61
+ def dom_attr_locate_alias(dom_attr, alias_name)
62
+ dom_attr_aliases=class_hash_get('dom_attr_aliases')
63
+ dom_attr_aliases[dom_attr] ||= Set.new
64
+ dom_attr_aliases[dom_attr] << alias_name
65
+ end
66
+
67
+ # dom_function is about the same as dom_attr, but dom_attr doesn't take arguments.
68
+ # also, dom_function methods call #wait; dom_attr ones don't.
69
+ def dom_function(*dom_functions)
70
+ dom_functions.each do |arg|
71
+ hash=arg.is_a?(Hash) ? arg : arg.is_a?(Symbol) || arg.is_a?(String) ? {arg => arg} : raise(ArgumentError, "don't know what to do with arg #{arg.inspect} (#{arg.class})")
72
+ hash.each_pair do |dom_function, ruby_method_names|
73
+ ruby_method_names= ruby_method_names.is_a?(Array) ? ruby_method_names : [ruby_method_names]
74
+ class_array_append 'dom_functions', dom_function
75
+ ruby_method_names.each do |ruby_method_name|
76
+ define_method ruby_method_name do |*args|
77
+ result=method_from_element_object(dom_function, *args)
78
+ wait
79
+ result
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ # dom_setter takes arguments in the same format as dom_attr, but sends the given setter method (plus = sign)
87
+ # to the element object. eg,
88
+ # module TextField
89
+ # dom_setter :value
90
+ # dom_setter :maxLength => :maxlength
91
+ # end
92
+ # the #value= method in ruby will send to #value= on the element object
93
+ # the #maxlength= method in ruby will send to #maxLength= on the element object (note case difference).
94
+ def dom_setter(*dom_setters)
95
+ dom_setters.each do |arg|
96
+ hash=arg.is_a?(Hash) ? arg : arg.is_a?(Symbol) || arg.is_a?(String) ? {arg => arg} : raise(ArgumentError, "don't know what to do with arg #{arg.inspect} (#{arg.class})")
97
+ hash.each_pair do |dom_setter, ruby_method_names|
98
+ ruby_method_names= ruby_method_names.is_a?(Array) ? ruby_method_names : [ruby_method_names]
99
+ class_array_append 'dom_setters', dom_setter
100
+ ruby_method_names.each do |ruby_method_name|
101
+ define_method(ruby_method_name.to_s+'=') do |value|
102
+ element_object.send(dom_setter.to_s+'=', value)
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ # defines an element collection method on the given element - such as SelectList#options
110
+ # or Table#rows. takes the name of the dom method that returns a collection
111
+ # of element objects, a ruby method name, and an element class - actually this is
112
+ # generally an Element module; this method goes ahead and finds the browser-specific
113
+ # class that will actually be instantiated. the defined method returns an
114
+ # ElementCollection.
115
+ def element_collection(dom_attr, ruby_method_name, element_class)
116
+ define_method ruby_method_name do
117
+ assert_exists do
118
+ ElementCollection.new(self, element_class_for(element_class), extra_for_contained.merge(:candidates => dom_attr))
119
+ end
120
+ end
121
+ end
122
+
123
+ # notes the given arguments to be inspected by #inspect and #to_s on each inheriting element.
124
+ # each argument may be a symbol, in which case the corresponding method is called on the element, or
125
+ # a hash, with the following keys:
126
+ # - :label - how the the attribute is labeled in the string returned by #inspect or #to_s.
127
+ # should be a string or symbol (but anything works; #to_s is called on the label).
128
+ # - :value - can be one of:
129
+ # - String starting with '@' - assumes this is an instance variable; gets the value of that instance variable
130
+ # - Symbol - assumes it is a method name, gives this to #send on the element. this is most commonly-used.
131
+ # - Proc - calls the proc, giving this element as an argument. should return a string. #to_s is called on its return value.
132
+ # - anything else - just assumes that that is the value that is wanted in the string.
133
+ # (see Element#attributes_for_stringifying)
134
+ # - :if - if defined, should be a proc that returns false/nil if this should not be included in the
135
+ # string, or anything else (that is, any value considered 'true') if it should. this element is passed
136
+ # as an argument to the proc.
137
+ def inspect_these(*inspect_these)
138
+ inspect_these.each do |inspect_this|
139
+ attribute_to_inspect=case inspect_this
140
+ when Hash
141
+ inspect_this
142
+ when Symbol
143
+ {:label => inspect_this, :value => inspect_this}
144
+ else
145
+ raise ArgumentError, "unrecognized thing to inspect: #{inspect_this} (#{inspect_this.class})"
146
+ end
147
+ class_array_append 'attributes_to_inspect', attribute_to_inspect
148
+ end
149
+ end
150
+ alias inspect_this inspect_these
151
+ # inspect_this_if(inspect_this, &block) is shorthand for
152
+ # inspect_this({:label => inspect_this, :value => inspect_this, :if => block)
153
+ # if a block isn't given, the :if proc is the result of sending the inspect_this symbol to the element.
154
+ # if inspect_this isn't a symbol, and no block is given, raises ArgumentError.
155
+ def inspect_this_if inspect_this, &block
156
+ unless inspect_this.is_a?(Symbol) || block
157
+ raise ArgumentError, "Either give a block, or specify a symbol as the first argument, instead of #{inspect_this.inspect} (#{inspect_this.class})"
158
+ end
159
+ to_inspect={:label => inspect_this, :value => inspect_this}
160
+ to_inspect[:if]= block || proc {|element| element.send(inspect_this) }
161
+ class_array_append 'attributes_to_inspect', to_inspect
162
+ end
163
+
164
+ def class_array_append(name, *elements)
165
+ =begin
166
+ name='@@'+name.to_s
167
+ unless self.class_variable_defined?(name)
168
+ class_variable_set(name, [])
169
+ end
170
+ class_variable_get(name).push(*elements)
171
+ =end
172
+ name=name.to_s.capitalize
173
+ unless self.const_defined?(name)
174
+ self.const_set(name, [])
175
+ end
176
+ self.const_get(name).push(*elements)
177
+ end
178
+
179
+ def class_array_get(name)
180
+ # just return the value of appending nothing
181
+ class_array_append(name)
182
+ end
183
+ def class_hash_merge(name, hash)
184
+ name=name.to_s.capitalize
185
+ unless self.const_defined?(name)
186
+ self.const_set(name, {})
187
+ end
188
+ self.const_get(name).merge!(hash)
189
+ end
190
+ def class_hash_get(name)
191
+ class_hash_merge(name, {})
192
+ end
193
+ def set_or_get_class_var(class_var, *arg)
194
+ if arg.length==0
195
+ class_variable_defined?(class_var) ? class_variable_get(class_var) : nil
196
+ elsif arg.length==1
197
+ class_variable_set(class_var, arg.first)
198
+ else
199
+ raise ArgumentError, "#{arg.length} arguments given; expected one or two. arguments were #{arg.inspect}"
200
+ end
201
+ end
202
+ def default_how(*arg)
203
+ set_or_get_class_var('@@default_how', *arg)
204
+ end
205
+ def add_container_method_extra_args(*args)
206
+ class_array_append('container_method_extra_args', *args)
207
+ end
208
+ def container_method_extra_args
209
+ class_array_get('container_method_extra_args')
210
+ end
211
+ def specifiers
212
+ class_array_get 'specifiers'
213
+ end
214
+ def container_single_methods
215
+ class_array_get 'container_single_methods'
216
+ end
217
+ def container_collection_methods
218
+ class_array_get 'container_collection_methods'
219
+ end
220
+
221
+ def parent_element_module(*arg)
222
+ defined_parent=set_or_get_class_var('@@parent_element_module', *arg)
223
+ defined_parent || (self==Watir::Element ? nil : Watir::Element)
224
+ end
225
+ def all_dom_attrs
226
+ super_attrs= parent_element_module ? parent_element_module.all_dom_attrs : []
227
+ super_attrs + class_array_get('dom_attrs')
228
+ end
229
+ def all_dom_attr_aliases
230
+ aliases=class_hash_get('dom_attr_aliases').dup
231
+ super_aliases= parent_element_module ? parent_element_module.all_dom_attr_aliases : {}
232
+ super_aliases.each_pair do |attr, alias_list|
233
+ aliases[attr] = (aliases[attr] || Set.new) + alias_list
234
+ end
235
+ aliases
236
+ end
237
+ end
238
+ module ElementHelper
239
+ def add_specifier(specifier)
240
+ class_array_append 'specifiers', specifier
241
+ end
242
+
243
+ def container_single_method(*method_names)
244
+ class_array_append 'container_single_methods', *method_names
245
+ element_module=self
246
+ method_names.each do |method_name|
247
+ Vapir::Element.module_eval do
248
+ # these methods (Element#parent_table, Element#parent_div, etc)
249
+ # iterate through parent nodes looking for a parent of the specified
250
+ # type. if no element of that type is found which is a parent of
251
+ # self, returns nil.
252
+ define_method("parent_#{method_name}") do
253
+ element_class=element_class_for(element_module)
254
+ parentNode=element_object
255
+ while true
256
+ parentNode=parentNode.parentNode
257
+ unless parentNode && parentNode != document_object # don't ascend up to the document. #TODO/Fix - for IE, comparing WIN32OLEs doesn't really work, this comparison is pointless.
258
+ return nil
259
+ end
260
+ matched=Vapir::ElementObjectCandidates.match_candidates([parentNode], element_class.specifiers, element_class.all_dom_attr_aliases)
261
+ if matched.size > 0
262
+ return element_class.new(:element_object, parentNode, extra_for_contained) # this is a little weird, passing extra_for_contained so that this is the container of its parent.
263
+ end
264
+ end
265
+ end
266
+ end
267
+ element_module = self
268
+ # define both bang-methods (like #text_field!) and not (#text_field) with corresponding :locate option for element_by_howwhat
269
+ [ {:method_name => method_name, :locate => true},
270
+ {:method_name => method_name.to_s+'!', :locate => :assert},
271
+ {:method_name => method_name.to_s+'?', :locate => :nil_unless_exists},
272
+ ].each do |method_hash|
273
+ Vapir::Container.module_eval do
274
+ define_method(method_hash[:method_name]) do |*args| # can't take how, what as args because blocks don't do default values so it will want 2 args
275
+ #locate! # make sure self is located before trying contained stuff
276
+ how=args.shift
277
+ what=args.shift
278
+ what_args=args
279
+ other_attribute_keys=element_class_for(element_module).container_method_extra_args
280
+ if what_args.size>other_attribute_keys.length
281
+ raise ArgumentError, "\##{method_hash[:method_name]} takes 1 to #{2+other_attribute_keys.length} arguments! Got #{([how, what]+what_args).map{|a|a.inspect}.join(', ')}}"
282
+ end
283
+ if what_args.size == 0
284
+ other_attributes= nil
285
+ else
286
+ other_attributes={}
287
+ what_args.each_with_index do |arg, i|
288
+ other_attributes[other_attribute_keys[i]]=arg
289
+ end
290
+ end
291
+ element_by_howwhat(element_class_for(element_module), how, what, :locate => method_hash[:locate], :other_attributes => other_attributes)
292
+ end
293
+ end
294
+ end
295
+ end
296
+ end
297
+ def container_collection_method(*method_names)
298
+ class_array_append 'container_collection_methods', *method_names
299
+ element_module=self
300
+ method_names.each do |container_multiple_method|
301
+ Vapir::Container.module_eval do
302
+ # returns an ElementCollection of Elements that are instances of the including class
303
+ define_method(container_multiple_method) do |*args|
304
+ case args.length
305
+ when 0
306
+ ElementCollection.new(self, element_class_for(element_module), extra_for_contained)
307
+ when 1,2
308
+ first, second=*args
309
+ how, what, index= *normalize_how_what_index(first, second, element_class_for(element_module))
310
+ if index
311
+ raise ArgumentError, "Cannot specify index on collection method! specified index was #{index.inspect}"
312
+ end
313
+ ElementCollection.new(self, element_class_for(element_module), extra_for_contained, how, what)
314
+ else
315
+ raise ArgumentError, "wrong number of arguments - expected 0 arguments, 1 argument (hash of attributes), or 2 arguments ('how' and 'what'). got #{args.length}: #{args.inspect}"
316
+ end
317
+ end
318
+ define_method('child_'+container_multiple_method.to_s) do
319
+ ElementCollection.new(self, element_class_for(element_module), extra_for_contained.merge(:candidates => :childNodes))
320
+ end
321
+ define_method('show_'+container_multiple_method.to_s) do |*io|
322
+ io=io.first||$stdout # io is a *array so that you don't have to give an arg (since procs don't do default args)
323
+ element_collection=ElementCollection.new(self, element_class_for(element_module), extra_for_contained)
324
+ io.write("There are #{element_collection.length} #{container_multiple_method}\n")
325
+ element_collection.each do |element|
326
+ io.write(element.to_s)
327
+ end
328
+ end
329
+ alias_deprecated "show#{container_multiple_method.to_s.capitalize}", "show_"+container_multiple_method.to_s
330
+ end
331
+ end
332
+ end
333
+
334
+ include ElementClassAndModuleMethods
335
+
336
+ def included(including_class)
337
+ including_class.send :extend, ElementClassAndModuleMethods
338
+
339
+ # copy constants (like Specifiers) onto classes when inherited
340
+ # this is here to set the constants of the Element modules below onto the actual classes that instantiate
341
+ # per-browser (Vapir::IE::TextField, Vapir::Firefox::TextField, etc) so that calling #const_defined? on those
342
+ # returns true, and so that the constants defined here clobber any inherited stuff from superclasses
343
+ # which is unwanted.
344
+ self.constants.each do |const| # copy all of its constants onto wherever it was included
345
+ to_copy=self.const_get(const)
346
+ to_copy=to_copy.dup if [Hash, Array, Set].any?{|klass| to_copy.is_a?(klass) }
347
+ including_class.const_set(const, to_copy)
348
+ end
349
+
350
+ # now the constants (above) have switched away from constants to class variables, pretty much, so copy those.
351
+ self.class_variables.each do |class_var|
352
+ to_copy=class_variable_get(class_var)
353
+ to_copy=to_copy.dup if [Hash, Array, Set].any?{|klass| to_copy.is_a?(klass) }
354
+ including_class.send(:class_variable_set, class_var, to_copy)
355
+ end
356
+
357
+ class << including_class
358
+ def attributes_to_inspect
359
+ super_attrs=superclass.respond_to?(:attributes_to_inspect) ? superclass.attributes_to_inspect : []
360
+ super_attrs + class_array_get('attributes_to_inspect')
361
+ end
362
+ def all_dom_attrs
363
+ super_attrs=superclass.respond_to?(:all_dom_attrs) ? superclass.all_dom_attrs : []
364
+ super_attrs + class_array_get('dom_attrs')
365
+ end
366
+ def all_dom_attr_aliases
367
+ aliases=class_hash_get('dom_attr_aliases').dup
368
+ super_aliases=superclass.respond_to?(:all_dom_attr_aliases) ? superclass.all_dom_attr_aliases : {}
369
+ super_aliases.each_pair do |attr, alias_list|
370
+ aliases[attr] = (aliases[attr] || Set.new) + alias_list
371
+ end
372
+ aliases
373
+ end
374
+ end
375
+ end
376
+
377
+ end
378
+ end
@@ -4,35 +4,46 @@ module Vapir
4
4
  class ElementCollection
5
5
  include Enumerable
6
6
 
7
- def initialize(container, collection_class, extra={})
7
+ def initialize(container, collection_class, extra={}, how=nil, what=nil)
8
8
  @container=container
9
9
  @collection_class=collection_class
10
10
  @extra=extra.merge(:container => container)
11
+ @how=how
12
+ @what=what
11
13
  end
12
14
 
13
- def each
14
- candidates.each do |candidate|
15
- yield @collection_class.new(:element_object, candidate, @extra)
15
+ # yields each element in the collection to the given block
16
+ def each # :yields: element
17
+ element_objects.each do |element_object|
18
+ # todo: instantiated Element should use @how/@what? that would make #each the same as with #each_by_index
19
+ yield @collection_class.new(:element_object, element_object, @extra)
16
20
  end
17
21
  self
18
22
  end
19
- def each_index
23
+ # yields each index from 1..length.
24
+ #
25
+ # note that if you are using this and accessing each subscript, it will be exponentially slower
26
+ # than using #each or #each_with_index.
27
+ def each_index # :yields: index
20
28
  (1..length).each do |i|
21
29
  yield i
22
30
  end
23
31
  end
32
+ # returns the number of elements in the collection
24
33
  def length
25
- candidates.length
34
+ element_objects.length
26
35
  end
27
36
  alias size length
37
+ # returns true if this collection contains no elements
28
38
  def empty?
29
39
  size==0
30
40
  end
31
41
  alias each_with_enumerable_index each_with_index # call ruby's 0-based indexing enumerable_index; call ours element_index
32
- def each_with_element_index
42
+ # yields each element and index in the collection
43
+ def each_with_element_index # :yields: element, index
33
44
  index=1
34
- candidates.each do |candidate|
35
- yield @collection_class.new(:index, nil, @extra.merge(:index => index, :element_object => candidate, :locate => false)), index
45
+ element_objects.each do |element_object|
46
+ yield @collection_class.new(@how, @what, @extra.merge(:index => index, :element_object => element_object, :locate => false)), index
36
47
  index+=1
37
48
  end
38
49
  self
@@ -46,38 +57,115 @@ module Vapir
46
57
  yield element
47
58
  end
48
59
  end
60
+ def by_index
61
+ Enumerator.new(self, :each_by_index)
62
+ end
49
63
  # returns the element at the given index in the collection. indices start at 1.
50
64
  def [](index)
51
65
  at(index)
52
66
  end
67
+ # returns the element at the given index in the collection. indices start at 1.
53
68
  def at(index)
54
- @collection_class.new(:index, nil, @extra.merge(:index => index))
69
+ @collection_class.new(@how, @what, @extra.merge(:index => index))
55
70
  end
71
+ # returns the first element in the collection.
56
72
  def first
57
73
  at(:first)
58
74
  end
75
+ # returns the last element. this will refer to the last element even if the number of elements changes, assuming relocation.
59
76
  def last
60
77
  at(:last)
61
78
  end
62
79
 
63
- def find(&block)
64
- element=@collection_class.new(:custom, block, @extra.merge(:locate => false))
65
- element.exists? ? element : nil
80
+ alias enumerable_select select
81
+ def select(&block) # :yields: element
82
+ # TODO: test
83
+ if @how
84
+ enumerable_select(&block)
85
+ else
86
+ ElementCollection.new(@container, @collection_class, @extra, :custom, block)
87
+ end
66
88
  end
67
- alias detect find
68
89
 
69
- def inspect
70
- "\#<#{self.class.name}:0x#{"%.8x"%(self.hash*2)} #{map{|el|el.inspect}.join(', ')}>"
90
+ alias enumerable_reject reject
91
+ def reject(&block) # :yields: element
92
+ # TODO: test
93
+ if @how
94
+ enumerable_reject(&block)
95
+ else
96
+ ElementCollection.new(@container, @collection_class, @extra, :custom, proc{|el| !block.call(el) })
97
+ end
71
98
  end
72
-
99
+
100
+ alias enumerable_find find
101
+ # returns an element for which the given block returns true (that is, not false or nil) when yielded that element
102
+ #
103
+ # returns nil if no such element exists.
104
+ def find(&block) # :yields: element
105
+ if @how # can't set how=:custom if @how is given to us, so fall back to Enumerable's #find method
106
+ enumerable_find(&block)
107
+ else
108
+ element=@collection_class.new(:custom, block, @extra.merge(:locate => false))
109
+ element.exists? ? element : nil
110
+ end
111
+ end
112
+ alias detect find
113
+ # returns an element for which the given block returns true (that is, not false or nil) when yielded that element
114
+ #
115
+ # raises UnknownObjectException if no such element exists.
116
+ def find!(&block)
117
+ if @how # can't set how=:custom if @how is given to us, so fall back to Enumerable's #find method
118
+ enumerable_find(&block) || begin
119
+ # TODO: DRY against Element#locate!
120
+ klass=(@collection_class <= Frame) ? Vapir::Exception::UnknownFrameException : Vapir::Exception::UnknownObjectException
121
+ message="Unable to locate #{@collection_class} using custom find block"
122
+ message+="\non element collection #{self.inspect}"
123
+ message+="\non container: #{@container.inspect}"
124
+ raise(klass, message)
125
+ end
126
+ else
127
+ element=@collection_class.new(:custom, block, @extra.merge(:locate => :assert))
128
+ end
129
+ end
130
+ alias detect! find!
131
+
73
132
  private
74
133
  include ElementObjectCandidates
75
- def candidates
134
+ def element_objects
135
+ # TODO: this is heavily redundant with Element#locate; DRY
76
136
  assert_container_exists
77
- matched_candidates(@collection_class.specifiers, @collection_class.all_dom_attr_aliases)
137
+ case @how
138
+ when nil
139
+ matched_candidates(@collection_class.specifiers, @collection_class.all_dom_attr_aliases)
140
+ when :xpath
141
+ unless @container.respond_to?(:element_objects_by_xpath)
142
+ raise Vapir::Exception::MissingWayOfFindingObjectException, "Locating by xpath is not supported on the container #{@container.inspect}"
143
+ end
144
+ by_xpath=@container.element_objects_by_xpath(@what)
145
+ match_candidates(by_xpath, @collection_class.specifiers, @collection_class.all_dom_attr_aliases)
146
+ when :css
147
+ assert_container_exists
148
+ match_candidates(@container.containing_object.querySelectorAll(@what), @collection_class.specifiers, @collection_class.all_dom_attr_aliases)
149
+ when :attributes
150
+ specified_attributes=@what
151
+ specifiers=@collection_class.specifiers.map{|spec| spec.merge(specified_attributes)}
152
+
153
+ matched_candidates(specifiers, @collection_class.all_dom_attr_aliases)
154
+ when :custom
155
+ matched_candidates(@collection_class.specifiers, @collection_class.all_dom_attr_aliases).select do |candidate|
156
+ @what.call(@collection_class.new(:element_object, candidate, @extra))
157
+ end
158
+ else
159
+ raise Vapir::Exception::MissingWayOfFindingObjectException, "Unknown 'how' given: #{@how.inspect} (#{@how.class}). 'what' was #{@what.inspect} (#{@what.class})"
160
+ end
78
161
  end
79
162
  public
80
- def pretty_print(pp)
163
+ def inspect # :nodoc:
164
+ # todo: include how/what if set
165
+ "\#<#{self.class.name}:0x#{"%.8x"%(self.hash*2)} #{map{|el|el.inspect}.join(', ')}>"
166
+ end
167
+ def pretty_print(pp) # :nodoc:
168
+ # todo: include how/what if set
81
169
  pp.object_address_group(self) do
82
170
  pp.seplist(self, lambda { pp.text ',' }) do |element|
83
171
  pp.breakable ' '