refactor
parent
6865409068
commit
ed57c3a696
|
@ -8,197 +8,199 @@ require "./form/button"
|
|||
require "./form/select_list"
|
||||
require "./utils/element_matcher"
|
||||
|
||||
# This class represents the form tag of html.
|
||||
class Mechanize::Form
|
||||
include Mechanize::ElementMatcher
|
||||
class Mechanize
|
||||
# This class represents the form tag of html.
|
||||
class Form
|
||||
include ElementMatcher
|
||||
|
||||
getter node : Node | Lexbor::Node
|
||||
# returns hoge array of `Mechanize::FormContent::Field` in the form.
|
||||
getter fields : Array(FormContent::Field)
|
||||
# returns an array of input tags whose type is checkbox in the form.
|
||||
getter checkboxes : Array(FormContent::CheckBox)
|
||||
# returns an array of input tags whose type is radio in the form.
|
||||
getter radiobuttons : Array(FormContent::RadioButton)
|
||||
# returns an array of input tags whose type is select in the form.
|
||||
getter selectboxes : Array(FormContent::MultiSelectList)
|
||||
# returns an array of button tags and input tag whose type is button,submit,reset,image.
|
||||
getter buttons : Array(FormContent::Button)
|
||||
# returns form's 'enctype' attribute.
|
||||
getter enctype : String
|
||||
# returns form's 'method' attribute.
|
||||
getter method : String
|
||||
# returns form's 'name' attribute.
|
||||
getter name : String
|
||||
# return form's 'action' attribute.
|
||||
property action : String
|
||||
# returns the page which includes the form.
|
||||
getter page : Mechanize::Page?
|
||||
getter node : Node | Lexbor::Node
|
||||
# returns hoge array of `Mechanize::FormContent::Field` in the form.
|
||||
getter fields : Array(FormContent::Field)
|
||||
# returns an array of input tags whose type is checkbox in the form.
|
||||
getter checkboxes : Array(FormContent::CheckBox)
|
||||
# returns an array of input tags whose type is radio in the form.
|
||||
getter radiobuttons : Array(FormContent::RadioButton)
|
||||
# returns an array of input tags whose type is select in the form.
|
||||
getter selectboxes : Array(FormContent::MultiSelectList)
|
||||
# returns an array of button tags and input tag whose type is button,submit,reset,image.
|
||||
getter buttons : Array(FormContent::Button)
|
||||
# returns form's 'enctype' attribute.
|
||||
getter enctype : String
|
||||
# returns form's 'method' attribute.
|
||||
getter method : String
|
||||
# returns form's 'name' attribute.
|
||||
getter name : String
|
||||
# return form's 'action' attribute.
|
||||
property action : String
|
||||
# returns the page which includes the form.
|
||||
getter page : Page?
|
||||
|
||||
def initialize(node : Node | Lexbor::Node, page : Mechanize::Page? = nil)
|
||||
@enctype = node.fetch("enctype", "application/x-www-form-urlencoded")
|
||||
@node = node
|
||||
@fields = Array(Mechanize::FormContent::Field).new
|
||||
@checkboxes = Array(Mechanize::FormContent::CheckBox).new
|
||||
@radiobuttons = Array(Mechanize::FormContent::RadioButton).new
|
||||
@selectboxes = Array(Mechanize::FormContent::MultiSelectList).new
|
||||
@buttons = Array(Mechanize::FormContent::Button).new
|
||||
@action = node.fetch("action", "")
|
||||
@method = node.fetch("method", "GET").upcase
|
||||
@name = node.fetch("name", "")
|
||||
@clicked_buttons = Array(Mechanize::FormContent::Button).new
|
||||
@page = page
|
||||
# @mech = mech
|
||||
def initialize(node : Node | Lexbor::Node, page : Page? = nil)
|
||||
@enctype = node.fetch("enctype", "application/x-www-form-urlencoded")
|
||||
@node = node
|
||||
@fields = Array(FormContent::Field).new
|
||||
@checkboxes = Array(FormContent::CheckBox).new
|
||||
@radiobuttons = Array(FormContent::RadioButton).new
|
||||
@selectboxes = Array(FormContent::MultiSelectList).new
|
||||
@buttons = Array(FormContent::Button).new
|
||||
@action = node.fetch("action", "")
|
||||
@method = node.fetch("method", "GET").upcase
|
||||
@name = node.fetch("name", "")
|
||||
@clicked_buttons = Array(FormContent::Button).new
|
||||
@page = page
|
||||
# @mech = mech
|
||||
|
||||
# @encoding = node['accept-charset'] || (page && page.encoding) || nil
|
||||
# @ignore_encoding_error = false
|
||||
parse
|
||||
end
|
||||
# @encoding = node['accept-charset'] || (page && page.encoding) || nil
|
||||
# @ignore_encoding_error = false
|
||||
parse
|
||||
end
|
||||
|
||||
def request_data
|
||||
query_params = build_query
|
||||
build_query_string(query_params)
|
||||
end
|
||||
def request_data
|
||||
query_params = build_query
|
||||
build_query_string(query_params)
|
||||
end
|
||||
|
||||
# generate fields_with and field_with methods.
|
||||
# These methods are used for finding nodes that matches conditions.
|
||||
# ex.) field_with("email") finds <input name="email">
|
||||
# generate fields_with and field_with methods.
|
||||
# These methods are used for finding nodes that matches conditions.
|
||||
# ex.) field_with("email") finds <input name="email">
|
||||
|
||||
elements_with "field"
|
||||
elements_with "radiobutton"
|
||||
elements_with "checkbox", "checkboxes"
|
||||
elements_with "button"
|
||||
elements_with "field"
|
||||
elements_with "radiobutton"
|
||||
elements_with "checkbox", "checkboxes"
|
||||
elements_with "button"
|
||||
|
||||
# Returns all fields of <input type="textarea">
|
||||
def textareas
|
||||
fields.select { |f| f.class == FormContent::Textarea }.map &.as(FormContent::Textarea)
|
||||
end
|
||||
# Returns all fields of <input type="textarea">
|
||||
def textareas
|
||||
fields.select { |f| f.class == FormContent::Textarea }.map &.as(FormContent::Textarea)
|
||||
end
|
||||
|
||||
private def parse
|
||||
@node.css("input").not_nil!.each do |html_node|
|
||||
html_node = html_node.as(Lexbor::Node)
|
||||
type = (html_node["type"]? || "text").downcase
|
||||
case type
|
||||
when "checkbox"
|
||||
checkboxes << Mechanize::FormContent::CheckBox.new(html_node, self)
|
||||
when "radio"
|
||||
radiobuttons << Mechanize::FormContent::RadioButton.new(html_node, self)
|
||||
when "button"
|
||||
buttons << Mechanize::FormContent::Button.new(html_node, @node)
|
||||
when "submit"
|
||||
buttons << Mechanize::FormContent::SubmitButton.new(html_node, @node)
|
||||
when "reset"
|
||||
buttons << Mechanize::FormContent::ResetButton.new(html_node, @node)
|
||||
when "image"
|
||||
buttons << Mechanize::FormContent::ImageButton.new(html_node, @node)
|
||||
when "text"
|
||||
fields << Mechanize::FormContent::Text.new(html_node)
|
||||
when "hidden"
|
||||
fields << Mechanize::FormContent::Hidden.new(html_node)
|
||||
when "textarea"
|
||||
fields << Mechanize::FormContent::Textarea.new(html_node)
|
||||
else
|
||||
fields << Mechanize::FormContent::Field.new(html_node)
|
||||
private def parse
|
||||
@node.css("input").not_nil!.each do |html_node|
|
||||
html_node = html_node.as(Lexbor::Node)
|
||||
type = (html_node["type"]? || "text").downcase
|
||||
case type
|
||||
when "checkbox"
|
||||
checkboxes << FormContent::CheckBox.new(html_node, self)
|
||||
when "radio"
|
||||
radiobuttons << FormContent::RadioButton.new(html_node, self)
|
||||
when "button"
|
||||
buttons << FormContent::Button.new(html_node, @node)
|
||||
when "submit"
|
||||
buttons << FormContent::SubmitButton.new(html_node, @node)
|
||||
when "reset"
|
||||
buttons << FormContent::ResetButton.new(html_node, @node)
|
||||
when "image"
|
||||
buttons << FormContent::ImageButton.new(html_node, @node)
|
||||
when "text"
|
||||
fields << FormContent::Text.new(html_node)
|
||||
when "hidden"
|
||||
fields << FormContent::Hidden.new(html_node)
|
||||
when "textarea"
|
||||
fields << FormContent::Textarea.new(html_node)
|
||||
else
|
||||
fields << FormContent::Field.new(html_node)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Find all textarea tags
|
||||
@node.css("textarea").each do |node|
|
||||
node = node.as(Lexbor::Node)
|
||||
next if node["name"].empty?
|
||||
@fields << FormContent::Textarea.new(node, node.inner_text)
|
||||
end
|
||||
|
||||
@node.css("button").each do |node|
|
||||
node = node.as(Lexbor::Node)
|
||||
type = node.fetch("type", "submit").downcase
|
||||
next if type == "reset"
|
||||
@buttons << FormContent::Button.new(node, @node)
|
||||
end
|
||||
|
||||
# Find all select tags
|
||||
@node.css("select").each do |node|
|
||||
node = node.as(Lexbor::Node)
|
||||
next if node["name"].empty?
|
||||
if node.has_key?("multiple")
|
||||
@selectboxes << FormContent::MultiSelectList.new(node)
|
||||
else
|
||||
@selectboxes << FormContent::SelectList.new(node)
|
||||
# Find all textarea tags
|
||||
@node.css("textarea").each do |node|
|
||||
node = node.as(Lexbor::Node)
|
||||
next if node["name"].empty?
|
||||
@fields << FormContent::Textarea.new(node, node.inner_text)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private def build_query_string(params : Array(Array(String)))
|
||||
params.reduce("") do |acc, arr|
|
||||
hash = {arr[0] => arr[1]}
|
||||
acc + URI::Params.encode(hash) + '&'
|
||||
end.rchop
|
||||
end
|
||||
|
||||
private def build_query
|
||||
query = [] of Array(String)
|
||||
successful_controls = Array(FormContent::Field | FormContent::CheckBox).new
|
||||
fields.each do |elm|
|
||||
successful_controls << elm
|
||||
end
|
||||
checkboxes.each do |elm|
|
||||
if elm.checked
|
||||
successful_controls << elm
|
||||
@node.css("button").each do |node|
|
||||
node = node.as(Lexbor::Node)
|
||||
type = node.fetch("type", "submit").downcase
|
||||
next if type == "reset"
|
||||
@buttons << FormContent::Button.new(node, @node)
|
||||
end
|
||||
end
|
||||
radio_groups = Hash(String, Array(FormContent::RadioButton)).new
|
||||
radiobuttons.each do |radio|
|
||||
name = radio.name
|
||||
radio_groups[name] = Array(FormContent::RadioButton).new unless radio_groups.has_key?(name)
|
||||
radio_groups[name] << radio
|
||||
end
|
||||
|
||||
radio_groups.each_value do |g|
|
||||
checked = g.select(&.checked)
|
||||
if checked.uniq.size > 1
|
||||
# values = checked.map(&.value).join(', ').inspect
|
||||
# name = checked.first.name.inspect
|
||||
# raise Mechanize::Error,
|
||||
# "radiobuttons #{values} are checked in the #{name} group, " \
|
||||
# "only one is allowed"
|
||||
raise Mechanize::Error.new
|
||||
else
|
||||
successful_controls << checked.first unless checked.empty?
|
||||
end
|
||||
end
|
||||
|
||||
@clicked_buttons.each do |b|
|
||||
successful_controls << b
|
||||
end
|
||||
|
||||
successful_controls.each do |ctrl|
|
||||
value = ctrl.query_value
|
||||
next if value[0] == ""
|
||||
query.push(value)
|
||||
end
|
||||
|
||||
@selectboxes.each do |s|
|
||||
value = s.query_value
|
||||
if value
|
||||
value.each do |v|
|
||||
query.push(v)
|
||||
# Find all select tags
|
||||
@node.css("select").each do |node|
|
||||
node = node.as(Lexbor::Node)
|
||||
next if node["name"].empty?
|
||||
if node.has_key?("multiple")
|
||||
@selectboxes << FormContent::MultiSelectList.new(node)
|
||||
else
|
||||
@selectboxes << FormContent::SelectList.new(node)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
query
|
||||
end
|
||||
|
||||
# This method adds a button to the query. If the form needs to be
|
||||
# submitted with multiple buttons, pass each button to this method.
|
||||
def add_button_to_query(button)
|
||||
unless button.form_node == @node
|
||||
message = ""
|
||||
"#{button.inspect} does not belong to the same page as " \
|
||||
"the form #{@name.inspect} in #{@page.try &.uri}"
|
||||
message = "not a valid button"
|
||||
raise ArgumentError.new(message)
|
||||
private def build_query_string(params : Array(Array(String)))
|
||||
params.reduce("") do |acc, arr|
|
||||
hash = {arr[0] => arr[1]}
|
||||
acc + URI::Params.encode(hash) + '&'
|
||||
end.rchop
|
||||
end
|
||||
|
||||
@clicked_buttons << button
|
||||
private def build_query
|
||||
query = [] of Array(String)
|
||||
successful_controls = Array(FormContent::Field | FormContent::CheckBox).new
|
||||
fields.each do |elm|
|
||||
successful_controls << elm
|
||||
end
|
||||
checkboxes.each do |elm|
|
||||
if elm.checked
|
||||
successful_controls << elm
|
||||
end
|
||||
end
|
||||
radio_groups = Hash(String, Array(FormContent::RadioButton)).new
|
||||
radiobuttons.each do |radio|
|
||||
name = radio.name
|
||||
radio_groups[name] = Array(FormContent::RadioButton).new unless radio_groups.has_key?(name)
|
||||
radio_groups[name] << radio
|
||||
end
|
||||
|
||||
radio_groups.each_value do |g|
|
||||
checked = g.select(&.checked)
|
||||
if checked.uniq.size > 1
|
||||
# values = checked.map(&.value).join(', ').inspect
|
||||
# name = checked.first.name.inspect
|
||||
# raise Mechanize::Error,
|
||||
# "radiobuttons #{values} are checked in the #{name} group, " \
|
||||
# "only one is allowed"
|
||||
raise Error.new
|
||||
else
|
||||
successful_controls << checked.first unless checked.empty?
|
||||
end
|
||||
end
|
||||
|
||||
@clicked_buttons.each do |b|
|
||||
successful_controls << b
|
||||
end
|
||||
|
||||
successful_controls.each do |ctrl|
|
||||
value = ctrl.query_value
|
||||
next if value[0] == ""
|
||||
query.push(value)
|
||||
end
|
||||
|
||||
@selectboxes.each do |s|
|
||||
value = s.query_value
|
||||
if value
|
||||
value.each do |v|
|
||||
query.push(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
query
|
||||
end
|
||||
|
||||
# This method adds a button to the query. If the form needs to be
|
||||
# submitted with multiple buttons, pass each button to this method.
|
||||
def add_button_to_query(button)
|
||||
unless button.form_node == @node
|
||||
message = ""
|
||||
"#{button.inspect} does not belong to the same page as " \
|
||||
"the form #{@name.inspect} in #{@page.try &.uri}"
|
||||
message = "not a valid button"
|
||||
raise ArgumentError.new(message)
|
||||
end
|
||||
|
||||
@clicked_buttons << button
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,11 +7,11 @@ class Mechanize
|
|||
# max page size history can save. default is 100.
|
||||
# as same as `agent.max_history`.
|
||||
property max_size : Int32
|
||||
property array : Array(Mechanize::Page)
|
||||
property array : Array(Page)
|
||||
|
||||
delegate :size, :empty?, :last, to: array
|
||||
|
||||
def initialize(max_size = 100, array = Array(Mechanize::Page).new)
|
||||
def initialize(max_size = 100, array = Array(Page).new)
|
||||
@max_size = max_size
|
||||
@array = array
|
||||
end
|
||||
|
|
|
@ -9,12 +9,12 @@ class Mechanize
|
|||
class Agent
|
||||
property request_headers : ::HTTP::Headers
|
||||
property context : Mechanize?
|
||||
property history : Mechanize::History
|
||||
property history : History
|
||||
property user_agent : String
|
||||
property request_cookies : ::HTTP::Cookies
|
||||
|
||||
def initialize(@context : Mechanize? = nil)
|
||||
@history = Mechanize::History.new
|
||||
@history = History.new
|
||||
@request_headers = ::HTTP::Headers.new
|
||||
@context = context
|
||||
@request_cookies = ::HTTP::Cookies.new
|
||||
|
@ -73,7 +73,7 @@ class Mechanize
|
|||
# ```
|
||||
# agent.current_page
|
||||
# ```
|
||||
def current_page : Mechanize::Page
|
||||
def current_page : Page
|
||||
@history.last
|
||||
end
|
||||
|
||||
|
@ -81,7 +81,7 @@ class Mechanize
|
|||
# ```
|
||||
# agent.back
|
||||
# ```
|
||||
def back : Mechanize::Page
|
||||
def back : Page
|
||||
@history.pop
|
||||
end
|
||||
|
||||
|
@ -116,7 +116,7 @@ class Mechanize
|
|||
end
|
||||
|
||||
# Sets a Referer header.
|
||||
def set_request_referer(referer : Mechanize::Page?)
|
||||
def set_request_referer(referer : Page?)
|
||||
return unless referer
|
||||
|
||||
request_headers["Referer"] = referer.uri.to_s
|
||||
|
|
|
@ -7,7 +7,7 @@ class Mechanize
|
|||
# If you send a request, it returns the instance of `Mechanize::Page`.
|
||||
# You can get status code, title, and page body, and search html node using css selector from page instance.
|
||||
class Page < Mechanize::File
|
||||
include Mechanize::ElementMatcher
|
||||
include ElementMatcher
|
||||
|
||||
# look at lexbor document.(https://github.com/kostya/lexbor#readme)
|
||||
delegate :css, to: parser
|
||||
|
@ -42,7 +42,7 @@ class Mechanize
|
|||
# ```
|
||||
# page.forms # => Array(Mechanize::Form)
|
||||
# ```
|
||||
def forms : Array(Mechanize::Form)
|
||||
def forms : Array(Form)
|
||||
forms = css("form").map do |html_form|
|
||||
form = Form.new(html_form, self)
|
||||
form.action ||= @uri.to_s
|
||||
|
@ -54,7 +54,7 @@ class Mechanize
|
|||
# ```
|
||||
# page.links # => Array(Mechanize::PageContent::Link)
|
||||
# ```
|
||||
def links : Array(Mechanize::PageContent::Link)
|
||||
def links : Array(PageContent::Link)
|
||||
links = %w{a area}.map do |tag|
|
||||
css(tag).map do |node|
|
||||
PageContent::Link.new(node, @mech, self)
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
# This class represents link element like <a> and <area>.
|
||||
# The instance of this class is clickable.
|
||||
class Mechanize::PageContent::Link
|
||||
getter node : Lexbor::Node
|
||||
getter page : Mechanize::Page
|
||||
getter mech : Mechanize
|
||||
getter href : String
|
||||
getter text : String
|
||||
class Mechanize
|
||||
class PageContent::Link
|
||||
getter node : Lexbor::Node
|
||||
getter page : Page
|
||||
getter mech : Mechanize
|
||||
getter href : String
|
||||
getter text : String
|
||||
|
||||
def initialize(node, mech, page)
|
||||
@node = node
|
||||
@page = page
|
||||
@mech = mech
|
||||
@href = node.fetch("href", "")
|
||||
@text = node.inner_text
|
||||
end
|
||||
def initialize(node, mech, page)
|
||||
@node = node
|
||||
@page = page
|
||||
@mech = mech
|
||||
@href = node.fetch("href", "")
|
||||
@text = node.inner_text
|
||||
end
|
||||
|
||||
# click on this link
|
||||
def click
|
||||
@mech.click self
|
||||
# click on this link
|
||||
def click
|
||||
@mech.click self
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -65,7 +65,7 @@ class Mechanize
|
|||
def {{singular.id}}_with(criteria)
|
||||
f = {{plural.id}}_with(criteria)
|
||||
# TODO: Write correct error message.
|
||||
raise Mechanize::ElementNotFoundError.new(:{{singular.id}}, "") if f.empty?
|
||||
raise ElementNotFoundError.new(:{{singular.id}}, "") if f.empty?
|
||||
f.first
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue