Class: Marta::XPath::XPathFactory
- Inherits:
-
Object
- Object
- Marta::XPath::XPathFactory
- Includes:
- UserValuePrework
- Defined in:
- lib/marta/x_path.rb
Overview
It is believed that no user will use it
Here we are creating xpath including arrays of xpaths with one-two-…-x parts that are not known
All xpaths for Marta are constructed out of three parts: granny, pappy, and self. Where self is a xpath for DOM element itself, pappy is for father element, granny is for grandfather. For example //DIV/SPAN/INPUT: //DIV = granny, /SPAN = pappy, /INPUT = self.
We are generating special arrays of hashes at first for each part. And then we are constructing final xpaths
Instance Attribute Summary collapse
-
#granny ⇒ Object
Returns the value of attribute granny.
-
#pappy ⇒ Object
Returns the value of attribute pappy.
Instance Method Summary collapse
-
#analyze(depth, limit) ⇒ Object
We are trying to understand here how much work should we do in order to generate all possible xpaths variants.
-
#array_of_hashes ⇒ Object
We are parsing stored element data (tag, text and attributes) into the array of hashes.
-
#generate_xpath ⇒ Object
We can generate straight xpath by all known data.
-
#generate_xpaths(depth, limit = 100000) ⇒ Object
Full logic of xpath generating We are understanding can we find all the possible xpath variations We are creating all possible masks (arrays of switches) one by one If we know that we cannot find all variants we are generating some by more random algorithm.
-
#get_masks(masks, depth) ⇒ Object
We are generating masks like [:empty,:full,:full,:empty] They are used for more understandable logic of looping xpaths variants In fact they are lists with all the combinations of switches :full:empty.
-
#initialize(meth, requestor) ⇒ XPathFactory
constructor
A new instance of XPathFactory.
-
#make_hash(full, empty = '') ⇒ Object
Creating the smallest possible part of array hash.
-
#monte_carlo(hashes, depth, limit) ⇒ Object
Generating not more than limit random xpaths variants considering that depth parts of xpath are unstable.
-
#negative_part_of_array_of_hashes(what) ⇒ Object
We are parsing negative part of element data to array of hashes.
-
#positive_part_of_array_of_hashes(what) ⇒ Object
We are parsing positive part of element data to array of hashes.
-
#xpaths_by_mask(mask, hashes) ⇒ Object
We are forming xpath strings by masks and hashes with data.
Constructor Details
#initialize(meth, requestor) ⇒ XPathFactory
Returns a new instance of XPathFactory.
28 29 30 31 32 |
# File 'lib/marta/x_path.rb', line 28 def initialize(meth, requestor) @meth = meth @granny = @pappy = true @requestor = requestor end |
Instance Attribute Details
#granny ⇒ Object
Returns the value of attribute granny.
27 28 29 |
# File 'lib/marta/x_path.rb', line 27 def granny @granny end |
#pappy ⇒ Object
Returns the value of attribute pappy.
27 28 29 |
# File 'lib/marta/x_path.rb', line 27 def pappy @pappy end |
Instance Method Details
#analyze(depth, limit) ⇒ Object
We are trying to understand here how much work should we do in order to generate all possible xpaths variants.
depth is suggested amount of unstable xpath parts limit is the maximum amount of xpaths that we want to generate If we can try all the combinations of xpaths with considering depth elements unstable withou reaching the limit of tries, method will return that depth and precreated array_of_hashes If limit will be reached on creation of all xpaths, method is returning last acceptable depth and array_of_hashes
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/marta/x_path.rb', line 44 def analyze(depth, limit) hashes = array_of_hashes count = 1 real_depth = 0 variativity = 0 hashes.each do |hash| variativity += (hash[:empty] - hash[:full]).count end depth.times do count = count * variativity if count < limit real_depth += 1 end end return real_depth, hashes end |
#array_of_hashes ⇒ Object
We are parsing stored element data (tag, text and attributes) into the array of hashes
Output looks like: ["//", "H1", ”],empty[“”, “[@*[contains(.,‘x’)]]”]]
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/marta/x_path.rb', line 200 def array_of_hashes result = Array.new result.push make_hash('//', '//') if @granny result = result + positive_part_of_array_of_hashes('granny') + negative_part_of_array_of_hashes('granny') result.push make_hash('/', '//') end if @pappy result = result + positive_part_of_array_of_hashes('pappy') + negative_part_of_array_of_hashes('pappy') result.push make_hash('/', '//') end result = result + positive_part_of_array_of_hashes('self') + negative_part_of_array_of_hashes('self') result end |
#generate_xpath ⇒ Object
We can generate straight xpath by all known data
148 149 150 151 152 153 154 |
# File 'lib/marta/x_path.rb', line 148 def generate_xpath result = '' array_of_hashes.each do |hash| result = result + hash[:full][0] end process_string result, @requestor end |
#generate_xpaths(depth, limit = 100000) ⇒ Object
Full logic of xpath generating We are understanding can we find all the possible xpath variations We are creating all possible masks (arrays of switches) one by one If we know that we cannot find all variants we are generating some by more random algorithm
134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/marta/x_path.rb', line 134 def generate_xpaths(depth, limit = 100000) xpaths = Array.new real_depth, hashes = analyze(depth, limit) masks = get_masks([Array.new(hashes.count, :full)], real_depth) masks.each do |mask| xpaths = xpaths + xpaths_by_mask(mask, hashes) end if real_depth != depth xpaths = xpaths + monte_carlo(hashes, depth, limit) end xpaths.uniq.map {|xpath| process_string(xpath, @requestor)} end |
#get_masks(masks, depth) ⇒ Object
We are generating masks like [:empty,:full,:full,:empty] They are used for more understandable logic of looping xpaths variants In fact they are lists with all the combinations of switches :full:empty. Where the length of switches is the same as length of xpath parts. And amount of :empty switches is depth
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/marta/x_path.rb', line 85 def get_masks(masks, depth) result = Array.new masks.each do |mask| result.push mask for i in 0..mask.count-1 result.push(mask.map { |e| e.dup }) result.last[i] = :empty end end if depth-1 == 0 result else get_masks(result, depth-1) end end |
#make_hash(full, empty = '') ⇒ Object
Creating the smallest possible part of array hash
222 223 224 |
# File 'lib/marta/x_path.rb', line 222 def make_hash(full, empty = '') {full: [full], empty: empty.class != Array ? [empty] : empty} end |
#monte_carlo(hashes, depth, limit) ⇒ Object
Generating not more than limit random xpaths variants considering that depth parts of xpath are unstable
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/marta/x_path.rb', line 63 def monte_carlo(hashes, depth, limit) xpaths = Array.new while xpaths.count < limit do mask = Array.new hashes.count, :full depth.times do mask[rand(mask.count)] = :empty end final_array = Array.new hashes.each_with_index do |hash, index| final_array.push(hash[mask[index]].sample) end xpaths.push final_array.join xpaths end xpaths end |
#negative_part_of_array_of_hashes(what) ⇒ Object
We are parsing negative part of element data to array of hashes
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/marta/x_path.rb', line 175 def negative_part_of_array_of_hashes(what) result = Array.new @meth['negative'][what]['tag'].each do |not_tag| result.push make_hash("[not(self::#{not_tag})]", '') end @meth['negative'][what]['text'].each do |not_text| result.push make_hash("[not(contains(text(),'#{not_text}'))]", '') end @meth['negative'][what]['attributes'].each_pair do |attribute, values| if (values != []) and (values != ['']) and !values.nil? values.each do |value| result.push make_hash("[not(contains(@#{attribute},'#{value}'))]") end end end result end |
#positive_part_of_array_of_hashes(what) ⇒ Object
We are parsing positive part of element data to array of hashes
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/marta/x_path.rb', line 157 def positive_part_of_array_of_hashes(what) result = Array.new result.push make_hash(@meth['positive'][what]['tag'] != [] ? @meth['positive'][what]['tag'][0] : '*', '*') if (@meth['positive'][what]['text'] != []) and (@meth['positive'][what]['text'] != ['']) result.push make_hash("[contains(text(),'#{@meth['positive'][what]['text'][0]}')]") end @meth['positive'][what]['attributes'].each_pair do |attribute, values| if (values != []) and (values != ['']) and !values.nil? values.each do |value| result.push make_hash("[contains(@#{attribute},'#{value}')]", ["[@*[contains(.,'#{value}')]]", ""]) end end end result end |
#xpaths_by_mask(mask, hashes) ⇒ Object
We are forming xpath strings by masks and hashes with data
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/marta/x_path.rb', line 102 def xpaths_by_mask(mask, hashes) xpaths = Array.new final_array = [[]] hashes.each_with_index do |hash, index| hash[mask[index]].each_with_index do |hash_value, empty_index| if empty_index == 0 final_array.each do |final_array_item| final_array_item.push(hash_value) end else alternative_final_array = [] final_array.each do |final_array_item| alternative_final_array.push final_array_item.dup end alternative_final_array.each do |a_final_array_item| a_final_array_item[-1] = hash_value end final_array = final_array + alternative_final_array end end end final_array.each do |final_array_item| xpaths.push final_array_item.join end xpaths end |