vapir-common 1.7.2 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 ' '