w3c_validators 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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
+