add comment

master
Kanezoh 2021-11-16 19:45:41 +09:00
parent fb18b49a3c
commit 45f14baad0
4 changed files with 128 additions and 117 deletions

View File

@ -9,11 +9,11 @@ require "./form/select_list"
require "./utils/element_matcher"
# THis class represents the form tag of html.
class MechanizeCr::Form
include MechanizeCr::ElementMatcher
class Mechanize::Form
include Mechanize::ElementMatcher
getter node : Node | Lexbor::Node
# returns an array of `MechanizeCr::FormContent::Field` in the form.
# returns an 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)

View File

@ -2,11 +2,12 @@ require "./page"
# This class represents the history of http response you sent.
# If you send a request, mechanize saves the history.
class Mechanize::History
# max page size history can save. default is 100.
# as same as `agent.max_history`.
property max_size : Int32
property array : Array(Mechanize::Page)
class Mechanize
class History
# max page size history can save. default is 100.
# as same as `agent.max_history`.
property max_size : Int32
property array : Array(Mechanize::Page)
delegate :size, :empty?, :last, to: array
@ -15,17 +16,21 @@ class Mechanize::History
@array = array
end
# add page to history.
def push(page, uri = nil)
@array.push(page)
while size > @max_size
@array.shift
# add page to history.
def push(page, uri = nil)
@array.push(page)
while size > @max_size
@array.shift
end
self
end
# take the last page out from history.
def pop
if size == 0
# TODO: raise error
# take the last page out from history.
def pop
if size == 0
# TODO: raise error
end
page = @array.pop
end
end
end

View File

@ -5,13 +5,12 @@ require "./page/link"
# This class represents the result of http response.
# 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 Mechanize::Page < Mechanize::File
include Mechanize::ElementMatcher
class Mechanize
class Page < Mechanize::File
include Mechanize::ElementMatcher
# look at lexbor document.(https://github.com/kostya/lexbor#readme)
delegate :css, to: parser
property mech : Mechanize
# look at lexbor document.(https://github.com/kostya/lexbor#readme)
delegate :css, to: parser
property mech : Mechanize
@ -20,41 +19,48 @@ class Mechanize::Page < Mechanize::File
super(uri, response, body, code)
end
# return page title.
# ```
# page.title # => String
# ```
def title : String
title_node = css("title")
if title_node.empty?
""
else
title_node.first.inner_text
# parser to parse response body.
# TODO: now it's Lexbor::Parser. I want to also support other parsers like JSON.
def parser : Lexbor::Parser
@parser ||= Lexbor::Parser.new(@body)
end
# return all forms(`Mechanize::Form`) in the page.
# ```
# page.forms # => Array(Mechanize::Form)
# ```
def forms : Array(Mechanize::Form)
forms = css("form").map do |html_form|
form = Form.new(html_form, self)
form.action ||= @uri.to_s
form
end.to_a
end
# return all links(`Mechanize::PageContent::Link`) in the page.
# ```
# page.links # => Array(Mechanize::PageContent::Link)
# ```
def links : Array(Mechanize::PageContent::Link)
links = %w{a area}.map do |tag|
css(tag).map do |node|
PageContent::Link.new(node, @mech, self)
# return page title.
# ```
# page.title # => String
# ```
def title : String
title_node = css("title")
if title_node.empty?
""
else
title_node.first.inner_text
end
end.flatten
end
end
# return all forms(`Mechanize::Form`) in the page.
# ```
# page.forms # => Array(Mechanize::Form)
# ```
def forms : Array(Mechanize::Form)
forms = css("form").map do |html_form|
form = Form.new(html_form, self)
form.action ||= @uri.to_s
form
end.to_a
end
# return all links(`Mechanize::PageContent::Link`) in the page.
# ```
# page.links # => Array(Mechanize::PageContent::Link)
# ```
def links : Array(Mechanize::PageContent::Link)
links = %w{a area}.map do |tag|
css(tag).map do |node|
PageContent::Link.new(node, @mech, self)
end
end.flatten
end
elements_with "form"
end

View File

