dominikh-tidy_ffi 0.1.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.
- data/CHANGELOG +7 -0
- data/LICENSE +21 -0
- data/Manifest +16 -0
- data/README.rdoc +31 -0
- data/Rakefile +24 -0
- data/lib/tidy_ffi.rb +11 -0
- data/lib/tidy_ffi/interface.rb +186 -0
- data/lib/tidy_ffi/lib_tidy.rb +67 -0
- data/lib/tidy_ffi/options_container.rb +90 -0
- data/lib/tidy_ffi/tidy.rb +83 -0
- data/lib/tidy_ffi/tidy_ffi_extensions.rb +26 -0
- data/test/test_helper.rb +13 -0
- data/test/test_lowlevel.rb +86 -0
- data/test/test_options.rb +133 -0
- data/test/test_simple.rb +52 -0
- data/tidy_ffi.gemspec +34 -0
- metadata +93 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
v0.1.1. Fixes for new version of FFI gem
|
2
|
+
v0.1.0. Clean class method.
|
3
|
+
v0.0.4. Options validation
|
4
|
+
v0.0.3. Add method “errors” to return errors after cleanup
|
5
|
+
v0.0.2. Do not require matchy, rr and context as development dependencies. A user does not need them.
|
6
|
+
v0.0.1. Options support.
|
7
|
+
v0.0.0. Initial release.
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2009 Eugene Pimenov
|
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.
|
21
|
+
|
data/Manifest
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
CHANGELOG
|
2
|
+
LICENSE
|
3
|
+
Manifest
|
4
|
+
README.rdoc
|
5
|
+
Rakefile
|
6
|
+
lib/tidy_ffi/interface.rb
|
7
|
+
lib/tidy_ffi/lib_tidy.rb
|
8
|
+
lib/tidy_ffi/options_container.rb
|
9
|
+
lib/tidy_ffi/tidy.rb
|
10
|
+
lib/tidy_ffi/tidy_ffi_extensions.rb
|
11
|
+
lib/tidy_ffi.rb
|
12
|
+
test/test_helper.rb
|
13
|
+
test/test_lowlevel.rb
|
14
|
+
test/test_options.rb
|
15
|
+
test/test_simple.rb
|
16
|
+
tidy_ffi.gemspec
|
data/README.rdoc
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
= Tidy FFI
|
2
|
+
|
3
|
+
== What is it all about?
|
4
|
+
|
5
|
+
I wanna clean and simple tidy library. For example:
|
6
|
+
TidyFFI::Tidy.new('a string').clean
|
7
|
+
|
8
|
+
For now it can't do anything else than clean (and saves errors from it) :)
|
9
|
+
|
10
|
+
== Options
|
11
|
+
|
12
|
+
You can use different ways to set up options. These examples are produces the same output:
|
13
|
+
|
14
|
+
TidyFFI::Tidy.default_options.show_body_only = true
|
15
|
+
TidyFFI::Tidy.new('test').clean
|
16
|
+
|
17
|
+
TidyFFI::Tidy.with_options(:show_body_only => true).new('test').clean
|
18
|
+
|
19
|
+
tidy = TidyFFI::Tidy.new('test')
|
20
|
+
tidy.options.show_body_only = true
|
21
|
+
tidy.clean
|
22
|
+
|
23
|
+
TidyFFI::Tidy.new('test', :show_body_only => true).clean
|
24
|
+
|
25
|
+
TidyFFI::Tidy.clean('test', :show_body_only => 1)
|
26
|
+
|
27
|
+
== Links
|
28
|
+
|
29
|
+
* Source code: http://github.com/libc/tidy_ffi
|
30
|
+
* Bug tracker: http://rubyforge.org/tracker/?atid=30230&group_id=7805&func=browse
|
31
|
+
* Rubyforge project: http://rubyforge.org/projects/tidy-ffi
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
begin
|
3
|
+
require 'echoe'
|
4
|
+
Echoe.new('tidy_ffi') do |p|
|
5
|
+
p.author = 'Eugene Pimenov'
|
6
|
+
p.summary = 'Tidy library interface via FFI'
|
7
|
+
p.url = 'http://github.com/libc/tidy_ffi'
|
8
|
+
p.runtime_dependencies = ['ffi >=0.3.5']
|
9
|
+
# p.development_dependencies = ['rr', 'matchy', 'context']
|
10
|
+
p.project = 'tidy-ffi'
|
11
|
+
p.email = 'libc@libc.st'
|
12
|
+
p.rdoc_pattern = /^(lib|bin|tasks|ext)|^README\.rdoc|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
|
13
|
+
p.retain_gemspec = true
|
14
|
+
end
|
15
|
+
|
16
|
+
# My common error
|
17
|
+
task :rdoc => :doc do; end
|
18
|
+
rescue LoadError => boom
|
19
|
+
puts "You are missing a dependency required for meta-operations on this gem."
|
20
|
+
puts "#{boom.to_s.capitalize}."
|
21
|
+
|
22
|
+
desc 'No effect.'
|
23
|
+
task :default do; end
|
24
|
+
end
|
data/lib/tidy_ffi.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ffi'
|
3
|
+
|
4
|
+
module TidyFFI
|
5
|
+
self.autoload :LibTidy, 'tidy_ffi/lib_tidy'
|
6
|
+
self.autoload :Interface, 'tidy_ffi/interface'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'tidy_ffi/options_container'
|
10
|
+
require 'tidy_ffi/tidy'
|
11
|
+
require 'tidy_ffi/tidy_ffi_extensions'
|
@@ -0,0 +1,186 @@
|
|
1
|
+
# Low level interface to libtidy.
|
2
|
+
class TidyFFI::Interface
|
3
|
+
LibTidy = TidyFFI::LibTidy
|
4
|
+
|
5
|
+
# Returns a TidyFFI::Interface with initialized interface.
|
6
|
+
def self.with_doc
|
7
|
+
doc = LibTidy.tidyCreate
|
8
|
+
nd = new(doc)
|
9
|
+
nd.with_redirected_error_buffer { yield nd }
|
10
|
+
ensure
|
11
|
+
LibTidy.tidyRelease(doc)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(doc) #:nodoc:
|
15
|
+
@doc = doc
|
16
|
+
end
|
17
|
+
|
18
|
+
# Apply options
|
19
|
+
def apply_options(options)
|
20
|
+
options.each do |key, value|
|
21
|
+
k = key.to_s.gsub('_', '-')
|
22
|
+
|
23
|
+
option = LibTidy.tidyGetOptionByName(@doc, k)
|
24
|
+
raise ArgumentError, "don't know about option #{key}" if option.null?
|
25
|
+
id = LibTidy.tidyOptGetId(option)
|
26
|
+
raise ArgumentError, "can't setup option #{key} to #{value}" if LibTidy.tidyOptSetValue(@doc, id, value.to_s) == 0
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Sets string to tidy
|
31
|
+
def string=(str)
|
32
|
+
LibTidy.tidyParseString(@doc, str)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Cleans string
|
36
|
+
def clean
|
37
|
+
@output = nil
|
38
|
+
LibTidy.tidyCleanAndRepair(@doc)
|
39
|
+
end
|
40
|
+
alias :clean_and_repair :clean
|
41
|
+
alias :repair :clean
|
42
|
+
|
43
|
+
# Returns output from tidy library
|
44
|
+
def output
|
45
|
+
@output ||= begin
|
46
|
+
with_buffer_pointer do |buf|
|
47
|
+
LibTidy.tidySaveBuffer(@doc, buf)
|
48
|
+
buf[:bp]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def errors
|
54
|
+
@error_buffer[:bp]
|
55
|
+
end
|
56
|
+
|
57
|
+
# Redirects error buffer
|
58
|
+
def with_redirected_error_buffer
|
59
|
+
with_buffer_pointer do |buf|
|
60
|
+
@error_buffer = buf
|
61
|
+
LibTidy.tidySetErrorBuffer(@doc, buf)
|
62
|
+
yield
|
63
|
+
end
|
64
|
+
ensure
|
65
|
+
@error_buffer = nil
|
66
|
+
end
|
67
|
+
|
68
|
+
# Yields block with new buffer
|
69
|
+
def with_buffer_pointer
|
70
|
+
buf = tidy_buf_object.new
|
71
|
+
yield buf
|
72
|
+
ensure
|
73
|
+
LibTidy.tidyBufFree(buf)
|
74
|
+
end
|
75
|
+
|
76
|
+
def tidy_buf_object
|
77
|
+
@tidy_buf_object ||= begin
|
78
|
+
TidyFFI::LibTidy::TidyBufWithAllocator
|
79
|
+
end
|
80
|
+
end
|
81
|
+
private :tidy_buf_object
|
82
|
+
|
83
|
+
class <<self
|
84
|
+
# Returns enumeration for opt.
|
85
|
+
#
|
86
|
+
# Some tidy options might try to trespass as integer, and in order to caught
|
87
|
+
# perpertraitors we need to call tidyOptGetPickList
|
88
|
+
def pick_list_for(opt)
|
89
|
+
iterator = LibTidy.tidyOptGetPickList(opt)
|
90
|
+
|
91
|
+
return nil if iterator.null?
|
92
|
+
|
93
|
+
pick_list = []
|
94
|
+
|
95
|
+
FFI::MemoryPointer.new(:pointer, 1) do |pointer|
|
96
|
+
pointer.put_pointer(0, iterator)
|
97
|
+
until iterator.null?
|
98
|
+
pick_list << LibTidy.tidyOptGetNextPick(opt, pointer)
|
99
|
+
iterator = pointer.get_pointer(0)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
pick_list
|
104
|
+
end
|
105
|
+
private :pick_list_for
|
106
|
+
|
107
|
+
# Loads default options.
|
108
|
+
def load_default_options
|
109
|
+
return if @default_options
|
110
|
+
|
111
|
+
doc = LibTidy.tidyCreate
|
112
|
+
iterator = LibTidy.tidyGetOptionList(doc)
|
113
|
+
|
114
|
+
@default_options = {}
|
115
|
+
|
116
|
+
FFI::MemoryPointer.new(:pointer, 1) do |pointer|
|
117
|
+
pointer.put_pointer(0, iterator)
|
118
|
+
|
119
|
+
until iterator.null?
|
120
|
+
opt = LibTidy.tidyGetNextOption(doc, pointer)
|
121
|
+
|
122
|
+
option = {}
|
123
|
+
|
124
|
+
option[:name] = LibTidy.tidyOptGetName(opt).gsub('-', '_').intern
|
125
|
+
option[:readonly?] = LibTidy.tidyOptIsReadOnly(opt) != 0
|
126
|
+
option[:type] = LibTidy::TIDY_OPTION_TYPE[LibTidy.tidyOptGetType(opt)]
|
127
|
+
option[:default] = case option[:type]
|
128
|
+
when :string
|
129
|
+
(LibTidy.tidyOptGetDefault(opt) rescue "")
|
130
|
+
when :integer
|
131
|
+
if pick_list = pick_list_for(opt)
|
132
|
+
option[:type] = :enum
|
133
|
+
option[:values] = pick_list
|
134
|
+
pick_list[LibTidy.tidyOptGetDefaultInt(opt)]
|
135
|
+
else
|
136
|
+
LibTidy.tidyOptGetDefaultInt(opt)
|
137
|
+
end
|
138
|
+
when :boolean
|
139
|
+
LibTidy.tidyOptGetDefaultBool(opt) != 0
|
140
|
+
end
|
141
|
+
|
142
|
+
@default_options[option[:name]] = option
|
143
|
+
|
144
|
+
iterator = pointer.get_pointer(0)
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
@default_options.freeze
|
149
|
+
ensure
|
150
|
+
LibTidy.tidyRelease(doc)
|
151
|
+
end
|
152
|
+
private :load_default_options
|
153
|
+
end
|
154
|
+
|
155
|
+
# Returns a hash that represents default options for tidy.
|
156
|
+
# Key for hash is the option name, value is also a hash...
|
157
|
+
# Possible values are:
|
158
|
+
# * :type - either :string, :integer, :boolean or :enum
|
159
|
+
# * :readonly?
|
160
|
+
# * :default - default value of an option
|
161
|
+
# * :values - possible values for :enum
|
162
|
+
# * :name
|
163
|
+
def self.default_options
|
164
|
+
@default_options ||= load_default_options
|
165
|
+
end
|
166
|
+
|
167
|
+
# Returns true if value is valid for +option+ and false otherwise.
|
168
|
+
def self.option_valid?(option, value)
|
169
|
+
return false unless spec = default_options[option]
|
170
|
+
|
171
|
+
case spec[:type]
|
172
|
+
when :boolean
|
173
|
+
true == value || false == value || value == 0 || value == 1 || %w(on off true false 0 1 yes no).include?(value.downcase)
|
174
|
+
when :integer
|
175
|
+
Integer === value || !!(value =~ /^\d+$/)
|
176
|
+
when :enum
|
177
|
+
if Integer === value
|
178
|
+
!!spec[:values][value]
|
179
|
+
else
|
180
|
+
spec[:values].include?(value)
|
181
|
+
end
|
182
|
+
when :string
|
183
|
+
String === value || Symbol === value
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# Very low level interface to tidy
|
2
|
+
|
3
|
+
# This file must be lazy loaded!
|
4
|
+
class TidyFFI::LibTidy #:nodoc:
|
5
|
+
extend FFI::Library
|
6
|
+
ffi_lib TidyFFI.library_path
|
7
|
+
|
8
|
+
attach_function :tidyReleaseDate, [], :string
|
9
|
+
|
10
|
+
attach_function :tidyCreate, [], :pointer
|
11
|
+
attach_function :tidyRelease, [:pointer], :void
|
12
|
+
|
13
|
+
attach_function :tidyCleanAndRepair, [:pointer], :int
|
14
|
+
attach_function :tidyRunDiagnostics, [:pointer], :int
|
15
|
+
|
16
|
+
attach_function :tidyParseString, [:pointer, :string], :int
|
17
|
+
|
18
|
+
attach_function :tidySaveBuffer, [:pointer, :pointer], :int
|
19
|
+
|
20
|
+
attach_function :tidySetErrorBuffer, [:pointer, :pointer], :int
|
21
|
+
|
22
|
+
attach_function :tidyBufFree, [:pointer], :void
|
23
|
+
|
24
|
+
attach_function :tidyGetOptionByName, [:pointer, :string], :pointer
|
25
|
+
attach_function :tidyOptGetId, [:pointer], :int
|
26
|
+
attach_function :tidyOptSetValue, [:pointer, :int, :string], :int
|
27
|
+
|
28
|
+
# iterators
|
29
|
+
attach_function :tidyGetOptionList, [:pointer], :pointer
|
30
|
+
attach_function :tidyGetNextOption, [:pointer, :pointer], :pointer
|
31
|
+
attach_function :tidyOptGetName, [:pointer], :string
|
32
|
+
attach_function :tidyOptGetType, [:pointer], :int
|
33
|
+
attach_function :tidyOptGetDefault, [:pointer], :string
|
34
|
+
attach_function :tidyOptGetDefaultInt, [:pointer], :ulong
|
35
|
+
attach_function :tidyOptGetDefaultBool, [:pointer], :int
|
36
|
+
attach_function :tidyOptIsReadOnly, [:pointer], :int
|
37
|
+
attach_function :tidyOptGetPickList, [:pointer], :pointer
|
38
|
+
attach_function :tidyOptGetNextPick, [:pointer, :pointer], :string
|
39
|
+
|
40
|
+
#types
|
41
|
+
# /** Option data types
|
42
|
+
# */
|
43
|
+
# typedef enum
|
44
|
+
# {
|
45
|
+
# TidyString, /**< String */
|
46
|
+
# TidyInteger, /**< Integer or enumeration */
|
47
|
+
# TidyBoolean /**< Boolean flag */
|
48
|
+
# } TidyOptionType;
|
49
|
+
TIDY_OPTION_TYPE = [:string, :integer, :boolean].freeze
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
class TidyFFI::LibTidy::TidyBuf < FFI::Struct #:nodoc:
|
54
|
+
layout :bp, :string,
|
55
|
+
:size, :uint,
|
56
|
+
:allocated, :uint,
|
57
|
+
:next, :uint
|
58
|
+
end
|
59
|
+
|
60
|
+
class TidyFFI::LibTidy::TidyBufWithAllocator < FFI::Struct #:nodoc:
|
61
|
+
layout :allocator, :pointer,
|
62
|
+
:bp, :string,
|
63
|
+
:size, :uint,
|
64
|
+
:allocated, :uint,
|
65
|
+
:next, :uint
|
66
|
+
end
|
67
|
+
|
@@ -0,0 +1,90 @@
|
|
1
|
+
class TidyFFI::OptionsContainer #:nodoc:
|
2
|
+
|
3
|
+
def initialize(ops = nil)
|
4
|
+
if ops
|
5
|
+
@options = ops.to_hash!
|
6
|
+
else
|
7
|
+
@options = {}
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_hash!
|
12
|
+
@options.dup
|
13
|
+
end
|
14
|
+
|
15
|
+
def merge_with_options(options)
|
16
|
+
options.each do |key, val|
|
17
|
+
key = key.intern unless Symbol === key
|
18
|
+
validate_option(key, val)
|
19
|
+
@options[key] = val
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def ==(obj)
|
24
|
+
if obj.is_a?(Hash)
|
25
|
+
@options == obj
|
26
|
+
elsif obj.is_a?(OptionsContainer)
|
27
|
+
obj == @options
|
28
|
+
else
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def clear!
|
34
|
+
@options = {}
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def method_missing(method, *args)
|
39
|
+
if method.to_s =~ /=$/
|
40
|
+
key, val = method.to_s.sub(/=$/, '').intern, args.first
|
41
|
+
validate_option(key, val)
|
42
|
+
@options[key] = val
|
43
|
+
else
|
44
|
+
@options[method]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# It's a kinda bad method: it uses TidyFFI::Interface.option_valid and TidyFFI::Tidy.validate_options?
|
49
|
+
# Also it do second lookup into default options
|
50
|
+
def validate_option(key, value)
|
51
|
+
if TidyFFI::Tidy.validate_options? && !TidyFFI::Interface.option_valid?(key, value)
|
52
|
+
if TidyFFI::Interface.default_options[key]
|
53
|
+
raise TidyFFI::Tidy::InvalidOptionValue, "#{value} is not valid for #{key}"
|
54
|
+
else
|
55
|
+
raise TidyFFI::Tidy::InvalidOptionName, "#{key} is invalid option name"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class Proxy #:nodoc:
|
61
|
+
attr_reader :options
|
62
|
+
|
63
|
+
define_method :new do |*args|
|
64
|
+
@obj.new(args.first, options.to_hash!.merge(args[1] || {}))
|
65
|
+
end
|
66
|
+
|
67
|
+
def clean(str, opts = {})
|
68
|
+
@obj.clean(str, options.to_hash!.merge(opts))
|
69
|
+
end
|
70
|
+
|
71
|
+
def initialize(obj, options1, options2)
|
72
|
+
@obj = obj
|
73
|
+
@options = TidyFFI::OptionsContainer.new(options1)
|
74
|
+
@options.merge_with_options(options2)
|
75
|
+
end
|
76
|
+
|
77
|
+
def with_options(options)
|
78
|
+
Proxy.new(@obj, @options, options)
|
79
|
+
end
|
80
|
+
|
81
|
+
def clear!
|
82
|
+
@options.clear!
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
def method_missing(meth, *args)
|
87
|
+
@obj.send(meth, *args)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# Clean and simple interface to Tidy
|
2
|
+
class TidyFFI::Tidy
|
3
|
+
OptionsContainer = TidyFFI::OptionsContainer
|
4
|
+
class InvalidOptionName < ArgumentError; end
|
5
|
+
class InvalidOptionValue < ArgumentError; end
|
6
|
+
|
7
|
+
#Initializing object.
|
8
|
+
#
|
9
|
+
#* str is a string to tidy
|
10
|
+
#* options are options for tidy
|
11
|
+
def initialize(str, options = {})
|
12
|
+
@string = str
|
13
|
+
@options = OptionsContainer.new(self.class.default_options)
|
14
|
+
self.options = options
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns cleaned string
|
18
|
+
def clean
|
19
|
+
@clean ||= TidyFFI::Interface.with_doc do |doc|
|
20
|
+
doc.apply_options(@options.to_hash!)
|
21
|
+
doc.string = @string
|
22
|
+
doc.clean
|
23
|
+
@errors = doc.errors
|
24
|
+
doc.output
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns cleaned string
|
29
|
+
def self.clean(str, options = {})
|
30
|
+
new(str, options).clean
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns errors for string
|
34
|
+
def errors
|
35
|
+
@errors ||= begin
|
36
|
+
clean
|
37
|
+
@errors
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Assigns options for tidy.
|
42
|
+
# It merges options, not deletes old ones.
|
43
|
+
# tidy.options= {:wrap_asp => true}
|
44
|
+
# tidy.options= {:show_body_only => true}
|
45
|
+
# Will send to tidy both options.
|
46
|
+
def options=(options)
|
47
|
+
@options.merge_with_options(options)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Proxy for options. Supports set and get
|
51
|
+
#
|
52
|
+
# tidy.options.show_body_only #=> nil
|
53
|
+
# tidy.options.show_body_only = true
|
54
|
+
# tidy.options.show_body_only #=> true
|
55
|
+
def options
|
56
|
+
@options
|
57
|
+
end
|
58
|
+
|
59
|
+
class <<self
|
60
|
+
# Default options for tidy. Works just like options method
|
61
|
+
def default_options
|
62
|
+
@default_options ||= OptionsContainer.new
|
63
|
+
end
|
64
|
+
|
65
|
+
# Default options for tidy. Works just like options= method
|
66
|
+
def default_options=(options)
|
67
|
+
@default_options.merge_with_options(options)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns a proxy class with options.
|
71
|
+
# Example:
|
72
|
+
# TidyFFI::Tidy.with_options(:show_body_only => true).with_options(:wrap_asp => true).new('test)
|
73
|
+
def with_options(options)
|
74
|
+
OptionsContainer::Proxy.new(self, @default_options, options)
|
75
|
+
end
|
76
|
+
|
77
|
+
# When true it validates name and option type (default is true).
|
78
|
+
def validate_options?
|
79
|
+
@validate_options != false
|
80
|
+
end
|
81
|
+
attr_writer :validate_options
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module TidyFFI::TidyFFIExtensions #:nodoc:
|
2
|
+
# Sets path to libtidy.{dylib,so}
|
3
|
+
def library_path=(path)
|
4
|
+
@libtidy_path = path
|
5
|
+
end
|
6
|
+
|
7
|
+
# Returns path to libtidy.{dylib,so}
|
8
|
+
def library_path
|
9
|
+
@libtidy_path ||= find_tidy
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_tidy
|
13
|
+
fnames = ['libtidy.dylib', 'libtidy.so']
|
14
|
+
pathes = []
|
15
|
+
pathes += ENV['LD_LIBRARY_PATH'].split(':') if ENV['LD_LIBRARY_PATH']
|
16
|
+
pathes += ENV['DYLD_LIBRARY_PATH'].split(':') if ENV['DYLD_LIBRARY_PATH']
|
17
|
+
pathes += ENV['PATH'].split(':').reject { |a| a['sbin'] }.map { |a| a.sub('/bin', '/lib') } if ENV['PATH']
|
18
|
+
pathes = ['/usr/lib', '/usr/local/lib'] if pathes.size == 0
|
19
|
+
for path in pathes.uniq
|
20
|
+
fnames.each { |fname| return File.join(path, fname) if File.exists?(File.join(path, fname)) }
|
21
|
+
end
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
private :find_tidy
|
25
|
+
end
|
26
|
+
TidyFFI.extend TidyFFI::TidyFFIExtensions
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
require 'tidy_ffi'
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'rr'
|
7
|
+
require 'matchy'
|
8
|
+
require 'context'
|
9
|
+
|
10
|
+
class Test::Unit::TestCase
|
11
|
+
alias method_name name unless instance_methods.include?('method_name')
|
12
|
+
include RR::Adapters::TestUnit unless instance_methods.include?('stub') || instance_methods.include?(:stub)
|
13
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
class TestSimple < Test::Unit::TestCase
|
4
|
+
I = TidyFFI::Interface
|
5
|
+
context "TidyFFI::Interface" do
|
6
|
+
context "default_options" do
|
7
|
+
it "returns a hash" do
|
8
|
+
I.default_options.is_a?(Hash)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "has show force_output option" do
|
12
|
+
I.default_options[:force_output][:name].should == :force_output
|
13
|
+
I.default_options[:force_output][:type].should == :boolean
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "option_valid" do
|
18
|
+
it "returns false when unknown options is specified" do
|
19
|
+
I.option_valid?(:gobbledygook, '1').should == false
|
20
|
+
end
|
21
|
+
|
22
|
+
it "accepts yes, no, 1, 0, true, false as boolean values" do
|
23
|
+
stub(I).default_options { {:gobbledygook => {:type => :boolean}} }
|
24
|
+
|
25
|
+
%w{yes no 1 0 true false on off YeS}.each do |val|
|
26
|
+
I.option_valid?(:gobbledygook, val).should == true
|
27
|
+
end
|
28
|
+
I.option_valid?(:gobbledygook, 1).should == true
|
29
|
+
I.option_valid?(:gobbledygook, 0).should == true
|
30
|
+
I.option_valid?(:gobbledygook, true).should == true
|
31
|
+
I.option_valid?(:gobbledygook, false).should == true
|
32
|
+
|
33
|
+
I.option_valid?(:gobbledygook, "gobbledygook").should == false
|
34
|
+
end
|
35
|
+
|
36
|
+
it "accepts a number as a valid integer value" do
|
37
|
+
stub(I).default_options { {:gobbledygook => {:type => :integer}} }
|
38
|
+
|
39
|
+
I.option_valid?(:gobbledygook, 1).should == true
|
40
|
+
I.option_valid?(:gobbledygook, 0).should == true
|
41
|
+
I.option_valid?(:gobbledygook, "0").should == true
|
42
|
+
I.option_valid?(:gobbledygook, "1").should == true
|
43
|
+
I.option_valid?(:gobbledygook, "42").should == true
|
44
|
+
|
45
|
+
I.option_valid?(:gobbledygook, "gobbledygook").should == false
|
46
|
+
I.option_valid?(:gobbledygook, "true").should == false
|
47
|
+
end
|
48
|
+
|
49
|
+
it "accepts any string as a valid string value" do
|
50
|
+
stub(I).default_options { {:gobbledygook => {:type => :string}} }
|
51
|
+
|
52
|
+
I.option_valid?(:gobbledygook, 1).should == false
|
53
|
+
I.option_valid?(:gobbledygook, 0).should == false
|
54
|
+
|
55
|
+
I.option_valid?(:gobbledygook, "0").should == true
|
56
|
+
I.option_valid?(:gobbledygook, "1").should == true
|
57
|
+
I.option_valid?(:gobbledygook, "42").should == true
|
58
|
+
I.option_valid?(:gobbledygook, "gobbledygook").should == true
|
59
|
+
I.option_valid?(:gobbledygook, "true").should == true
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
it "accepts a symbols as a valid string value" do
|
64
|
+
stub(I).default_options { {:gobbledygook => {:type => :string}} }
|
65
|
+
|
66
|
+
I.option_valid?(:gobbledygook, :test).should == true
|
67
|
+
end
|
68
|
+
|
69
|
+
it "accepts number in range as a valid enum value" do
|
70
|
+
stub(I).default_options { {:gobbledygook => {:type => :enum, :values => ["one", "two"]}} }
|
71
|
+
|
72
|
+
I.option_valid?(:gobbledygook, 1).should == true
|
73
|
+
I.option_valid?(:gobbledygook, 0).should == true
|
74
|
+
I.option_valid?(:gobbledygook, 3).should == false
|
75
|
+
end
|
76
|
+
|
77
|
+
it "accepts string representation of enum value" do
|
78
|
+
stub(I).default_options { {:gobbledygook => {:type => :enum, :values => ["one", "two"]}} }
|
79
|
+
|
80
|
+
I.option_valid?(:gobbledygook, "one").should == true
|
81
|
+
I.option_valid?(:gobbledygook, "two").should == true
|
82
|
+
I.option_valid?(:gobbledygook, "three").should == false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
class TestOptions < Test::Unit::TestCase
|
4
|
+
T = TidyFFI::Tidy
|
5
|
+
context "public interface" do
|
6
|
+
[:default_options, :default_options=, :with_options, :validate_options].each do |method|
|
7
|
+
it "responds to #{method}" do
|
8
|
+
T.respond_to?(method)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "default_options method" do
|
14
|
+
before :each do
|
15
|
+
T.default_options.clear!
|
16
|
+
T.validate_options = false
|
17
|
+
end
|
18
|
+
|
19
|
+
context "equals version" do
|
20
|
+
it "passes arguments to default_options.merge_with_options" do
|
21
|
+
T.default_options.expects(:merge_with_options)
|
22
|
+
T.default_options = {:test => 1, :test2 => 2}
|
23
|
+
end
|
24
|
+
|
25
|
+
it "merges options with existing" do
|
26
|
+
T.default_options = {:test => 1, :test2 => 2}
|
27
|
+
T.default_options = {:test3 => 3, :test2 => 42}
|
28
|
+
T.default_options.should == {:test => 1, :test2 => 42, :test3 => 3}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "has clear! method to clear anything" do
|
33
|
+
T.default_options.test = 1
|
34
|
+
T.default_options.clear!
|
35
|
+
T.default_options.test.should == nil
|
36
|
+
end
|
37
|
+
|
38
|
+
it "saves options" do
|
39
|
+
T.default_options.option = 1
|
40
|
+
T.default_options.option.should == 1
|
41
|
+
end
|
42
|
+
|
43
|
+
it "sets optons after creation" do
|
44
|
+
T.new('test').options.option.should == nil
|
45
|
+
T.default_options.option = 1
|
46
|
+
T.new('test').options.option.should == 1
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "options method" do
|
51
|
+
before :each do
|
52
|
+
T.validate_options = false
|
53
|
+
T.default_options.clear!
|
54
|
+
@t = T.new('test')
|
55
|
+
end
|
56
|
+
|
57
|
+
context "equal version" do
|
58
|
+
before :each do
|
59
|
+
@t.options.clear!
|
60
|
+
end
|
61
|
+
|
62
|
+
it "passes arguments to options.merge_with_options" do
|
63
|
+
@t.options.expects(:merge_with_options)
|
64
|
+
@t.options = {:test => 1, :test2 => 2}
|
65
|
+
end
|
66
|
+
|
67
|
+
it "merges options with existing" do
|
68
|
+
@t.options = {:test => 1, :test2 => 2}
|
69
|
+
@t.options = {:test3 => 3, :test2 => 42}
|
70
|
+
@t.options.should == {:test => 1, :test2 => 42, :test3 => 3}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context "clear! method" do
|
75
|
+
it "clears options' options" do
|
76
|
+
@t.options.test = 1
|
77
|
+
@t.options.clear!
|
78
|
+
@t.options.test.should == nil
|
79
|
+
end
|
80
|
+
|
81
|
+
it "clears default_options's options" do
|
82
|
+
T.default_options.test = 1
|
83
|
+
@t = T.new('test')
|
84
|
+
@t.options.clear!
|
85
|
+
@t.options.test.should == nil
|
86
|
+
end
|
87
|
+
|
88
|
+
it "clears with_options's options" do
|
89
|
+
@t = T.with_options(:test => 1).new('test')
|
90
|
+
@t.options.clear!
|
91
|
+
@t.options.test.should == nil
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it "saves options" do
|
96
|
+
@t.options.option = 1
|
97
|
+
@t.options.option.should == 1
|
98
|
+
end
|
99
|
+
|
100
|
+
it "passes options to libtidy" do
|
101
|
+
@t.options.show_body_only = 1
|
102
|
+
@t.clean.should == "test\n"
|
103
|
+
end
|
104
|
+
|
105
|
+
it "passes options to clean class method" do
|
106
|
+
T.with_options(:show_body_only => true).clean("test").should == "test\n"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context "with_options proxy class" do
|
111
|
+
before :each do
|
112
|
+
T.validate_options = false
|
113
|
+
end
|
114
|
+
|
115
|
+
it "has options method" do
|
116
|
+
T.with_options(:test => 1).options.test.should == 1
|
117
|
+
end
|
118
|
+
|
119
|
+
it "has clear! method" do
|
120
|
+
T.with_options(:test => 1).clear!.options.test.should == nil
|
121
|
+
end
|
122
|
+
|
123
|
+
it "chain methods" do
|
124
|
+
proxy = T.with_options(:test => 1).with_options(:test2 => 2)
|
125
|
+
proxy.options.test.should == 1
|
126
|
+
proxy.options.test2.should == 2
|
127
|
+
end
|
128
|
+
|
129
|
+
it "passes options to object" do
|
130
|
+
T.with_options(:test => 1).new('test').options.test.should == 1
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/test/test_simple.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
class TestSimple < Test::Unit::TestCase
|
4
|
+
T = TidyFFI::Tidy
|
5
|
+
context "TidyFFI::Tidy" do
|
6
|
+
context "public interface" do
|
7
|
+
[[:initialize, -2],
|
8
|
+
[:clean, 0],
|
9
|
+
[:errors, 0]].each do |method, arity|
|
10
|
+
it "method #{method} has arity #{arity}" do
|
11
|
+
T.instance_method(method).arity.should == arity
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context "simple cleanup" do
|
17
|
+
it "clean up text" do
|
18
|
+
T.new("test").clean.should =~ %r{<body>\s+test\s+</body>}
|
19
|
+
T.new("test").clean.should =~ %r{<meta name="generator" content=.+?Tidy.+?>}m
|
20
|
+
|
21
|
+
T.clean("test").should =~ %r{<body>\s+test\s+</body>}
|
22
|
+
T.clean("test").should =~ %r{<meta name="generator" content=.+?Tidy.+?>}m
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "should have method for errors" do
|
27
|
+
it "have method for errors" do
|
28
|
+
t = T.new("test")
|
29
|
+
t.clean
|
30
|
+
t.errors.should == "line 1 column 1 - Warning: missing <!DOCTYPE> declaration\nline 1 column 1 - Warning: plain text isn't allowed in <head> elements\nline 1 column 1 - Warning: inserting missing 'title' element\n"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
# Commented out, because of broken upstream matchy gem :(
|
36
|
+
# context "options validation" do
|
37
|
+
# it "raises error on invalid option name" do
|
38
|
+
# TidyFFI::Tidy.validate_options = true
|
39
|
+
# lambda do
|
40
|
+
# TidyFFI::Tidy.default_options = {:complete_garbage => true}
|
41
|
+
# end.should raise_error(TidyFFI::Tidy::InvalidOptionName)
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# it "raises error on invalid option value" do
|
45
|
+
# TidyFFI::Tidy.validate_options = true
|
46
|
+
# lambda do
|
47
|
+
# TidyFFI::Tidy.default_options = {:force_output => "utter trash"}
|
48
|
+
# end.should raise_error(TidyFFI::Tidy::InvalidOptionValue)
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
end
|
52
|
+
end
|
data/tidy_ffi.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{tidy_ffi}
|
5
|
+
s.version = "0.1.2"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Eugene Pimenov"]
|
9
|
+
s.date = %q{2009-06-07}
|
10
|
+
s.description = %q{Tidy library interface via FFI}
|
11
|
+
s.email = %q{libc@libc.st}
|
12
|
+
s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README.rdoc", "lib/tidy_ffi/interface.rb", "lib/tidy_ffi/lib_tidy.rb", "lib/tidy_ffi/options_container.rb", "lib/tidy_ffi/tidy.rb", "lib/tidy_ffi/tidy_ffi_extensions.rb", "lib/tidy_ffi.rb"]
|
13
|
+
s.files = ["CHANGELOG", "LICENSE", "Manifest", "README.rdoc", "Rakefile", "lib/tidy_ffi/interface.rb", "lib/tidy_ffi/lib_tidy.rb", "lib/tidy_ffi/options_container.rb", "lib/tidy_ffi/tidy.rb", "lib/tidy_ffi/tidy_ffi_extensions.rb", "lib/tidy_ffi.rb", "test/test_helper.rb", "test/test_lowlevel.rb", "test/test_options.rb", "test/test_simple.rb", "tidy_ffi.gemspec"]
|
14
|
+
s.homepage = %q{http://github.com/libc/tidy_ffi}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Tidy_ffi", "--main", "README.rdoc"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{tidy-ffi}
|
18
|
+
s.rubygems_version = %q{1.3.4}
|
19
|
+
s.summary = %q{Tidy library interface via FFI}
|
20
|
+
s.test_files = ["test/test_helper.rb", "test/test_lowlevel.rb", "test/test_options.rb", "test/test_simple.rb"]
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 3
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
27
|
+
s.add_runtime_dependency(%q<ffi>, [">= 0.3.5"])
|
28
|
+
else
|
29
|
+
s.add_dependency(%q<ffi>, [">= 0.3.5"])
|
30
|
+
end
|
31
|
+
else
|
32
|
+
s.add_dependency(%q<ffi>, [">= 0.3.5"])
|
33
|
+
end
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dominikh-tidy_ffi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eugene Pimenov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-06-07 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: ffi
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.3.5
|
24
|
+
version:
|
25
|
+
description: Tidy library interface via FFI
|
26
|
+
email: libc@libc.st
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- CHANGELOG
|
33
|
+
- LICENSE
|
34
|
+
- README.rdoc
|
35
|
+
- lib/tidy_ffi/interface.rb
|
36
|
+
- lib/tidy_ffi/lib_tidy.rb
|
37
|
+
- lib/tidy_ffi/options_container.rb
|
38
|
+
- lib/tidy_ffi/tidy.rb
|
39
|
+
- lib/tidy_ffi/tidy_ffi_extensions.rb
|
40
|
+
- lib/tidy_ffi.rb
|
41
|
+
files:
|
42
|
+
- CHANGELOG
|
43
|
+
- LICENSE
|
44
|
+
- Manifest
|
45
|
+
- README.rdoc
|
46
|
+
- Rakefile
|
47
|
+
- lib/tidy_ffi/interface.rb
|
48
|
+
- lib/tidy_ffi/lib_tidy.rb
|
49
|
+
- lib/tidy_ffi/options_container.rb
|
50
|
+
- lib/tidy_ffi/tidy.rb
|
51
|
+
- lib/tidy_ffi/tidy_ffi_extensions.rb
|
52
|
+
- lib/tidy_ffi.rb
|
53
|
+
- test/test_helper.rb
|
54
|
+
- test/test_lowlevel.rb
|
55
|
+
- test/test_options.rb
|
56
|
+
- test/test_simple.rb
|
57
|
+
- tidy_ffi.gemspec
|
58
|
+
has_rdoc: false
|
59
|
+
homepage: http://github.com/libc/tidy_ffi
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options:
|
62
|
+
- --line-numbers
|
63
|
+
- --inline-source
|
64
|
+
- --title
|
65
|
+
- Tidy_ffi
|
66
|
+
- --main
|
67
|
+
- README.rdoc
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: "0"
|
75
|
+
version:
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: "1.2"
|
81
|
+
version:
|
82
|
+
requirements: []
|
83
|
+
|
84
|
+
rubyforge_project: tidy-ffi
|
85
|
+
rubygems_version: 1.2.0
|
86
|
+
signing_key:
|
87
|
+
specification_version: 3
|
88
|
+
summary: Tidy library interface via FFI
|
89
|
+
test_files:
|
90
|
+
- test/test_helper.rb
|
91
|
+
- test/test_lowlevel.rb
|
92
|
+
- test/test_options.rb
|
93
|
+
- test/test_simple.rb
|