saper 0.5.1 → 0.5.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.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -0
  3. data/Rakefile +6 -6
  4. data/bin/saper +17 -36
  5. data/lib/saper.rb +63 -20
  6. data/lib/saper/actions/append_with.rb +1 -1
  7. data/lib/saper/actions/convert_to_html.rb +1 -1
  8. data/lib/saper/actions/convert_to_json.rb +6 -2
  9. data/lib/saper/actions/convert_to_markdown.rb +1 -1
  10. data/lib/saper/actions/convert_to_time.rb +38 -1
  11. data/lib/saper/actions/convert_to_xml.rb +1 -1
  12. data/lib/saper/actions/create_atom.rb +6 -2
  13. data/lib/saper/actions/fetch.rb +3 -2
  14. data/lib/saper/actions/fetch_with_token.rb +18 -0
  15. data/lib/saper/actions/find.rb +1 -1
  16. data/lib/saper/actions/find_first.rb +1 -1
  17. data/lib/saper/actions/get_attribute.rb +7 -2
  18. data/lib/saper/actions/get_contents.rb +1 -1
  19. data/lib/saper/actions/get_text.rb +1 -1
  20. data/lib/saper/actions/nothing.rb +11 -0
  21. data/lib/saper/actions/prepend_with.rb +2 -2
  22. data/lib/saper/actions/remove_after.rb +2 -2
  23. data/lib/saper/actions/remove_before.rb +2 -2
  24. data/lib/saper/actions/remove_matching.rb +2 -2
  25. data/lib/saper/actions/remove_tags.rb +1 -1
  26. data/lib/saper/actions/replace.rb +1 -1
  27. data/lib/saper/actions/run_recipe.rb +2 -2
  28. data/lib/saper/actions/run_recipe_and_save.rb +3 -3
  29. data/lib/saper/actions/save.rb +2 -2
  30. data/lib/saper/actions/select_matching.rb +2 -2
  31. data/lib/saper/actions/set_input.rb +1 -1
  32. data/lib/saper/actions/skip_tags.rb +1 -1
  33. data/lib/saper/actions/split.rb +1 -1
  34. data/lib/saper/arguments/attribute.rb +6 -0
  35. data/lib/saper/arguments/recipe.rb +12 -6
  36. data/lib/saper/arguments/service.rb +12 -0
  37. data/lib/saper/arguments/text.rb +1 -0
  38. data/lib/saper/arguments/timezone.rb +2 -1
  39. data/lib/saper/arguments/url.rb +12 -0
  40. data/lib/saper/arguments/variable.rb +2 -1
  41. data/lib/saper/arguments/xpath.rb +2 -1
  42. data/lib/saper/core/action.rb +43 -13
  43. data/lib/saper/core/argument.rb +30 -14
  44. data/lib/saper/core/browser.rb +29 -28
  45. data/lib/saper/core/dsl.rb +14 -16
  46. data/lib/saper/core/error.rb +1 -1
  47. data/lib/saper/core/item.rb +2 -2
  48. data/lib/saper/core/keychain.rb +58 -5
  49. data/lib/saper/core/namespace.rb +15 -18
  50. data/lib/saper/core/recipe.rb +53 -12
  51. data/lib/saper/core/result.rb +72 -0
  52. data/lib/saper/core/runtime.rb +44 -177
  53. data/lib/saper/core/stack.rb +57 -0
  54. data/lib/saper/items/atom.rb +18 -1
  55. data/lib/saper/items/document.rb +39 -19
  56. data/lib/saper/items/html.rb +52 -7
  57. data/lib/saper/items/json.rb +19 -1
  58. data/lib/saper/items/markdown.rb +14 -4
  59. data/lib/saper/items/nothing.rb +6 -0
  60. data/lib/saper/items/text.rb +28 -3
  61. data/lib/saper/items/time.rb +25 -2
  62. data/lib/saper/items/url.rb +8 -2
  63. data/lib/saper/items/xml.rb +51 -11
  64. data/lib/{lib → saper/patches}/mechanize.rb +0 -0
  65. data/lib/{lib → saper/patches}/nokogiri.rb +0 -0
  66. data/lib/saper/version.rb +2 -2
  67. data/spec/actions/append_with_spec.rb +29 -52
  68. data/spec/actions/convert_to_html_spec.rb +13 -30
  69. data/spec/actions/convert_to_json_spec.rb +13 -30
  70. data/spec/actions/convert_to_markdown_spec.rb +17 -19
  71. data/spec/actions/convert_to_time_spec.rb +25 -43
  72. data/spec/actions/convert_to_xml_spec.rb +15 -11
  73. data/spec/actions/create_atom_spec.rb +11 -19
  74. data/spec/actions/fetch_spec.rb +3 -8
  75. data/spec/actions/find_first_spec.rb +38 -40
  76. data/spec/actions/find_spec.rb +23 -39
  77. data/spec/actions/get_attribute_spec.rb +30 -3
  78. data/spec/actions/{get_contents.rb → get_contents_spec.rb} +0 -0
  79. data/spec/actions/{get_text.rb → get_text_spec.rb} +0 -0
  80. data/spec/actions/nothing_spec.rb +7 -0
  81. data/spec/actions/prepend_with_spec.rb +31 -18
  82. data/spec/actions/{remove_after.rb → remove_after_spec.rb} +0 -0
  83. data/spec/actions/{remove_before.rb → remove_before_spec.rb} +0 -0
  84. data/spec/actions/remove_matching_spec.rb +7 -0
  85. data/spec/actions/remove_tags_spec.rb +7 -0
  86. data/spec/actions/run_recipe_and_save_spec.rb +7 -0
  87. data/spec/actions/run_recipe_spec.rb +7 -0
  88. data/spec/arguments/attribute_spec.rb +7 -0
  89. data/spec/arguments/recipe_spec.rb +7 -0
  90. data/spec/arguments/text_spec.rb +7 -0
  91. data/spec/arguments/timezone_spec.rb +7 -0
  92. data/spec/arguments/variable_spec.rb +7 -0
  93. data/spec/arguments/xpath_spec.rb +7 -0
  94. data/spec/core/action_spec.rb +13 -142
  95. data/spec/core/argument_spec.rb +38 -71
  96. data/spec/core/browser_spec.rb +18 -3
  97. data/spec/core/dsl_spec.rb +1 -1
  98. data/spec/core/item_spec.rb +1 -1
  99. data/spec/core/namespace_spec.rb +0 -11
  100. data/spec/core/recipe_spec.rb +54 -77
  101. data/spec/core/result_spec.rb +7 -0
  102. data/spec/core/runtime_spec.rb +20 -151
  103. data/spec/core/stack_spec.rb +7 -0
  104. data/spec/integration/simple_invalid_spec.rb +39 -0
  105. data/spec/integration/simple_valid_spec.rb +39 -0
  106. data/spec/items/atom_spec.rb +6 -1
  107. data/spec/items/document_spec.rb +93 -15
  108. data/spec/items/html_spec.rb +45 -28
  109. data/spec/items/json_spec.rb +10 -10
  110. data/spec/items/markdown_spec.rb +24 -3
  111. data/spec/items/nothing_spec.rb +1 -1
  112. data/spec/items/text_spec.rb +13 -41
  113. data/spec/items/time_spec.rb +25 -4
  114. data/spec/items/url_spec.rb +1 -7
  115. data/spec/items/xml_spec.rb +46 -39
  116. data/spec/spec_helper.rb +2 -21
  117. metadata +63 -60
  118. data/lib/lib/json_search.rb +0 -54
  119. data/spec/actions/run_recipe_and_save_spec.tmp.rb +0 -52
  120. data/spec/actions/run_recipe_spec.tmp.rb +0 -53
@@ -2,12 +2,17 @@ module Saper
2
2
  module Items
3
3
  class HTML < Item
4
4
 
5
+ # @todo
5
6
  def self.new(item)
6
7
  super case item
7
8
  when Nokogiri::XML::Element
8
9
  item
10
+ when Nokogiri::XML::Document
11
+ item
9
12
  when Nokogiri::HTML
10
13
  item
14
+ when Document
15
+ parse(item.body, item.uri, item.charset)
11
16
  when Text
12
17
  parse(item.to_s)
13
18
  when String
@@ -17,14 +22,23 @@ module Saper
17
22
  end
18
23
  end
19
24
 
25
+ # @todo
20
26
  def self.parse(string, uri = nil, charset = nil)
27
+ if string.empty?
28
+ raise(InvalidItem, string)
29
+ end
21
30
  begin
31
+ # Nokogiri adds an additional body tag for strings that don't
32
+ # have one. Should probably use Nokogiri::HTML.fragment in that
33
+ # case, but it breaks the search function:
34
+ # https://github.com/sparklemotion/nokogiri/issues/572
22
35
  Nokogiri::HTML.parse(string, uri, charset)
23
36
  rescue
24
37
  raise(InvalidItem, string)
25
38
  end
26
39
  end
27
40
 
41
+ # @todo
28
42
  def initialize(noko)
29
43
  @noko = noko
30
44
  # Force UTF-8 encoding
@@ -32,48 +46,79 @@ module Saper
32
46
  @noko.document.encoding = 'UTF-8'
33
47
  end
34
48
 
49
+ # @todo
50
+ def dup
51
+ HTML.new(@noko.dup)
52
+ end
53
+
54
+ # @todo
55
+ def ==(other)
56
+ to_s == other.to_s
57
+ end
58
+
59
+ # @todo
35
60
  def name
36
61
  @noko.name
37
62
  end
38
63
 
64
+ # @todo
39
65
  def [](name)
40
- @noko[name]
66
+ @noko.has_attribute?(name) ? Text.new(@noko[name]) : nil
41
67
  end
42
68
 
69
+ # @todo
43
70
  def find(xpath)
44
71
  find_all(xpath).first
45
72
  end
46
73
 
74
+ # @todo
47
75
  def find_all(xpath)
48
76
  @noko.search(xpath).map { |element| HTML.new(element) }
49
77
  end
50
78
 
51
- def remove_children_with_content(xpath)
79
+ # @todo
80
+ def remove_children_with_content!(xpath)
52
81
  @noko.search(xpath).each { |item| item.remove }; self
53
82
  end
54
83
 
55
- def remove_children_preserving_content(xpath)
84
+ # @todo
85
+ def remove_children_with_content(xpath)
86
+ dup.remove_children_with_content!(xpath)
87
+ end
88
+
89
+ # @todo
90
+ def remove_children_preserving_content!(xpath)
56
91
  @noko.search(xpath).each { |item| item.replace(item.children) }; self
57
92
  end
93
+
94
+ # @todo
95
+ def remove_children_preserving_content(xpath)
96
+ dup.remove_children_preserving_content!(xpath)
97
+ end
58
98
 
99
+ # @todo
59
100
  def inner_html
60
- @noko.inner_html
101
+ Text.new @noko.inner_html.gsub("\n","")
61
102
  end
62
103
 
104
+ # @todo
63
105
  def inner_text
64
- @noko.inner_text
106
+ Text.new @noko.inner_text
65
107
  end
66
108
 
109
+ # @todo
67
110
  def to_markdown
68
111
  Markdown.new self
69
112
  end
70
113
 
114
+ # @todo
71
115
  def to_native
72
- inner_html
116
+ inner_html.to_native
73
117
  end
74
118
 
119
+ # @todo
75
120
  def to_s
76
- inner_html
121
+ @noko.is_a?(Nokogiri::HTML::Document) ? to_native : @noko.to_s
77
122
  end
78
123
 
79
124
  end
@@ -2,6 +2,7 @@ module Saper
2
2
  module Items
3
3
  class JSON < Item
4
4
 
5
+ # @todo
5
6
  def self.new(item, bypass = false)
6
7
  super case item
7
8
  when Text
@@ -15,6 +16,7 @@ module Saper
15
16
  end
16
17
  end
17
18
 
19
+ # @todo
18
20
  def self.parse(string)
19
21
  begin
20
22
  ::JSON.parse(string)
@@ -23,28 +25,44 @@ module Saper
23
25
  end
24
26
  end
25
27
 
28
+ # @todo
26
29
  def initialize(hash)
27
30
  @hash = hash
28
31
  end
29
32
 
33
+ # @todo
34
+ def ==(other)
35
+ @hash == other
36
+ end
37
+
38
+ # @todo
30
39
  def find(xpath)
31
40
  find_all(xpath).first
32
41
  end
33
42
 
43
+ # @todo
34
44
  def find_all(xpath)
35
- sanitize JSONSearch.find(@hash, xpath)
45
+ sanitize JPath.find(@hash, xpath)
36
46
  end
37
47
 
48
+ # @todo
38
49
  def to_s
39
50
  ::JSON.dump(@hash)
40
51
  end
41
52
 
53
+ # @todo
54
+ def to_json(*a)
55
+ a.empty? ? self : @hash
56
+ end
57
+
58
+ # @todo
42
59
  def to_native
43
60
  to_s
44
61
  end
45
62
 
46
63
  private
47
64
 
65
+ # @todo
48
66
  def sanitize(item)
49
67
  case item
50
68
  when Hash
@@ -2,33 +2,43 @@ module Saper
2
2
  module Items
3
3
  class Markdown < Item
4
4
 
5
+ # @todo
5
6
  def self.new(item)
6
7
  super case item
7
8
  when HTML
8
- parse(item.inner_html)
9
+ parse(item.to_s)
9
10
  else
10
11
  raise(InvalidItem, item)
11
12
  end
12
13
  end
13
14
 
15
+ # @todo
14
16
  def self.parse(string)
15
17
  begin
