azebiki 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
File without changes
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Eric Allam
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,75 @@
1
+ ## Overview
2
+
3
+ Azebiki provides a simple dsl for declaring CSS and XPATH matchers against a block of HTML content. It is built on top of Webrat matchers and nokogiri.
4
+
5
+ ## Example:
6
+
7
+ Given this block of HTML:
8
+
9
+ <html>
10
+ <head><title>Example</title></head>
11
+ <body>
12
+ <!--
13
+ <a href="http://incomment.com">In Comment</a>
14
+ -->
15
+ <div id='main' class="big">
16
+ <p id="body">
17
+ <table class='short table'>
18
+ <thead>
19
+ <tr>
20
+ <th>First Column</th>
21
+ </tr>
22
+ </thead>
23
+ </table>
24
+ </p>
25
+ </div>
26
+
27
+ Given this Azebiki::Checker definition:
28
+
29
+ c = Azebiki::Checker.new(HTML) do
30
+ div('#main.big') do
31
+ p('#body') do
32
+ table do
33
+ thead do
34
+ tr do
35
+ th(:content => 'First Column')
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ Will result in:
44
+
45
+ c.success?
46
+ # => true
47
+
48
+ Or given this definition:
49
+
50
+ c = Azebiki::Checker.new(HTML) do
51
+ a(:href => 'http://incomment.com', :content => 'In Comment')
52
+ end
53
+
54
+ Will result in:
55
+
56
+ c.success?
57
+ # => false
58
+ c.errors.inspect
59
+ # => ['Content should have included <a href="http://incomment.com">In Comment</a>, but did not']
60
+
61
+ Or give it a custom error message:
62
+
63
+ c = Azebiki::Checker.new(HTML) do
64
+ a(:href => 'http://incomment.com', :content => 'In Comment').failure_message('No tag :tag, SORRY!')
65
+ end
66
+
67
+ c.errors.inspect
68
+ # => ['No tag <a href="http://incomment.com">In Comment</a>, SORRY!']
69
+
70
+
71
+ ## Install
72
+
73
+ gem install azebiki
74
+
75
+
File without changes
@@ -0,0 +1 @@
1
+ require "azebiki/azebiki"
@@ -0,0 +1,234 @@
1
+ require 'webrat/core/matchers/have_selector'
2
+ require 'webrat/core/matchers/have_content'
3
+ require 'nokogiri'
4
+
5
+ # This class is useful for making sure an HTML text has certain tags/content
6
+ # It supports both XML and HTML. For example, if you want to make sure someone has included
7
+ # a link that points to google, has no follow, with the text 'Google Sucks!':
8
+ # c = Checker.new(html) do |v|
9
+ # v.matches('a', :href => "http://google.com", :rel => "nofollow", :content => "Google Sucks!")
10
+ # end
11
+ #
12
+ # c.success? == true # if html does include the link to google
13
+ # c.errors == [] # errors is a list of error messages for each match that did not succeed, which can be customized:
14
+ #
15
+ # c = Checker.new(html) do |v|
16
+ # v.matches('a', :href => "http://google.com", :rel => "nofollow", :content => "Google Sucks!").failure_message('Sorry, no google link, should have :tag')
17
+ # end
18
+ #
19
+ # c.errors
20
+ #=> ["Sorry, no google link, should have <a href='http://google.com' rel='nofollow'>Google Sucks!</a>"]
21
+ #
22
+ # You can also nest matches, to match against children content. For example, if you
23
+ # want check for an image link:
24
+ #
25
+ # c = Checker.new(html) do |v|
26
+ # v.matches('a', :href => "http://google.com", :rel => "nofollow", :content => "Google Sucks!") do |a|
27
+ # a.matches('img', :src => 'http://google.com/sucks.png')
28
+ # end
29
+ # end
30
+ #
31
+ # which will succeed only if the img tag is nested below the a tag. You can nest matchers pretty deep
32
+ module Azebiki
33
+ class Checker
34
+
35
+ class MyHaveSelector < Webrat::Matchers::HaveSelector
36
+ def add_attributes_conditions_to(query)
37
+ attribute_conditions = []
38
+
39
+ @options.each do |key, value|
40
+ next if [:content, :count].include?(key)
41
+ if value.is_a?(Hash)
42
+ func, match = value.keys.first, value.values.first
43
+ attribute_conditions << "#{func}(@#{key}, #{xpath_escape(match)})"
44
+ else
45
+ attribute_conditions << "@#{key} = #{xpath_escape(value)}"
46
+ end
47
+ end
48
+
49
+ if attribute_conditions.any?
50
+ query << "[#{attribute_conditions.join(' and ')}]"
51
+ end
52
+ end
53
+
54
+ def matches?(stringlike, &block)
55
+ @block ||= block
56
+ matched = matches(stringlike)
57
+
58
+ if @options[:count]
59
+ matched.size == @options[:count] && (!@block || @block.call(matched))
60
+ else
61
+ matched.any? && (!@block || @block.call(matched))
62
+ end
63
+ end
64
+
65
+
66
+ end
67
+
68
+ class MatcherProxy
69
+
70
+ def initialize(have_matcher)
71
+ @have_matcher = have_matcher
72
+ @failure_message = "Content should have included #{content_message}, but did not"
73
+ end
74
+
75
+ def failure_message(new_failure_message)
76
+ @failure_message = new_failure_message.gsub(/:tag/, content_message)
77
+ end
78
+
79
+ def content_message
80
+ if @have_matcher.respond_to?(:tag_inspect)
81
+ @have_matcher.tag_inspect
82
+ else
83
+ @have_matcher.content_message
84
+ end
85
+ end
86
+
87
+ def matches?(content)
88
+ @have_matcher.matches?(content)
89
+ end
90
+
91
+ def message
92
+ @failure_message
93
+ end
94
+
95
+ end
96
+
97
+ class MatcherBuilder < BasicObject
98
+
99
+ attr_reader :tags, :contents, :failure_message
100
+
101
+ def initialize(&block)
102
+ @tags = []
103
+ @contents = []
104
+ instance_eval &block
105
+ end
106
+
107
+ def has_content(text)
108
+ @contents << text
109
+ end
110
+
111
+ def method_missing(method, *args, &block)
112
+ tag = {:tag_name => method.to_s}
113
+
114
+ if args.first.is_a?(::String)
115
+ id_or_class = args.first
116
+
117
+ if id_or_class.split('#').size == 2
118
+ id_and_classes = id_or_class.split('#').last
119
+ id_and_classes = id_and_classes.split('.')
120
+ tag[:id] = id_and_classes.shift
121
+ tag[:class] = id_and_classes
122
+ end
123
+
124
+ elsif args.first.is_a?(::Hash)
125
+ tag.merge!(args.first)
126
+ end
127
+
128
+ if args[1] && args[1].is_a?(::Hash)
129
+ if classes = args[1][:class]
130
+ tag[:class] ||= []
131
+ classes.split('.').reject {|s| s.empty? }.each do |s|
132
+ tag[:class] << s
133
+ end
134
+
135
+ tag[:class].uniq!
136
+ end
137
+
138
+ tag.merge!(args[1])
139
+ end
140
+
141
+ if tag[:class]
142
+ tag[:class] = tag[:class].join(' ')
143
+ tag.delete(:class) if tag[:class].strip.empty?
144
+ end
145
+
146
+ tag[:child] = block
147
+
148
+ @tags << tag
149
+
150
+ def tag.failure_message(text)
151
+ self[:failure_message] = text
152
+ end
153
+
154
+ return tag
155
+ end
156
+ end
157
+
158
+ attr_accessor :content, :have_matchers, :errors
159
+
160
+ def initialize(content, &block)
161
+ @content = content
162
+ @errors = []
163
+ @have_matchers = []
164
+ @self_before_instance_eval = eval "self", block.binding
165
+ @matcher_builder = MatcherBuilder.new(&block)
166
+ build_contents
167
+ build_matchers
168
+ run_matchers
169
+ end
170
+
171
+ def contains(matching_text)
172
+ selector = MatcherProxy.new(Webrat::Matchers::HasContent.new(matching_text))
173
+ @have_matchers << selector
174
+ selector
175
+ end
176
+
177
+
178
+ def matches(name, attributes = {}, &block)
179
+ if block_given?
180
+
181
+ have = MyHaveSelector.new(name, attributes) do |n|
182
+ Azebiki::Checker.new(n, &block).success?
183
+ end
184
+
185
+ selector = MatcherProxy.new(have)
186
+ else
187
+ selector = MatcherProxy.new(MyHaveSelector.new(name, attributes))
188
+ end
189
+
190
+ @have_matchers << selector
191
+ selector
192
+ end
193
+
194
+ def success?
195
+ @success
196
+ end
197
+
198
+ private
199
+
200
+ def build_contents
201
+ @matcher_builder.contents.each do |s|
202
+ contains s
203
+ end
204
+ end
205
+
206
+ def method_missing(method, *args, &block)
207
+ @self_before_instance_eval.send method, *args, &block
208
+ end
209
+
210
+ def build_matchers
211
+ @matcher_builder.tags.each do |tag|
212
+ name = tag.delete(:tag_name)
213
+ b = tag.delete(:child)
214
+ attributes = tag
215
+ selector = matches(name, attributes, &b)
216
+ if tag[:failure_message]
217
+ selector.failure_message(tag[:failure_message])
218
+ end
219
+ end
220
+ end
221
+
222
+ def run_matchers
223
+ return true if @have_matchers.empty?
224
+ @have_matchers.each do |selector|
225
+ if !selector.matches?(@content)
226
+ @errors << selector.message
227
+ end
228
+ end
229
+
230
+ @success = @errors.empty?
231
+ end
232
+
233
+ end
234
+ end
@@ -0,0 +1,3 @@
1
+ module Azebiki
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: azebiki
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Eric Allam
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-06-20 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: webrat
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ description: ""
34
+ email:
35
+ - eric@envylabs.com
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files: []
41
+
42
+ files:
43
+ - lib/azebiki/azebiki.rb
44
+ - lib/azebiki/version.rb
45
+ - lib/azebiki.rb
46
+ - LICENSE
47
+ - CHANGELOG.md
48
+ - README.md
49
+ - ROADMAP.md
50
+ has_rdoc: true
51
+ homepage: http://github.com/rubymaverick/azebiki
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options: []
56
+
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ segments:
73
+ - 1
74
+ - 3
75
+ - 6
76
+ version: 1.3.6
77
+ requirements: []
78
+
79
+ rubyforge_project: azebiki
80
+ rubygems_version: 1.3.7
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: A DSL for validating HTML
84
+ test_files: []
85
+