w3c_validators 0.9.0

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/LICENSE ADDED
@@ -0,0 +1,60 @@
1
+ = W3C Validators License
2
+
3
+ W3C Validators is copyrighted free software by Alex Dunae (http://dunae.ca/).
4
+ You can redistribute it and/or modify it under either the terms of the GPL
5
+ or the conditions below:
6
+
7
+ 1. You may make and give away verbatim copies of the source form of the
8
+ software without restriction, provided that you duplicate all of the
9
+ original copyright notices and associated disclaimers.
10
+
11
+ 2. You may modify your copy of the software in any way, provided that
12
+ you do at least ONE of the following:
13
+
14
+ a) place your modifications in the Public Domain or otherwise
15
+ make them Freely Available, such as by posting said
16
+ modifications to Usenet or an equivalent medium, or by allowing
17
+ the author to include your modifications in the software.
18
+
19
+ b) use the modified software only within your corporation or
20
+ organization.
21
+
22
+ c) rename any non-standard executables so the names do not conflict
23
+ with standard executables, which must also be provided.
24
+
25
+ d) make other distribution arrangements with the author.
26
+
27
+ 3. You may distribute the software in object code or executable
28
+ form, provided that you do at least ONE of the following:
29
+
30
+ a) distribute the executables and library files of the software,
31
+ together with instructions (in the manual page or equivalent)
32
+ on where to get the original distribution.
33
+
34
+ b) accompany the distribution with the machine-readable source of
35
+ the software.
36
+
37
+ c) give non-standard executables non-standard names, with
38
+ instructions on where to get the original software distribution.
39
+
40
+ d) make other distribution arrangements with the author.
41
+
42
+ 4. You may modify and include the part of the software into any other
43
+ software (possibly commercial). But some files in the distribution
44
+ are not written by the author, so that they are not under this terms.
45
+
46
+ They are gc.c(partly), utils.c(partly), regex.[ch], st.[ch] and some
47
+ files under the ./missing directory. See each file for the copying
48
+ condition.
49
+
50
+ 5. The scripts and library files supplied as input to or produced as
51
+ output from the software do not automatically fall under the
52
+ copyright of the software, but belong to whomever generated them,
53
+ and may be sold commercially, and may be aggregated with this
54
+ software.
55
+
56
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
57
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
58
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
59
+ PURPOSE.
60
+
data/README ADDED
@@ -0,0 +1,116 @@
1
+ = W3C Validators Gem README
2
+
3
+ W3C Validators is a Ruby wrapper for the World Wide Web Consortium's online
4
+ validation services.
5
+
6
+ It supports the markup validator, the feed validator and the CSS validator.
7
+
8
+ === Installation
9
+
10
+ gem install w3c_validators
11
+
12
+ === Usage
13
+
14
+ There are three main validator classes available, the W3CValidators::MarkupValidator
15
+ (used for HTML), the W3CValidators::FeedValidator and the
16
+ W3CValidators::CSSValidator.
17
+
18
+ Each validator has offers three different validation methods.
19
+
20
+ * +validate_text+ methods take a string
21
+ * +validate_file+ methods take a path to a file
22
+ * +validate_uri+ methods take a published URL
23
+
24
+ In addition, the W3CValidators::MarkupValidator has a validate_uri_quickly method, which
25
+ performs a HEAD request against the markup validation service. The Results
26
+ of this call give an error count but no error details.
27
+
28
+ ==== Using a local validator
29
+
30
+ Each of the three validators allows you to specify a custom path to the
31
+ validator. You can set your own validator like this:
32
+
33
+ validator = MarkupValidator.new(:validator_uri => 'http://localhost/check')
34
+
35
+ === Examples
36
+
37
+ ==== Example #1: Markup validator, local file
38
+
39
+ require 'w3c_validators'
40
+
41
+ include W3CValidators
42
+
43
+ @validator = MarkupValidator.new
44
+
45
+ # override the DOCTYPE
46
+ @validator.set_doctype!(:html32)
47
+
48
+ # turn on debugging messages
49
+ @validator.set_debug!(true)
50
+
51
+ file = File.dirname(__FILE__) + '/fixtures/markup.html'
52
+ results = @validator.validate_file(fp)
53
+
54
+ if results.errors.length > 0
55
+ results.errors.each do |err|
56
+ puts err.to_s
57
+ end
58
+ else
59
+ puts 'Valid!'
60
+ end
61
+
62
+ puts 'Debugging messages'
63
+
64
+ results.debug_messages.each do |key, value|
65
+ puts "#{key}: #{value}"
66
+ end
67
+
68
+
69
+ ==== Example #2: Feed validator, remote file
70
+
71
+ require 'w3c_validators'
72
+
73
+ include W3CValidators
74
+
75
+ @validator = FeedValidator.new
76
+
77
+ results = @validator.validate_uri('http://example.com/feed.xml')
78
+
79
+ if results.errors.length > 0
80
+ results.errors.each do |err|
81
+ puts err.to_s
82
+ end
83
+ else
84
+ puts 'Valid!'
85
+ end
86
+
87
+ ==== Example #3: CSS validator, text fragment
88
+
89
+ require 'w3c_validators'
90
+
91
+ include W3CValidators
92
+
93
+ @validator = CSSValidator.new
94
+
95
+ results = @validator.validate_text('body { margin: 0px; }')
96
+
97
+ if results.errors.length > 0
98
+ results.errors.each do |err|
99
+ puts err.to_s
100
+ end
101
+ else
102
+ puts 'Valid!'
103
+ end
104
+
105
+ === Tests
106
+
107
+ Run unit tests using <tt>rake test</tt>. Note that there is a one second delay
108
+ between each call to the W3C's validators per their request.
109
+
110
+
111
+ === Credits and code
112
+
113
+ Documentation and the source code are available at http://code.dunae.ca/w3c_validators.
114
+
115
+ Written by Alex Dunae (dunae.ca, e-mail 'code' at the same domain), 2007.
116
+
@@ -0,0 +1,4 @@
1
+ require 'lib/w3c_validators/validator'
2
+ require 'lib/w3c_validators/markup_validator'
3
+ require 'lib/w3c_validators/feed_validator'
4
+ require 'lib/w3c_validators/css_validator'
@@ -0,0 +1,79 @@
1
+ module W3CValidators
2
+ CSS_PROFILES = { :css1 => 'CSS 1',
3
+ :css2 => 'CSS 2.0',
4
+ :css21 => 'CSS 1.0',
5
+ :css3 => 'CSS 3.0',
6
+ :svg => 'SVG',
7
+ :svgbasic => 'SVG Basic',
8
+ :svgtiny => 'SVG Tiny',
9
+ :mobile => 'Mobile',
10
+ :atsc_tv => 'ATSC TV',
11
+ :tv => 'TV'
12
+ }
13
+
14
+ DOCTYPES = { :xhtml10_strict => 'XHTML 1.0 Strict',
15
+ :xhtml10_transitional => 'XHTML 1.0 Transitional',
16
+ :xhtml10_frameset => 'XHTML 1.0 Frameset',
17
+ :xhtml401_strict => 'HTML 4.01 Strict',
18
+ :xhtml401_transitional => 'HTML 4.01 Transitional',
19
+ :xhtml401_framest => 'HTML 4.01 Frameset',
20
+ :html32 => 'HTML 3.2',
21
+ :html20 => 'HTML 2.0',
22
+ :iso_html => 'ISO/IEC 15445:2000 (&quot;ISO HTML&quot;)',
23
+ :xhtml11 => 'XHTML 1.1',
24
+ :xhtml_basic10 => 'XHTML Basic 1.0',
25
+ :xhtml_print10 => 'XHTML-Print 1.0',
26
+ :xhtml11_plus_mathml20 => 'XHTML 1.1 plus MathML 2.0',
27
+ :xhtml11_plus_mathml20_plus_svg11 => 'XHTML 1.1 plus MathML 2.0 plus SVG 1.1',
28
+ :mathml20=> 'MathML 2.0',
29
+ :svg10 => 'SVG 1.0',
30
+ :svg11 => 'SVG 1.1',
31
+ :svg11tiny => 'SVG 1.1 Tiny',
32
+ :svg11_basic => 'SVG 1.1 Basic',
33
+ :smil10 => 'SMIL 1.0',
34
+ :smil20 => 'SMIL 2.0'
35
+ }
36
+
37
+ CHARSETS = { :utf_8 => 'utf-8',
38
+ :utf_16 => 'utf-16',
39
+ :iso_8859_1 => 'iso-8859-1',
40
+ :iso_8859_2 => 'iso-8859-2',
41
+ :iso_8859_3 => 'iso-8859-3',
42
+ :iso_8859_4 => 'iso-8859-4',
43
+ :iso_8859_5 => 'iso-8859-5',
44
+ :iso_8859_6i => 'iso-8859-6-i',
45
+ :iso_8859_7 => 'iso-8859-7',
46
+ :iso_8859_8 => 'iso-8859-8',
47
+ :iso_8859_8i => 'iso-8859-8-i',
48
+ :iso_8859_9 => 'iso-8859-9',
49
+ :iso_8859_10 => 'iso-8859-10',
50
+ :iso_8859_11 => 'iso-8859-11',
51
+ :iso_8859_13 => 'iso-8859-13',
52
+ :iso_8859_14 => 'iso-8859-14',
53
+ :iso_8859_15 => 'iso-8859-15',
54
+ :iso_8859_16 => 'iso-8859-16',
55
+ :us_ascci => 'us-ascii',
56
+ :euc_jp => 'euc-jp',
57
+ :shift_jis => 'shift_jis',
58
+ :iso_2022_hp => 'iso-2022-jp',
59
+ :euc_jr => 'euc-kr',
60
+ :ksk_5601 => 'ksc_5601',
61
+ :gb_2312 => 'gb2312',
62
+ :gb_18030 => 'gb18030',
63
+ :big5 => 'big5',
64
+ :big5_hkscs => 'big5-HKSCS',
65
+ :tis_620 => 'tis-620',
66
+ :koi8_r => 'koi8-r',
67
+ :koi8_u => 'koi8-u',
68
+ :iso_ir_111 => 'iso-ir-111',
69
+ :macintosh => 'macintosh',
70
+ :windows_1250 => 'windows-1250',
71
+ :windows_1251 => 'windows-1251',
72
+ :windows_1252 => 'windows-1252',
73
+ :windows_1253 => 'windows-1253',
74
+ :windows_1254 => 'windows-1254',
75
+ :windows_1255 => 'windows-1255',
76
+ :windows_1256 => 'windows-1256',
77
+ :windows_1257 => 'windows-1257'
78
+ }
79
+ end
@@ -0,0 +1,146 @@
1
+ module W3CValidators
2
+ class CSSValidator < Validator
3
+ CSS_VALIDATOR_URI = 'http://jigsaw.w3.org/css-validator/validator'
4
+
5
+ # Create a new instance of the CSSValidator.
6
+ #
7
+ # ==== Options
8
+ # You can pass in your own validator's URI (i.e.
9
+ # <tt>CSSValidator.new(:validator_uri => 'http://localhost/check')</tt>).
10
+ def initialize(options = {})
11
+ if options[:validator_uri]
12
+ @validator_uri = URI.parse(options[:validator_uri])
13
+ options.delete(options[:validator_uri])
14
+ else
15
+ @validator_uri = URI.parse(CSS_VALIDATOR_URI)
16
+ end
17
+ super(options)
18
+ end
19
+
20
+ # The CSS profile used for the validation.
21
+ #
22
+ # +charset+ can be a string or a symbl from the W3CValidators::CSS_PROFILES hash.
23
+ #
24
+ # ==== Example
25
+ # set_profile!('css1')
26
+ # set_profile!(:css1)
27
+ def set_profile!(profile)
28
+ if profile.kind_of?(Symbol)
29
+ if CSS_PROFILES.has_key?(profile)
30
+ profile = profile.to_s
31
+ else
32
+ return
33
+ end
34
+ end
35
+ @options[:profile] = profile
36
+ end
37
+
38
+ # The warning level, no for no warnings, 0 for less warnings, 1or 2 for more warnings
39
+ def set_warn_level!(level = 2)
40
+ warn_levels = ['0','1','2','no']
41
+ return unless warn_levels.include?(level.to_s.downcase)
42
+
43
+ @options[:warning] = level
44
+ end
45
+
46
+ # The language used for the response.
47
+ def set_language!(lang = 'en')
48
+ @options[:lang] = lang
49
+ end
50
+
51
+ # Validate the CSS of an URI.
52
+ #
53
+ # Returns W3CValidators::Results.
54
+ def validate_uri(uri)
55
+ return validate({:uri => uri})
56
+ end
57
+
58
+ # Validate the CSS of a string.
59
+ #
60
+ # Returns W3CValidators::Results.
61
+ def validate_text(text)
62
+ return validate({:text => text})
63
+ end
64
+
65
+ # Validate the CSS of a local file.
66
+ #
67
+ # +file_path+ must be the fully-expanded path to the file.
68
+ #
69
+ # Returns W3CValidators::Results.
70
+ def validate_file(file_path)
71
+ src = read_local_file(file_path)
72
+ return validate_text(src)
73
+ end
74
+
75
+
76
+ protected
77
+ def validate(options) # :nodoc:
78
+ options = get_request_options(options)
79
+ #puts options.inspect
80
+ response = send_request(options, :get)
81
+ @results = parse_soap_response(response.body)
82
+ #puts response.body
83
+ @results
84
+ end
85
+
86
+ # Perform sanity checks on request params
87
+ def get_request_options(options) # :nodoc:
88
+ options = @options.merge(options)
89
+
90
+ options[:output] = SOAP_OUTPUT_PARAM
91
+
92
+ unless options[:uri] or options[:text]
93
+ raise ArgumentError, "an uri or text is required."
94
+ end
95
+
96
+ # URI should be a string. If it is a URI object, .to_s will
97
+ # be seamless; if it is not an exception will be raised.
98
+ if options[:uri] and not options[:uri].kind_of?(String)
99
+ options[:uri] = options[:uri].to_s
100
+ end
101
+
102
+ options
103
+ end
104
+
105
+
106
+ def parse_soap_response(response) # :nodoc:
107
+ doc = REXML::Document.new(response)
108
+
109
+ result_params = {}
110
+
111
+ {:uri => 'uri', :checked_by => 'checkedby', :validity => 'validity', :css_level => 'csslevel'}.each do |local_key, remote_key|
112
+ if val = doc.elements["//*[local-name()='cssvalidationresponse']/*[local-name()='#{remote_key.to_s}']"]
113
+ result_params[local_key] = val.text
114
+ end
115
+ end
116
+
117
+ results = Results.new(result_params)
118
+
119
+ ['warninglist', 'errorlist'].each do |list_type|
120
+ doc.elements.each("//*[local-name()='#{list_type.to_s}']") do |message_list|
121
+
122
+ if uri_node = message_list.elements["*[local-name()='uri']"]
123
+ uri = uri_node.text
124
+ end
125
+
126
+ [:warning, :error].each do |msg_type|
127
+ message_list.elements.each("*[local-name()='#{msg_type.to_s}']") do |message|
128
+ message_params = {}
129
+ message.each_element_with_text do |el|
130
+ message_params[el.name.to_sym] = el.text
131
+ end
132
+ message_params[:uri] = uri
133
+ results.add_message(msg_type, message_params)
134
+ end
135
+ end
136
+ end
137
+ end
138
+ return results
139
+
140
+ rescue Exception => e
141
+ handle_exception e
142
+ end
143
+
144
+
145
+ end
146
+ end
@@ -0,0 +1,4 @@
1
+ module W3CValidators
2
+ class ValidatorUnavailable < StandardError; end
3
+ class ParsingError < StandardError; end
4
+ end
@@ -0,0 +1,105 @@
1
+ module W3CValidators
2
+ class FeedValidator < Validator
3
+ FEED_VALIDATOR_URI = 'http://validator.w3.org/feed/check.cgi'
4
+
5
+ # Create a new instance of the FeedValidator.
6
+ #
7
+ # ==== Options
8
+ # You can pass in your own validator's URI (i.e.
9
+ # <tt>FeedValidator.new(:validator_uri => 'http://localhost/check')</tt>).
10
+ def initialize(options = {})
11
+ if options[:validator_uri]
12
+ @validator_uri = URI.parse(options[:validator_uri])
13
+ options.delete(options[:validator_uri])
14
+ else
15
+ @validator_uri = URI.parse(FEED_VALIDATOR_URI)
16
+ end
17
+ super(options)
18
+ end
19
+
20
+ # Validate a feed URI using a +SOAP+ request.
21
+ #
22
+ # Returns W3CValidators::Results.
23
+ def validate_uri(url)
24
+ return validate({:url => url})
25
+ end
26
+
27
+ # Validate a feed from a string.
28
+ #
29
+ # Returns W3CValidators::Results.
30
+ def validate_text(text)
31
+ return validate({:rawdata => text})
32
+ end
33
+
34
+ # Validate a local feed file.
35
+ #
36
+ # +file_path+ must be the fully-expanded path to the file.
37
+ #
38
+ # Returns W3CValidators::Results.
39
+ def validate_file(file_path)
40
+ src = read_local_file(file_path)
41
+ return validate_text(src)
42
+ end
43
+
44
+ protected
45
+ def validate(options) # :nodoc:
46
+ options = get_request_options(options)
47
+ response = send_request(options, :get)
48
+ @results = parse_soap_response(response.body)
49
+ @results
50
+ end
51
+
52
+ # Perform sanity checks on request params
53
+ def get_request_options(options) # :nodoc:
54
+ options = @options.merge(options)
55
+
56
+ options[:output] = SOAP_OUTPUT_PARAM
57
+
58
+ unless options[:url] or options[:rawdata]
59
+ raise ArgumentError, "an url or rawdata is required."
60
+ end
61
+
62
+ # URL should be a string. If it is a URL object, .to_s will
63
+ # be seamless; if it is not an exception will be raised.
64
+ if options[:url] and not options[:url].kind_of?(String)
65
+ options[:url] = options[:url].to_s
66
+ end
67
+
68
+ options
69
+ end
70
+
71
+ # Parse the SOAP XML response.
72
+ #
73
+ # +response+ must be a Net::HTTPResponse.
74
+ #
75
+ # Returns W3CValidators::Results.
76
+ def parse_soap_response(response) # :nodoc:
77
+ doc = REXML::Document.new(response)
78
+
79
+ result_params = {}
80
+
81
+ {:uri => 'uri', :checked_by => 'checkedby', :validity => 'validity'}.each do |local_key, remote_key|
82
+ if val = doc.elements["//*[local-name()='feedvalidationresponse']/*[local-name()='#{remote_key.to_s}']"]
83
+ result_params[local_key] = val.text
84
+ end
85
+ end
86
+
87
+ results = Results.new(result_params)
88
+
89
+ [:warning, :error].each do |msg_type|
90
+ doc.elements.each("//*[local-name()='#{msg_type.to_s}']") do |message|
91
+ message_params = {}
92
+ message.each_element_with_text do |el|
93
+ message_params[el.name.to_sym] = el.text
94
+ end
95
+ results.add_message(msg_type, message_params)
96
+ end
97
+ end
98
+
99
+ return results
100
+
101
+ rescue Exception => e
102
+ handle_exception e
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,215 @@
1
+ module W3CValidators
2
+ class MarkupValidator < Validator
3
+ MARKUP_VALIDATOR_URI = 'http://validator.w3.org/check'
4
+
5
+ # Create a new instance of the MarkupValidator.
6
+ #
7
+ # ==== Options
8
+ # The +options+ hash allows you to set request parameters (see
9
+ # http://validator.w3.org/docs/api.html#requestformat) quickly. Request
10
+ # parameters can also be set using set_charset!, set_debug! and set_doctype!.
11
+ #
12
+ # You can pass in your own validator's URI (i.e.
13
+ # <tt>MarkupValidator.new(:validator_uri => 'http://localhost/check')</tt>).
14
+ def initialize(options = {})
15
+ if options[:validator_uri]
16
+ @validator_uri = URI.parse(options[:validator_uri])
17
+ options.delete(options[:validator_uri])
18
+ else
19
+ @validator_uri = URI.parse(MARKUP_VALIDATOR_URI)
20
+ end
21
+ super(options)
22
+ end
23
+
24
+ # Specify the character encoding to use when parsing the document.
25
+ #
26
+ # When +only_as_fallback+ is +true+, the given encoding will only be
27
+ # used as a fallback value, in case the +charset+ is absent or unrecognized.
28
+ #
29
+ # +charset+ can be a string (e.g. <tt>set_charset!('utf-8')</tt>) or
30
+ # a symbol (e.g. <tt>set_charset!(:utf_8)</tt>) from the
31
+ # W3CValidators::CHARSETS hash.
32
+ #
33
+ # Has no effect when using validate_uri_quickly.
34
+ def set_charset!(charset, only_as_fallback = false)
35
+ if charset.kind_of?(Symbol)
36
+ if CHARSETS.has_key?(charset)
37
+ charset = CHARSETS[charset]
38
+ else
39
+ return
40
+ end
41
+ end
42
+ @options[:charset] = charset
43
+ @options[:fbc] = only_as_fallback
44
+ end
45
+
46
+ # Specify the Document Type (+DOCTYPE+) to use when parsing the document.
47
+ #
48
+ # When +only_as_fallback+ is +true+, the given document type will only be
49
+ # used as a fallback value, in case the document's +DOCTYPE+ declaration
50
+ # is missing or unrecognized.
51
+ #
52
+ # +doctype+ can be a string (e.g. <tt>set_doctype!('HTML 3.2')</tt>) or
53
+ # a symbol (e.g. <tt>set_doctype!(:html32)</tt>) from the
54
+ # W3CValidators::DOCTYPES hash.
55
+ #
56
+ # Has no effect when using validate_uri_quickly.
57
+ def set_doctype!(doctype, only_as_fallback = false)
58
+ if doctype.kind_of?(Symbol)
59
+ if DOCTYPES.has_key?(doctype)
60
+ doctype = DOCTYPES[doctype]
61
+ else
62
+ return
63
+ end
64
+ end
65
+ @options[:doctype] = doctype
66
+ @options[:fbd] = only_as_fallback
67
+ end
68
+
69
+ # When set the validator will output some extra debugging information on
70
+ # the validated resource (such as HTTP headers) and validation process
71
+ # (such as parser used, parse mode, etc.).
72
+ #
73
+ # Debugging information is stored in the Results +debug_messages+ hash.
74
+ # Custom debugging messages can be set with Results#add_debug_message.
75
+ #
76
+ # Has no effect when using validate_uri_quickly.
77
+ def set_debug!(debug = true)
78
+ @options[:debug] = debug
79
+ end
80
+
81
+ # Validate the markup of an URI using a +SOAP+ request.
82
+ #
83
+ # Returns W3CValidators::Results.
84
+ def validate_uri(uri)
85
+ return validate({:uri => uri}, false)
86
+ end
87
+
88
+ # Validate the markup of an URI using a +HEAD+ request.
89
+ #
90
+ # Returns W3CValidators::Results with an error count, not full error messages.
91
+ def validate_uri_quickly(uri)
92
+ return validate({:uri => uri}, true)
93
+ end
94
+
95
+ # Validate the markup of a string.
96
+ #
97
+ # Returns W3CValidators::Results.
98
+ def validate_text(text)
99
+ return validate({:fragment => text}, false)
100
+ end
101
+
102
+ # Validate the markup of a local file.
103
+ #
104
+ # +file_path+ must be the fully-expanded path to the file.
105
+ #
106
+ # Returns W3CValidators::Results.
107
+ def validate_file(file_path)
108
+ src = read_local_file(file_path)
109
+ return validate({:uploaded_file => src}, false)
110
+ end
111
+
112
+ protected
113
+ def validate(options, quick = false) # :nodoc:
114
+ options = get_request_options(options)
115
+
116
+ if quick
117
+ response = send_request(options, :head)
118
+ @results = parse_head_response(response, options[:uri])
119
+ else
120
+ if options.has_key?(:uri) or options.has_key?(:fragment)
121
+ response = send_request(options, :get)
122
+ else
123
+ response = send_request(options, :post)
124
+ end
125
+
126
+ @results = parse_soap_response(response.body)
127
+ end
128
+ @results
129
+ end
130
+
131
+ # Perform sanity checks on request params
132
+ def get_request_options(options) # :nodoc:
133
+ options = @options.merge(options)
134
+
135
+ options[:output] = SOAP_OUTPUT_PARAM
136
+
137
+ unless options[:uri] or options[:uploaded_file] or options[:fragment]
138
+ raise ArgumentError, "an uri, uploaded file or fragment is required."
139
+ end
140
+
141
+ # URI should be a string. If it is a URI object, .to_s will
142
+ # be seamless; if it is not an exception will be raised.
143
+ if options[:uri] and not options[:uri].kind_of?(String)
144
+ options[:uri] = options[:uri].to_s
145
+ end
146
+
147
+ # Convert booleans to integers
148
+ [:fbc, :fbd, :verbose, :debug, :ss, :outline].each do |k|
149
+ if options.has_key?(k) and not options[k].kind_of?(Fixnum)
150
+ options[k] = options[k] ? 1 : 0
151
+ end
152
+ end
153
+
154
+ options
155
+ end
156
+
157
+
158
+ # Parse the SOAP XML response.
159
+ #
160
+ # +response+ must be a Net::HTTPResponse.
161
+ #
162
+ # Returns W3CValidators::Results.
163
+ def parse_soap_response(response) # :nodoc:
164
+ doc = REXML::Document.new(response)
165
+
166
+ result_params = {}
167
+
168
+ {:doctype => 'm:doctype', :uri => 'm:uri', :charset => 'm:charset',
169
+ :checked_by => 'm:checkedby', :validity => 'm:validity'}.each do |local_key, remote_key|
170
+ if val = doc.elements["env:Envelope/env:Body/m:markupvalidationresponse/#{remote_key}"]
171
+ result_params[local_key] = val.text
172
+ end
173
+ end
174
+
175
+ results = Results.new(result_params)
176
+
177
+ {:warning => 'm:warnings/m:warninglist/m:warning', :error => 'm:errors/m:errorlist/m:error'}.each do |local_type, remote_type|
178
+ doc.elements.each("env:Envelope/env:Body/m:markupvalidationresponse/#{remote_type}") do |message|
179
+ message_params = {}
180
+ message.each_element_with_text do |el|
181
+ message_params[el.name.to_sym] = el.text
182
+ end
183
+ results.add_message(local_type, message_params)
184
+ end
185
+ end
186
+
187
+ doc.elements.each("env:Envelope/env:Body/m:markupvalidationresponse/m:debug") do |debug|
188
+ results.add_debug_message(debug.attribute('name').value, debug.text)
189
+ end
190
+ return results
191
+
192
+ rescue Exception => e
193
+ handle_exception e
194
+ end
195
+
196
+ # Parse the HEAD response into HTMLValidator::Results.
197
+ #
198
+ # +response+ must be a Net::HTTPResponse.
199
+ #
200
+ # Returns Results.
201
+ def parse_head_response(response, validated_uri = nil) # :nodoc:
202
+ validity = (response[HEAD_STATUS_HEADER].downcase == 'valid')
203
+
204
+ results = Results.new(:uri => validated_uri, :validity => validity)
205
+
206
+ # Fill the results with empty error messages so we can count them
207
+ errors = response[HEAD_ERROR_COUNT_HEADER].to_i
208
+ errors.times { results.add_error }
209
+
210
+ results
211
+ end
212
+
213
+
214
+ end
215
+ end
@@ -0,0 +1,82 @@
1
+ module W3CValidators
2
+ class Message
3
+ attr_accessor :type, :line, :col, :source, :explanation, :message, :message_id
4
+ attr_accessor :message_count, :element, :parent, :value
5
+
6
+ MESSAGE_TYPES = [:warning, :error]
7
+
8
+ # Due to the responses received from the W3C's validators, different data
9
+ # are available for different validation types.
10
+ #
11
+ # ==== Feed validation
12
+ # * +line+
13
+ # * +col+
14
+ # * +message+ (originally +text+)
15
+ # * +message_count+
16
+ # * +element+
17
+ # * +parent+
18
+ # * +value+
19
+ # See http://validator.w3.org/feed/docs/soap.html#soap12message for full explanations.
20
+ #
21
+ # ==== Markup validation
22
+ # * +line+
23
+ # * +col+
24
+ # * +message+
25
+ # * +message_id+
26
+ # * +explanation+
27
+ # * +source+
28
+ # See http://validator.w3.org/docs/api.html#soap12message for full explanations.
29
+ #
30
+ # ==== CSS validation (http://jigsaw.w3.org/css-validator/api.html#soap12message)
31
+ # * +level+
32
+ # * +line+
33
+ # * +message+
34
+ # See http://jigsaw.w3.org/css-validator/api.html#soap12message for full explanations.
35
+ def initialize(uri, message_type, options = {})
36
+ @type = message_type
37
+ @uri = uri
38
+
39
+ # All validators
40
+ @line = options[:line]
41
+ @col = options[:col]
42
+
43
+ # MarkupValidator
44
+ @source = options[:source]
45
+ @explanation = options[:explanation]
46
+ @message = options[:message]
47
+ @message_id = options[:messageid]
48
+
49
+ # FeedValidator
50
+ @message = options[:text] unless @message
51
+ @message_count = options[:message_count]
52
+ @element = options[:element]
53
+ @parent = options[:parent]
54
+ @value = options[:value]
55
+
56
+ # CSSValidator
57
+ @level = options[:level]
58
+ end
59
+
60
+ def is_warning?
61
+ @type == :warning
62
+ end
63
+
64
+ def is_error?
65
+ @type == :error
66
+ end
67
+
68
+ # Return the message as a string.
69
+ def to_s
70
+ str = @type.to_s.upcase
71
+ if @uri and not @uri.empty?
72
+ str << "; URI: #{@uri}"
73
+ end
74
+ str << "; line #{@line}"
75
+ if @message and not @message.empty?
76
+ str << ": #{@message}"
77
+ end
78
+ return str
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,58 @@
1
+ module W3CValidators
2
+ class Results
3
+ attr_reader :uri, :checked_by, :doctype, :css_level, :charset, :validity, :debug_messages
4
+
5
+ def initialize(options = {})
6
+ @messages = []
7
+ @uri = options[:uri]
8
+ @checked_by = options[:checked_by]
9
+ @doctype = options[:doctype]
10
+ @css_level = options[:css_level]
11
+ @charset = options[:charset]
12
+ @validity = options[:validity]
13
+ @debug_messages = {}
14
+ end
15
+
16
+ def add_message(type, params = {})
17
+ uri = params[:uri] ||= @uri
18
+ @messages << Message.new(uri, type, params)
19
+ end
20
+
21
+ def add_error(params = {})
22
+ add_message(:error, params)
23
+ end
24
+
25
+ def add_warning(params = {})
26
+ add_message(:warnings, params)
27
+ end
28
+
29
+ def add_debug_message(key, val)
30
+ @debug_messages[key] = val
31
+ end
32
+
33
+ # Returns either the +DOCTYPE+ or CSS level, whichever is present.
34
+ def checked_against
35
+ return @doctype if @doctype
36
+ return @css_level if @css_level
37
+ nil
38
+ end
39
+
40
+ def is_valid?
41
+ @validity.downcase.strip == 'true'
42
+ end
43
+
44
+ # Returns an array of Message objects.
45
+ def errors
46
+ errors = []
47
+ @messages.each { |msg| errors << msg if msg.is_error? }
48
+ errors
49
+ end
50
+
51
+ # Returns an array of Message objects.
52
+ def warnings
53
+ errors = []
54
+ @messages.each { |msg| errors << msg if msg.is_warning? }
55
+ errors
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,144 @@
1
+ require 'cgi'
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'rexml/document'
5
+
6
+ require 'lib/w3c_validators/exceptions'
7
+ require 'lib/w3c_validators/constants'
8
+ require 'lib/w3c_validators/results'
9
+ require 'lib/w3c_validators/message'
10
+
11
+ module W3CValidators
12
+ # Base class for MarkupValidator and FeedValidator.
13
+ class Validator
14
+ USER_AGENT = 'Ruby W3C Validators/0.9 (http://code.dunae.ca/w3c_validators/)'
15
+ VERSION = '0.9'
16
+ HEAD_STATUS_HEADER = 'X-W3C-Validator-Status'
17
+ HEAD_ERROR_COUNT_HEADER = 'X-W3C-Validator-Errors'
18
+ SOAP_OUTPUT_PARAM = 'soap12'
19
+
20
+ attr_reader :results, :validator_uri
21
+
22
+ # Create a new instance of the Validator.
23
+ def initialize(options = {})
24
+ @options = options
25
+ end
26
+
27
+ protected
28
+ # Perform a validation request.
29
+ #
30
+ # +request_mode+ must be either <tt>:get</tt>, <tt>:head</tt> or <tt>:post</tt>.
31
+ #
32
+ # Returns Net::HTTPResponse.
33
+ def send_request(options, request_mode = :get)
34
+ response = nil
35
+ results = nil
36
+
37
+ Net::HTTP.start(@validator_uri.host, @validator_uri.port) do |http|
38
+ case request_mode
39
+ when :head
40
+ # perform a HEAD request
41
+ raise ArgumentError, "a URI must be provided for HEAD requests." unless options[:uri]
42
+ query = create_query_string_data(options)
43
+ response = http.request_head(@validator_uri.path + '?' + query)
44
+ when :get
45
+ # send a GET request
46
+ query = create_query_string_data(options)
47
+ response = http.get(@validator_uri.path + '?' + query)
48
+ when :post
49
+ # send a multipart form request
50
+ query, boundary = create_multipart_data(options)
51
+ response = http.post2(@validator_uri.path, query, "Content-type" => "multipart/form-data; boundary=" + boundary)
52
+ else
53
+ raise ArgumentError, "request_mode must be either :get, :head or :post"
54
+ end
55
+ end
56
+
57
+ response.value
58
+ return response
59
+
60
+ rescue Exception => e
61
+ handle_exception e
62
+ end
63
+
64
+ def create_multipart_data(options) # :nodoc:
65
+ boundary = '349832898984244898448024464570528145'
66
+ params = []
67
+ if options[:uploaded_file]
68
+ filename = options[:file_path] ||= 'temp.html'
69
+ content = options[:uploaded_file]
70
+ params << "Content-Disposition: form-data; name=\"uploaded_file\"; filename=\"#{filename}\"\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "#{content}\r\n"
71
+ options.delete(:uploaded_file)
72
+ options.delete(:file_path)
73
+ end
74
+
75
+ options.each do |key, value|
76
+ if value
77
+ params << "Content-Disposition: form-data; name=\"#{CGI::escape(key.to_s)}\"\r\n" + "\r\n" + "#{value}\r\n"
78
+ end
79
+ end
80
+
81
+ multipart_query = params.collect {|p| '--' + boundary + "\r\n" + p}.join('') + "--" + boundary + "--\r\n"
82
+
83
+ [multipart_query, boundary]
84
+ end
85
+
86
+ def create_query_string_data(options) # :nodoc:
87
+ qs = ''
88
+ options.each do |key, value|
89
+ if value
90
+ qs += "#{key}=" + CGI::escape(value.to_s) + "&"
91
+ end
92
+ end
93
+ qs
94
+ end
95
+
96
+ def read_local_file(file_path) # :nodoc:
97
+ fh = File.new(file_path, 'r+')
98
+ src = fh.read
99
+ fh.close
100
+ src
101
+ end
102
+
103
+ private
104
+ #--
105
+ # Big thanks to ara.t.howard and Joel VanderWerf on Ruby-Talk for the exception handling help.
106
+ #++
107
+ def handle_exception(e, msg = '') # :nodoc:
108
+ case e
109
+ when Net::HTTPServerException, SocketError
110
+ msg = "unable to connect to the validator at #{@validator_uri} (response was #{e.message})."
111
+ raise ValidatorUnavailable, msg, caller
112
+ when REXML::ParseException
113
+ msg = "unable to parse the response from the validator."
114
+ raise ParsingError, msg, caller
115
+ else
116
+ raise e
117
+ end
118
+
119
+ if e.respond_to?(:error_handler_before)
120
+ fcall(e, :error_handler_before, self)
121
+ end
122
+
123
+ if e.respond_to?(:error_handler_instead)
124
+ fcall(e, :error_handler_instead, self)
125
+ else
126
+ if e.respond_to? :status
127
+ exit_status(( e.status ))
128
+ end
129
+
130
+ if SystemExit === e
131
+ stderr.puts e.message unless(SystemExit === e and e.message.to_s == 'exit') ### avoids double message for abort('message')
132
+ end
133
+ end
134
+
135
+ if e.respond_to?(:error_handler_after)
136
+ fcall(e, :error_handler_after, self)
137
+ end
138
+
139
+ exit_status(( exit_failure )) if exit_status == exit_success
140
+ exit_status(( Integer(exit_status) rescue(exit_status ? 0 : 1) ))
141
+ exit exit_status
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ # Test cases for the CSSValidator.
4
+ class CSSValidatorTests < Test::Unit::TestCase
5
+ include W3CValidators
6
+ def setup
7
+ @v = CSSValidator.new
8
+
9
+ @invalid_fragment = <<-EOT
10
+ a { color: white; }
11
+ body { margin: blue; }
12
+
13
+ EOT
14
+
15
+ sleep 1
16
+ end
17
+
18
+ def test_overriding_css_profile
19
+ @v.set_profile!(:svgbasic)
20
+ r = @v.validate_text(@invalid_fragment)
21
+ assert_equal 'svgbasic', r.css_level
22
+ end
23
+
24
+ def test_validating_file
25
+ file_path = File.expand_path(File.dirname(__FILE__) + '/fixtures/invalid_css.css')
26
+ r = @v.validate_file(file_path)
27
+ assert_equal 1, r.errors.length
28
+ end
29
+
30
+ def test_validating_uri
31
+ @v.set_profile!(:svgbasic)
32
+ r = @v.validate_text(@invalid_fragment)
33
+ assert_equal 1, r.errors.length
34
+ end
35
+
36
+ def test_validating_text
37
+ r = @v.validate_text(@invalid_fragment)
38
+ assert_equal 1, r.errors.length
39
+ end
40
+
41
+
42
+ end
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class ExceptionTests < Test::Unit::TestCase
4
+ include W3CValidators
5
+
6
+ def setup
7
+ @valid_fragment = <<-EOV
8
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
9
+ <title>Test</title>
10
+ <body>
11
+ <div class="example">This is a test</div>
12
+ </body>
13
+ EOV
14
+ end
15
+
16
+ def test_bad_validator_uri
17
+ ['http://noexist/', 'http://noexist.badtld/',
18
+ 'http://example.com/noexist'].each do |uri|
19
+ v = MarkupValidator.new(:validator_uri => uri)
20
+ assert_raise ValidatorUnavailable do
21
+ r = v.validate_text(@valid_fragment)
22
+ end
23
+ end
24
+ end
25
+
26
+ def test_bad_soap_response
27
+ return # need to set up a test host
28
+ [].each do |uri|
29
+ v = MarkupValidator.new(:validator_uri => uri)
30
+ assert_raise ParsingError do
31
+ r = v.validate_text(@valid_fragment)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,51 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ # Test cases for the FeedValidator.
4
+ class FeedValidatorTests < Test::Unit::TestCase
5
+ include W3CValidators
6
+ def setup
7
+ @v = FeedValidator.new
8
+ sleep 1
9
+ end
10
+
11
+ def test_validating_uri_with_soap
12
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_feed.xml')
13
+ assert_equal 1, r.errors.length
14
+ assert_equal 1, r.warnings.length
15
+ end
16
+
17
+ def test_validating_file
18
+ file_path = File.expand_path(File.dirname(__FILE__) + '/fixtures/invalid_feed.xml')
19
+ r = @v.validate_file(file_path)
20
+ assert_equal 1, r.errors.length
21
+ end
22
+
23
+ def test_validating_text
24
+ fragment = <<-EOT
25
+ <?xml version="1.0" encoding="utf-8"?>
26
+ <feed xmlns="http://www.w3.org/2005/Atom">
27
+ <title>Example Feed</title>
28
+ <subtitle>A subtitle.</subtitle>
29
+ <link href="http://example.org/feed/" rel="self"/>
30
+ <link href="http://example.org/"/>
31
+ <updated>2003-12-13T18:30:02Z</updated>
32
+ <author>
33
+ <email>johndoe@example.com</email>
34
+ </author>
35
+ <id>urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6</id>
36
+ <entry>
37
+ <title>Atom-Powered Robots Run Amok</title>
38
+ <link href="http://example.org/2003/12/13/atom03"/>
39
+ <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
40
+ <updated>2003-12-13T18:30:02Z</updated>
41
+ <summary>Some text.</summary>
42
+ </entry>
43
+ </feed>
44
+ EOT
45
+
46
+ r = @v.validate_text(fragment)
47
+ assert_equal 1, r.errors.length
48
+ end
49
+
50
+
51
+ end
@@ -0,0 +1,6 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__), '../'))
2
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__), '../lib/'))
3
+
4
+ require 'rubygems'
5
+ require 'test/unit'
6
+ require 'w3c_validators'
@@ -0,0 +1,72 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ # Test cases for the MarkupValidator.
4
+ class MarkupValidatorTests < Test::Unit::TestCase
5
+ include W3CValidators
6
+ def setup
7
+ @v = MarkupValidator.new
8
+ sleep 1
9
+ end
10
+
11
+ def test_overriding_doctype
12
+ @v.set_doctype!(:html32, false)
13
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
14
+ assert_equal '-//W3C//DTD HTML 3.2 Final//EN', r.doctype
15
+ end
16
+
17
+ def test_overriding_doctype_for_fallback_only
18
+ @v.set_doctype!(:html32, true)
19
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
20
+ assert_not_equal '-//W3C//DTD HTML 3.2 Final//EN', r.doctype
21
+ end
22
+
23
+ def test_overriding_charset
24
+ @v.set_charset!(:utf_16, false)
25
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
26
+ assert_equal 'utf-16', r.charset
27
+ end
28
+
29
+ def test_overriding_charset_for_fallback_only
30
+ @v.set_doctype!(:utf_16, true)
31
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
32
+ assert_not_equal 'utf-16', r.charset
33
+ end
34
+
35
+ def test_validating_uri_with_head_request
36
+ r = @v.validate_uri_quickly('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
37
+ assert_equal 1, r.errors.length
38
+ assert_equal 0, r.warnings.length
39
+ end
40
+
41
+ def test_validating_uri_with_soap
42
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
43
+ assert_equal 1, r.errors.length
44
+ assert_equal 0, r.warnings.length
45
+ end
46
+
47
+ def test_debugging_uri
48
+ @v.set_debug!
49
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
50
+ assert r.debug_messages.length > 0
51
+ end
52
+
53
+ def test_validating_file
54
+ file = File.dirname(__FILE__) + '/fixtures/invalid_markup.html'
55
+ r = @v.validate_file(file)
56
+ assert_equal 1, r.errors.length
57
+ end
58
+
59
+ def test_validating_text
60
+ valid_fragment = <<-EOV
61
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
62
+ <title>Test</title>
63
+ <body>
64
+ <div class="example">This is a test</div>
65
+ </body>
66
+ EOV
67
+
68
+ r = @v.validate_text(valid_fragment)
69
+ assert_equal 0, r.errors.length
70
+ assert_equal 0, r.warnings.length
71
+ end
72
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.4
3
+ specification_version: 1
4
+ name: w3c_validators
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.9.0
7
+ date: 2007-11-10 00:00:00 -08:00
8
+ summary: Wrapper for the World Wide Web Consortium's online validation services.
9
+ require_paths:
10
+ - lib
11
+ email:
12
+ homepage: http://code.dunae.ca/w3c_validators
13
+ rubyforge_project:
14
+ description: W3C Validators is a Ruby wrapper for the World Wide Web Consortium's online validation services.
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Alex Dunae
31
+ files:
32
+ - lib/w3c_validators
33
+ - lib/w3c_validators/constants.rb
34
+ - lib/w3c_validators/css_validator.rb
35
+ - lib/w3c_validators/exceptions.rb
36
+ - lib/w3c_validators/feed_validator.rb
37
+ - lib/w3c_validators/markup_validator.rb
38
+ - lib/w3c_validators/message.rb
39
+ - lib/w3c_validators/results.rb
40
+ - lib/w3c_validators/validator.rb
41
+ - lib/w3c_validators.rb
42
+ - README
43
+ - LICENSE
44
+ test_files:
45
+ - test/test_css_validator.rb
46
+ - test/test_exceptions.rb
47
+ - test/test_feed_validator.rb
48
+ - test/test_helper.rb
49
+ - test/test_markup_validator.rb
50
+ rdoc_options:
51
+ - --all
52
+ - --inline-source
53
+ - --line-numbers
54
+ extra_rdoc_files:
55
+ - README
56
+ - LICENSE
57
+ executables: []
58
+
59
+ extensions: []
60
+
61
+ requirements: []
62
+
63
+ dependencies: []
64
+