16
- ::ReverseMarkdown.convert(string).strip
18
+ ::ReverseMarkdown.convert(string, unknown_tags: :drop).strip
17
19
  rescue
18
20
  raise(InvalidItem, string)
19
21
  end
20
22
  end
21
23
 
24
+ # @todo
22
25
  def initialize(string)
23
26
  @string = string
24
27
  end
25
28
 
29
+ # @todo
30
+ def ==(other)
31
+ to_s == other.to_s
32
+ end
33
+
34
+ # @todo
26
35
  def to_s
27
- @string
36
+ to_native
28
37
  end
29
38
 
39
+ # @todo
30
40
  def to_native
31
- to_s
41
+ @string
32
42
  end
33
43
 
34
44
  end
@@ -2,14 +2,20 @@ module Saper
2
2
  module Items
3
3
  class Nothing < Item
4
4
 
5
+ # @todo
5
6
  def nil?
6
7
  true
7
8
  end
8
9
 
10
+ # @todo
9
11
  def to_native
10
12
  nil
11
13
  end
12
14
 
15
+ def to_s
16
+ 'nil'
17
+ end
18
+
13
19
  end
14
20
  end
15
21
  end
@@ -2,6 +2,7 @@ module Saper
2
2
  module Items
3
3
  class Text < Item
4
4
 
5
+ # @todo
5
6
  def self.new(item)
6
7
  super case item
7
8
  when String
@@ -13,40 +14,64 @@ module Saper
13
14
  end
14
15
  end
15
16
 
17
+ # @todo
16
18
  def initialize(string)
17
19
  @string = string
18
20
  end
19
21
 
20
- def split(separator, number = nil)
22
+ # @todo
23
+ def ==(other)
24
+ hash == other.hash
25
+ end
26
+
27
+ # @todo
28
+ def hash
29
+ to_s.hash
30
+ end
31
+
32
+ # @todo
33
+ def empty?
34
+ @string.empty?
35
+ end
36
+
37
+ # @todo
38
+ def split(separator, number = -1)
21
39
  @string.split(separator, number)
22
40
  end
23
41
 
42
+ # @todo
24
43
  def gsub(*args, &block)
25
44
  @string.gsub(*args, &block)
26
45
  end
27
46
 
47
+ # @todo
28
48
  def to_xml
29
49
  XML.new(self)
30
50
  end
31
51
 
52
+ # @todo
32
53
  def to_html
33
54
  HTML.new(self)
34
55
  end
35
56
 
57
+ # @todo
36
58
  def to_json
37
59
  JSON.new(self)
38
60
  end
39
61
 
62
+ # @todo
40
63
  def to_time(format, tz = nil)
41
64
  Time.new(self, format, tz)
42
65
  end
43
66
 
67
+ # @todo
44
68
  def to_s
45
- @string
69
+ to_native
46
70
  end
47
71
 
72
+ # @todo
48
73
  def to_native
49
- to_s
74
+ @string
50
75
  end
51
76
 
52
77
  end
@@ -2,17 +2,21 @@ module Saper
2
2
  module Items
3
3
  class Time < Item
4
4
 
5
- def self.new(item, format, tz = nil)
5
+ # @todo
6
+ def self.new(item, format = nil, tz = nil)
6
7
  super case item
7
8
  when Text
8
9
  parse(item.to_s, format, tz)
9
10
  when String
10
11
  parse(item, format, tz)
12
+ when Vremya
13
+ item
11
14
  else
12
15
  raise(InvalidItem, item)
13
16
  end
14
17
  end
15
18
 
19
+ # @todo
16
20
  def self.parse(string, format, tz = nil)
17
21
  begin
18
22
  ::Vremya.parse(string, format, tz)
@@ -21,20 +25,39 @@ module Saper
21
25
  end
22
26
  end
23
27
 
28
+ # @todo
29
+ def self.now(tz = nil)
30
+ new ::Vremya.now(tz)
31
+ end
32
+
33
+ # @todo
24
34
  def initialize(vremya)
25
35
  @vremya = vremya
26
36
  end
27
37
 
38
+ # @todo
39
+ def ==(other)
40
+ to_i == other.to_i
41
+ end
42
+
43
+ # @todo
28
44
  def to_s
29
45
  @vremya.iso8601
30
46
  end
31
47
 
48
+ # @todo
32
49
  def to_native
33
50
  @vremya.to_time
34
51
  end
35
52
 
53
+ # @todo
54
+ def to_i
55
+ to_native.to_i
56
+ end
57
+
58
+ # @todo
36
59
  def serialize
37
- to_s
60
+ @vremya.to_utc.to_time
38
61
  end
