neocoin-mechanize 2.0.2
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.
- data/.autotest +6 -0
- data/.gemtest +0 -0
- data/CHANGELOG.rdoc +638 -0
- data/EXAMPLES.rdoc +187 -0
- data/FAQ.rdoc +11 -0
- data/GUIDE.rdoc +163 -0
- data/LICENSE.rdoc +20 -0
- data/Manifest.txt +172 -0
- data/README.rdoc +63 -0
- data/Rakefile +36 -0
- data/examples/flickr_upload.rb +22 -0
- data/examples/mech-dump.rb +5 -0
- data/examples/proxy_req.rb +7 -0
- data/examples/rubyforge.rb +20 -0
- data/examples/spider.rb +21 -0
- data/lib/mechanize.rb +662 -0
- data/lib/mechanize/content_type_error.rb +14 -0
- data/lib/mechanize/cookie.rb +85 -0
- data/lib/mechanize/cookie_jar.rb +241 -0
- data/lib/mechanize/element_matcher.rb +35 -0
- data/lib/mechanize/file.rb +80 -0
- data/lib/mechanize/file_connection.rb +17 -0
- data/lib/mechanize/file_request.rb +26 -0
- data/lib/mechanize/file_response.rb +74 -0
- data/lib/mechanize/file_saver.rb +37 -0
- data/lib/mechanize/form.rb +478 -0
- data/lib/mechanize/form/button.rb +9 -0
- data/lib/mechanize/form/check_box.rb +11 -0
- data/lib/mechanize/form/field.rb +44 -0
- data/lib/mechanize/form/file_upload.rb +23 -0
- data/lib/mechanize/form/image_button.rb +20 -0
- data/lib/mechanize/form/multi_select_list.rb +83 -0
- data/lib/mechanize/form/option.rb +49 -0
- data/lib/mechanize/form/radio_button.rb +48 -0
- data/lib/mechanize/form/select_list.rb +40 -0
- data/lib/mechanize/headers.rb +25 -0
- data/lib/mechanize/history.rb +83 -0
- data/lib/mechanize/http.rb +3 -0
- data/lib/mechanize/http/agent.rb +738 -0
- data/lib/mechanize/inspect.rb +88 -0
- data/lib/mechanize/monkey_patch.rb +37 -0
- data/lib/mechanize/page.rb +408 -0
- data/lib/mechanize/page/base.rb +8 -0
- data/lib/mechanize/page/frame.rb +27 -0
- data/lib/mechanize/page/image.rb +30 -0
- data/lib/mechanize/page/label.rb +20 -0
- data/lib/mechanize/page/link.rb +82 -0
- data/lib/mechanize/page/meta_refresh.rb +56 -0
- data/lib/mechanize/pluggable_parsers.rb +101 -0
- data/lib/mechanize/redirect_limit_reached_error.rb +16 -0
- data/lib/mechanize/redirect_not_get_or_head_error.rb +19 -0
- data/lib/mechanize/response_code_error.rb +22 -0
- data/lib/mechanize/response_read_error.rb +27 -0
- data/lib/mechanize/robots_disallowed_error.rb +29 -0
- data/lib/mechanize/unsupported_scheme_error.rb +8 -0
- data/lib/mechanize/util.rb +113 -0
- data/test/data/htpasswd +1 -0
- data/test/data/server.crt +16 -0
- data/test/data/server.csr +12 -0
- data/test/data/server.key +15 -0
- data/test/data/server.pem +15 -0
- data/test/helper.rb +175 -0
- data/test/htdocs/alt_text.html +10 -0
- data/test/htdocs/bad_form_test.html +9 -0
- data/test/htdocs/button.jpg +0 -0
- data/test/htdocs/canonical_uri.html +9 -0
- data/test/htdocs/dir with spaces/foo.html +1 -0
- data/test/htdocs/empty_form.html +6 -0
- data/test/htdocs/file_upload.html +26 -0
- data/test/htdocs/find_link.html +41 -0
- data/test/htdocs/form_multi_select.html +16 -0
- data/test/htdocs/form_multival.html +37 -0
- data/test/htdocs/form_no_action.html +18 -0
- data/test/htdocs/form_no_input_name.html +16 -0
- data/test/htdocs/form_select.html +16 -0
- data/test/htdocs/form_select_all.html +16 -0
- data/test/htdocs/form_select_none.html +17 -0
- data/test/htdocs/form_select_noopts.html +10 -0
- data/test/htdocs/form_set_fields.html +14 -0
- data/test/htdocs/form_test.html +188 -0
- data/test/htdocs/frame_referer_test.html +10 -0
- data/test/htdocs/frame_test.html +30 -0
- data/test/htdocs/google.html +13 -0
- data/test/htdocs/iframe_test.html +16 -0
- data/test/htdocs/index.html +6 -0
- data/test/htdocs/link with space.html +5 -0
- data/test/htdocs/meta_cookie.html +11 -0
- data/test/htdocs/no_title_test.html +6 -0
- data/test/htdocs/nofollow.html +9 -0
- data/test/htdocs/noindex.html +9 -0
- data/test/htdocs/norobots.html +8 -0
- data/test/htdocs/rails_3_encoding_hack_form_test.html +27 -0
- data/test/htdocs/rel_nofollow.html +8 -0
- data/test/htdocs/relative/tc_relative_links.html +21 -0
- data/test/htdocs/robots.html +8 -0
- data/test/htdocs/robots.txt +2 -0
- data/test/htdocs/tc_bad_charset.html +9 -0
- data/test/htdocs/tc_bad_links.html +5 -0
- data/test/htdocs/tc_base_images.html +10 -0
- data/test/htdocs/tc_base_link.html +8 -0
- data/test/htdocs/tc_blank_form.html +11 -0
- data/test/htdocs/tc_charset.html +6 -0
- data/test/htdocs/tc_checkboxes.html +19 -0
- data/test/htdocs/tc_encoded_links.html +5 -0
- data/test/htdocs/tc_field_precedence.html +11 -0
- data/test/htdocs/tc_follow_meta.html +8 -0
- data/test/htdocs/tc_form_action.html +48 -0
- data/test/htdocs/tc_images.html +8 -0
- data/test/htdocs/tc_links.html +18 -0
- data/test/htdocs/tc_meta_in_body.html +9 -0
- data/test/htdocs/tc_no_attributes.html +16 -0
- data/test/htdocs/tc_pretty_print.html +17 -0
- data/test/htdocs/tc_radiobuttons.html +17 -0
- data/test/htdocs/tc_referer.html +16 -0
- data/test/htdocs/tc_relative_links.html +19 -0
- data/test/htdocs/tc_textarea.html +23 -0
- data/test/htdocs/test_bad_encoding.html +52 -0
- data/test/htdocs/test_click.html +11 -0
- data/test/htdocs/unusual______.html +5 -0
- data/test/servlets.rb +402 -0
- data/test/ssl_server.rb +48 -0
- data/test/test_cookies.rb +129 -0
- data/test/test_form_action.rb +52 -0
- data/test/test_form_as_hash.rb +59 -0
- data/test/test_form_button.rb +46 -0
- data/test/test_frames.rb +34 -0
- data/test/test_headers.rb +33 -0
- data/test/test_history.rb +118 -0
- data/test/test_history_added.rb +16 -0
- data/test/test_html_unscape_forms.rb +46 -0
- data/test/test_if_modified_since.rb +20 -0
- data/test/test_images.rb +19 -0
- data/test/test_mechanize.rb +842 -0
- data/test/test_mechanize_cookie.rb +345 -0
- data/test/test_mechanize_cookie_jar.rb +401 -0
- data/test/test_mechanize_file.rb +53 -0
- data/test/test_mechanize_file_request.rb +19 -0
- data/test/test_mechanize_file_response.rb +21 -0
- data/test/test_mechanize_form.rb +576 -0
- data/test/test_mechanize_form_check_box.rb +37 -0
- data/test/test_mechanize_form_encoding.rb +120 -0
- data/test/test_mechanize_form_field.rb +21 -0
- data/test/test_mechanize_form_image_button.rb +12 -0
- data/test/test_mechanize_form_textarea.rb +51 -0
- data/test/test_mechanize_http_agent.rb +697 -0
- data/test/test_mechanize_link.rb +84 -0
- data/test/test_mechanize_page_encoding.rb +147 -0
- data/test/test_mechanize_page_link.rb +382 -0
- data/test/test_mechanize_page_meta_refresh.rb +115 -0
- data/test/test_mechanize_redirect_not_get_or_head_error.rb +18 -0
- data/test/test_mechanize_subclass.rb +22 -0
- data/test/test_mechanize_util.rb +92 -0
- data/test/test_multi_select.rb +118 -0
- data/test/test_no_attributes.rb +13 -0
- data/test/test_option.rb +18 -0
- data/test/test_pluggable_parser.rb +136 -0
- data/test/test_post_form.rb +37 -0
- data/test/test_pretty_print.rb +22 -0
- data/test/test_radiobutton.rb +75 -0
- data/test/test_redirect_limit_reached.rb +39 -0
- data/test/test_referer.rb +81 -0
- data/test/test_relative_links.rb +40 -0
- data/test/test_request.rb +13 -0
- data/test/test_response_code.rb +53 -0
- data/test/test_robots.rb +72 -0
- data/test/test_save_file.rb +48 -0
- data/test/test_scheme.rb +48 -0
- data/test/test_select.rb +119 -0
- data/test/test_select_all.rb +15 -0
- data/test/test_select_none.rb +15 -0
- data/test/test_select_noopts.rb +18 -0
- data/test/test_set_fields.rb +44 -0
- data/test/test_ssl_server.rb +20 -0
- metadata +354 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class Mechanize
|
|
2
|
+
class Form
|
|
3
|
+
# This class represents a check box found in a Form. To activate the
|
|
4
|
+
# CheckBox in the Form, set the checked method to true.
|
|
5
|
+
class CheckBox < RadioButton
|
|
6
|
+
def query_value
|
|
7
|
+
[[@name, @value || "on"]]
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
class Mechanize
|
|
2
|
+
class Form
|
|
3
|
+
# This class represents a field in a form. It handles the following input
|
|
4
|
+
# tags found in a form:
|
|
5
|
+
# text, password, hidden, int, textarea
|
|
6
|
+
#
|
|
7
|
+
# To set the value of a field, just use the value method:
|
|
8
|
+
# field.value = "foo"
|
|
9
|
+
class Field
|
|
10
|
+
attr_accessor :name, :value, :node
|
|
11
|
+
|
|
12
|
+
def initialize node, value = node['value']
|
|
13
|
+
@node = node
|
|
14
|
+
@name = Util.html_unescape(node['name'])
|
|
15
|
+
@value = if value.is_a? String
|
|
16
|
+
Util.html_unescape(value)
|
|
17
|
+
else
|
|
18
|
+
value
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def query_value
|
|
23
|
+
[[@name, @value || '']]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def <=> other
|
|
27
|
+
return 0 if self == other
|
|
28
|
+
return 1 if Hash === node
|
|
29
|
+
return -1 if Hash === other.node
|
|
30
|
+
node <=> other.node
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# This method is a shortcut to get field's DOM id.
|
|
34
|
+
# Common usage: form.field_with(:dom_id => "foo")
|
|
35
|
+
def dom_id
|
|
36
|
+
node['id']
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Text < Field; end
|
|
41
|
+
class Textarea < Field; end
|
|
42
|
+
class Hidden < Field; end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class Mechanize
|
|
2
|
+
class Form
|
|
3
|
+
# This class represents a file upload field found in a form. To use this
|
|
4
|
+
# class, set FileUpload#file_data= to the data of the file you want
|
|
5
|
+
# to upload and FileUpload#mime_type= to the appropriate mime type
|
|
6
|
+
# of the file.
|
|
7
|
+
# See the example in EXAMPLES[link://files/EXAMPLES_txt.html]
|
|
8
|
+
class FileUpload < Field
|
|
9
|
+
attr_accessor :file_name # File name
|
|
10
|
+
attr_accessor :mime_type # Mime Type (Optional)
|
|
11
|
+
|
|
12
|
+
alias :file_data :value
|
|
13
|
+
alias :file_data= :value=
|
|
14
|
+
|
|
15
|
+
def initialize node, file_name
|
|
16
|
+
@file_name = Util.html_unescape(file_name)
|
|
17
|
+
@file_data = nil
|
|
18
|
+
@node = node
|
|
19
|
+
super(node, @file_data)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class Mechanize
|
|
2
|
+
class Form
|
|
3
|
+
# This class represents an image button in a form. Use the x and y methods
|
|
4
|
+
# to set the x and y positions for where the mouse "clicked".
|
|
5
|
+
class ImageButton < Button
|
|
6
|
+
attr_accessor :x, :y
|
|
7
|
+
|
|
8
|
+
def initialize *args
|
|
9
|
+
@x = nil
|
|
10
|
+
@y = nil
|
|
11
|
+
super
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def query_value
|
|
15
|
+
[["#{@name}.x", (@x || 0).to_s],
|
|
16
|
+
["#{@name}.y", (@y || 0).to_s]]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# This class represents a select list where multiple values can be selected.
|
|
2
|
+
# MultiSelectList#value= accepts an array, and those values are used as
|
|
3
|
+
# values for the select list. For example, to select multiple values,
|
|
4
|
+
# simply do this:
|
|
5
|
+
#
|
|
6
|
+
# list.value = ['one', 'two']
|
|
7
|
+
#
|
|
8
|
+
# Single values are still supported, so these two are the same:
|
|
9
|
+
#
|
|
10
|
+
# list.value = ['one']
|
|
11
|
+
# list.value = 'one'
|
|
12
|
+
class Mechanize::Form::MultiSelectList < Mechanize::Form::Field
|
|
13
|
+
extend Mechanize::ElementMatcher
|
|
14
|
+
|
|
15
|
+
attr_accessor :options
|
|
16
|
+
|
|
17
|
+
def initialize node
|
|
18
|
+
value = []
|
|
19
|
+
@options = []
|
|
20
|
+
|
|
21
|
+
# parse
|
|
22
|
+
node.search('option').each do |n|
|
|
23
|
+
option = Mechanize::Form::Option.new(n, self)
|
|
24
|
+
@options << option
|
|
25
|
+
end
|
|
26
|
+
super(node, value)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# Find one option on this select list with +criteria+
|
|
31
|
+
# Example:
|
|
32
|
+
# select_list.option_with(:value => '1').value = 'foo'
|
|
33
|
+
|
|
34
|
+
##
|
|
35
|
+
# Find all options on this select list with +criteria+
|
|
36
|
+
# Example:
|
|
37
|
+
# select_list.options_with(:value => /1|2/).each do |field|
|
|
38
|
+
# field.value = '20'
|
|
39
|
+
# end
|
|
40
|
+
|
|
41
|
+
elements_with :option
|
|
42
|
+
|
|
43
|
+
def query_value
|
|
44
|
+
value ? value.collect { |v| [name, v] } : ''
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Select no options
|
|
48
|
+
def select_none
|
|
49
|
+
@value = []
|
|
50
|
+
options.each { |o| o.untick }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Select all options
|
|
54
|
+
def select_all
|
|
55
|
+
@value = []
|
|
56
|
+
options.each { |o| o.tick }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Get a list of all selected options
|
|
60
|
+
def selected_options
|
|
61
|
+
@options.find_all { |o| o.selected? }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def value=(values)
|
|
65
|
+
select_none
|
|
66
|
+
[values].flatten.each do |value|
|
|
67
|
+
option = options.find { |o| o.value == value }
|
|
68
|
+
if option.nil?
|
|
69
|
+
@value.push(value)
|
|
70
|
+
else
|
|
71
|
+
option.select
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def value
|
|
77
|
+
value = []
|
|
78
|
+
value.push(*@value)
|
|
79
|
+
value.push(*selected_options.collect { |o| o.value })
|
|
80
|
+
value
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
class Mechanize
|
|
2
|
+
class Form
|
|
3
|
+
# This class contains option an option found within SelectList. A
|
|
4
|
+
# SelectList can have many Option classes associated with it. An option
|
|
5
|
+
# can be selected by calling Option#tick, or Option#click. For example,
|
|
6
|
+
# select the first option in a list:
|
|
7
|
+
# select_list.first.tick
|
|
8
|
+
class Option
|
|
9
|
+
attr_reader :value, :selected, :text, :select_list
|
|
10
|
+
|
|
11
|
+
alias :to_s :value
|
|
12
|
+
alias :selected? :selected
|
|
13
|
+
|
|
14
|
+
def initialize(node, select_list)
|
|
15
|
+
@text = node.inner_text
|
|
16
|
+
@value = Util.html_unescape(node['value'] || node.inner_text)
|
|
17
|
+
@selected = node.has_attribute? 'selected'
|
|
18
|
+
@select_list = select_list # The select list this option belongs to
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Select this option
|
|
22
|
+
def select
|
|
23
|
+
unselect_peers
|
|
24
|
+
@selected = true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Unselect this option
|
|
28
|
+
def unselect
|
|
29
|
+
@selected = false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
alias :tick :select
|
|
33
|
+
alias :untick :unselect
|
|
34
|
+
|
|
35
|
+
# Toggle the selection value of this option
|
|
36
|
+
def click
|
|
37
|
+
unselect_peers
|
|
38
|
+
@selected = !@selected
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
def unselect_peers
|
|
43
|
+
if @select_list.instance_of? SelectList
|
|
44
|
+
@select_list.select_none
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
class Mechanize
|
|
2
|
+
class Form
|
|
3
|
+
# This class represents a radio button found in a Form. To activate the
|
|
4
|
+
# RadioButton in the Form, set the checked method to true.
|
|
5
|
+
class RadioButton < Field
|
|
6
|
+
attr_accessor :checked
|
|
7
|
+
|
|
8
|
+
def initialize node, form
|
|
9
|
+
@checked = !!node['checked']
|
|
10
|
+
@form = form
|
|
11
|
+
super(node)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def check
|
|
15
|
+
uncheck_peers
|
|
16
|
+
@checked = true
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def uncheck
|
|
20
|
+
@checked = false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def click
|
|
24
|
+
checked ? uncheck : check
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def label
|
|
28
|
+
(id = self['id']) && @form.page.labels_hash[id] || nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def text
|
|
32
|
+
label.text rescue nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def [](key)
|
|
36
|
+
@node[key]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
def uncheck_peers
|
|
41
|
+
@form.radiobuttons_with(:name => name).each do |b|
|
|
42
|
+
next if b.value == value
|
|
43
|
+
b.uncheck
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# This class represents a select list or drop down box in a Form. Set the
|
|
2
|
+
# value for the list by calling SelectList#value=. SelectList contains a
|
|
3
|
+
# list of Option that were found. After finding the correct option, set
|
|
4
|
+
# the select lists value to the option value:
|
|
5
|
+
# selectlist.value = selectlist.options.first.value
|
|
6
|
+
# Options can also be selected by "clicking" or selecting them. See Option
|
|
7
|
+
class Mechanize::Form::SelectList < Mechanize::Form::MultiSelectList
|
|
8
|
+
def initialize node
|
|
9
|
+
super
|
|
10
|
+
if selected_options.length > 1
|
|
11
|
+
selected_options.reverse[1..selected_options.length].each do |o|
|
|
12
|
+
o.unselect
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def value
|
|
18
|
+
value = super
|
|
19
|
+
if value.length > 0
|
|
20
|
+
value.last
|
|
21
|
+
elsif @options.length > 0
|
|
22
|
+
@options.first.value
|
|
23
|
+
else
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def value=(new)
|
|
29
|
+
if new != new.to_s and new.respond_to? :first
|
|
30
|
+
super([new.first])
|
|
31
|
+
else
|
|
32
|
+
super([new.to_s])
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def query_value
|
|
37
|
+
value ? [[name, value]] : nil
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
class Mechanize
|
|
2
|
+
class Headers < Hash
|
|
3
|
+
def [](key)
|
|
4
|
+
super(key.downcase)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def []=(key, value)
|
|
8
|
+
super(key.downcase, value)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def key?(key)
|
|
12
|
+
super(key.downcase)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def canonical_each
|
|
16
|
+
block_given? or return enum_for(__method__)
|
|
17
|
+
each { |key, value|
|
|
18
|
+
key = key.capitalize
|
|
19
|
+
key.gsub!(/-([a-z])/) { "-#{$1.upcase}" }
|
|
20
|
+
yield [key, value]
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
class Mechanize
|
|
2
|
+
##
|
|
3
|
+
# This class manages history for your mechanize object.
|
|
4
|
+
class History < Array
|
|
5
|
+
attr_accessor :max_size
|
|
6
|
+
|
|
7
|
+
def initialize(max_size = nil)
|
|
8
|
+
@max_size = max_size
|
|
9
|
+
@history_index = {}
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize_copy(orig)
|
|
13
|
+
super
|
|
14
|
+
@history_index = orig.instance_variable_get(:@history_index).dup
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def inspect # :nodoc:
|
|
18
|
+
uris = map { |page| page.uri }.join ', '
|
|
19
|
+
|
|
20
|
+
"[#{uris}]"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def push(page, uri = nil)
|
|
24
|
+
super(page)
|
|
25
|
+
|
|
26
|
+
@history_index[(uri ? uri : page.uri).to_s] = page
|
|
27
|
+
|
|
28
|
+
if @max_size && self.length > @max_size
|
|
29
|
+
while self.length > @max_size
|
|
30
|
+
self.shift
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
self
|
|
35
|
+
end
|
|
36
|
+
alias :<< :push
|
|
37
|
+
|
|
38
|
+
def visited_page(uri)
|
|
39
|
+
page = @history_index[uri.to_s]
|
|
40
|
+
|
|
41
|
+
return page if page # HACK
|
|
42
|
+
|
|
43
|
+
uri = uri.dup
|
|
44
|
+
uri.path = '/' if uri.path.empty?
|
|
45
|
+
|
|
46
|
+
@history_index[uri.to_s]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
alias visited? visited_page
|
|
50
|
+
|
|
51
|
+
def clear
|
|
52
|
+
@history_index.clear
|
|
53
|
+
super
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def shift
|
|
57
|
+
return nil if length == 0
|
|
58
|
+
page = self[0]
|
|
59
|
+
self[0] = nil
|
|
60
|
+
|
|
61
|
+
super
|
|
62
|
+
|
|
63
|
+
remove_from_index(page)
|
|
64
|
+
page
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def pop
|
|
68
|
+
return nil if length == 0
|
|
69
|
+
page = super
|
|
70
|
+
remove_from_index(page)
|
|
71
|
+
page
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def remove_from_index(page)
|
|
77
|
+
@history_index.each do |k,v|
|
|
78
|
+
@history_index.delete(k) if v == page
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end
|
|
83
|
+
end
|