@ -1,71 +1,71 @@
class Mechanize
module ElementMatcher
macro elements_with(singular, plural = "")
{% plural = "#{singular.id}s" if plural.empty? %}
# search {{ plural.id }} which match condition.
#
# Examples
# ```
# # if you specify String like "foo", it searches form which name is "foo".
{% if ["form", "button"].includes?("#{singular.id}") %}
# # like <{{ singular.id }} name="foo"></{{ singular.id }}>
{% elsif "#{singular.id}" == "field" %}
# # like <input name="foo"></input>
{% elsif "#{singular.id}" == "radiobutton" %}
# # like <input type="radio" name="foo"></input>
{% else %}
# # like <input type="{{ singular.id }}" name="foo"></input>
{% end %}
# page.{{ plural.id }}_with("foo")
# # you can also specify tag's attribute and its' value by NamedTuple or Hash(String, String).
{% if ["form", "button"].includes?("#{singular.id}") %}
# # ex) <{{ singular.id }} class="foo"></{{ singular.id }}>
{% elsif "#{singular.id}" == "field" %}
# # ex) <input class="foo"></input>
{% elsif "#{singular.id}" == "radiobutton" %}
# # ex) <input type="radio" class="foo"></input>
{% else %}
# # ex) <input type="{{ singular.id }}" class="foo"></input>
{% end %}
# page.{{ plural.id }}_with("class" => "foo")
# page.{{ plural.id }}_with(class: "foo")
# ```
def {{plural.id}}_with(criteria : String | NamedTuple | Hash(String,String))
{{plural.id}}_with(criteria){}
end
{% plural = "#{singular.id}s" if plural.empty? %}
# search {{ plural.id }} which match condition.
#
# Examples
# ```
# # if you specify String like "foo", it searches form which name is "foo".
{% if ["form", "button"].includes?("#{singular.id}") %}
# # like <{{ singular.id }} name="foo"></{{ singular.id }}>
{% elsif "#{singular.id}" == "field" %}
# # like <input name="foo"></input>
{% elsif "#{singular.id}" == "radiobutton" %}
# # like <input type="radio" name="foo"></input>
{% else %}
# # like <input type="{{ singular.id }}" name="foo"></input>
{% end %}
# page.{{ plural.id }}_with("foo")
# # you can also specify tag's attribute and its' value by NamedTuple or Hash(String, String).
{% if ["form", "button"].includes?("#{singular.id}") %}
# # ex) <{{ singular.id }} class="foo"></{{ singular.id }}>
{% elsif "#{singular.id}" == "field" %}
# # ex) <input class="foo"></input>
{% elsif "#{singular.id}" == "radiobutton" %}
# # ex) <input type="radio" class="foo"></input>
{% else %}
# # ex) <input type="{{ singular.id }}" class="foo"></input>
{% end %}
# page.{{ plural.id }}_with("class" => "foo")
# page.{{ plural.id }}_with(class: "foo")
# ```
def {{plural.id}}_with(criteria : String | NamedTuple | Hash(String,String))
{{plural.id}}_with(criteria){}
end
def {{plural.id}}_with(criteria, &block)
if criteria.is_a?(NamedTuple)
criteria = criteria.to_h
end
if criteria.is_a?(String)
criteria = {"name" => criteria}
else
criteria = criteria.each_with_object(Hash(String,String).new) do |(k, v), h|
k = k.to_s
h[k] = v
def {{plural.id}}_with(criteria, &block)
if criteria.is_a?(NamedTuple)
criteria = criteria.to_h
end
end
f = {{plural.id}}.select do |elm|
criteria.all? do |k,v|
if k == "text"
v == elm.node.inner_text
else
v == elm.node.fetch(k,"")
if criteria.is_a?(String)
criteria = {"name" => criteria}
else
criteria = criteria.each_with_object(Hash(String,String).new) do |(k, v), h|
k = k.to_s
h[k] = v
end
end
f = {{plural.id}}.select do |elm|
criteria.all? do |k,v|
if k == "text"
v == elm.node.inner_text
else
v == elm.node.fetch(k,"")
end
end
end
yield f
f
end
yield f
f
end
# returns first element of `#{{ plural.id }}_with`
def {{singular.id}}_with(criteria)
f = {{plural.id}}_with(criteria)
# TODO: Write correct error message.
raise Mechanize::ElementNotFoundError.new(:{{singular.id}}, "") if f.empty?
f.first
# returns first element of `#{{ plural.id }}_with`
def {{singular.id}}_with(criteria)
f = {{plural.id}}_with(criteria)
# TODO: Write correct error message.
raise Mechanize::ElementNotFoundError.new(:{{singular.id}}, "") if f.empty?
f.first
end
end
end
end
end