39
62
 
40
63
  end
@@ -2,6 +2,7 @@ module Saper
2
2
  module Items
3
3
  class URL < Item
4
4
 
5
+ # @todo
5
6
  def self.new(item)
6
7
  super case item
7
8
  when Text
@@ -13,22 +14,27 @@ module Saper
13
14
  end
14
15
  end
15
16
 
17
+ # @todo
16
18
  def self.parse(string)
17
19
  unless string =~ /^#{URI::regexp}$/
18
20
  raise(InvalidItem, string)
19
21
  end
22
+ string
20
23
  end
21
24
 
25
+ # @todo
22
26
  def initialize(url)
23
27
  @url = url
24
28
  end
25
29
 
30
+ # @todo
26
31
  def to_s
27
- @url
32
+ to_native
28
33
  end
29
34
 
35
+ # @todo
30
36
  def to_native
31
- to_s
37
+ @url
32
38
  end
33
39
 
34
40
  end
@@ -2,10 +2,15 @@ module Saper
2
2
  module Items
3
3
  class XML < Item
4
4
 
5
+ # @todo
5
6
  def self.new(item)
6
7
  super case item
7
8
  when Nokogiri::XML
8
9
  item
10
+ when Nokogiri::XML::Document
11
+ item
12
+ when Nokogiri::XML::Element
13
+ item
9
14
  when Text
10
15
  parse(item.to_s)
11
16
  when String
@@ -15,7 +20,11 @@ module Saper
15
20
  end
16
21
  end
17
22
 
23
+ # @todo
18
24
  def self.parse(string, uri = nil, charset = nil)
25
+ if string.empty?
26
+ raise(InvalidItem, string)
27
+ end
19
28
  begin
20
29
  Nokogiri::XML.parse(string, uri, charset)
21
30
  rescue
@@ -23,6 +32,7 @@ module Saper
23
32
  end
24
33
  end
25
34
 
35
+ # @todo
26
36
  def initialize(noko)
27
37
  @noko = noko
28
38
  # Force UTF-8 encoding
@@ -30,46 +40,76 @@ module Saper
30
40
  @noko.document.encoding = 'UTF-8'
31
41
  end
32
42
 
43
+ # @todo
44
+ def dup
45
+ XML.new(@noko.dup)
46
+ end
47
+
48
+ # @todo
49
+ def ==(other)
50
+ to_s == other.to_s
51
+ end
52
+
53
+ # @todo
33
54
  def name
34
55
  @noko.name
35
56
  end
36
57
 
58
+ # @todo
37
59
  def [](name)
38
- @noko[name]
60
+ @noko.has_attribute?(name) ? Text.new(@noko[name]) : nil
39
61
  end
40
62
 
63
+ # @todo
41
64
  def find(xpath)
42
65
  find_all(xpath).first
43
66
  end
44
67
 
68
+ # @todo
45
69
  def find_all(xpath)
46
70
  @noko.search(xpath).map { |element| XML.new(element) }
47
71
  end
48
72
 
49
- def remove_children_with_content(xpath)
73
+ # @todo
74
+ def remove_children_with_content!(xpath)
50
75
  @noko.search(xpath).each { |item| item.remove }; self
51
76
  end
52
77
 
53
- def remove_children_preserving_content(xpath)
54
- @noko.search(xpath).each { |item| item.replace(item.children) }; self
78
+ # @todo
79
+ def remove_children_with_content(xpath)
80
+ dup.remove_children_with_content!(xpath)
55
81
  end
56
82
 
57
- def inner_html
58
- @noko.inner_html
83
+ # @todo
84
+ def remove_children_preserving_content!(xpath)
85
+ @noko.search(xpath).each { |item| item.replace(item.children) }; self
86
+ end
87
+
88
+ # @todo
89
+ def remove_children_preserving_content(xpath)
90
+ dup.remove_children_preserving_content!(xpath)
59
91
  end
60
92
 
61
- def inner_text
62
- @noko.inner_text
93
+ # @todo
94
+ def inner_xml
95
+ Text.new @noko.inner_html
63
96
  end
64
97
 
65
- def to_s
66
- inner_html
98
+ # @todo
99
+ def inner_text
100
+ Text.new @noko.inner_text
67
101
  end
68
102
 
103
+ # @todo
69
104
  def to_native
70
- inner_html
105
+ inner_xml.to_native
71
106
  end
72
107
 
108
+ # @todo
109
+ def to_s
110
+ @noko.is_a?(Nokogiri::XML::Document) ? to_native : @noko.to_s
111
+ end
112
+
73
113
  end
74
114
  end
75
115
  end