tidy_ffi 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,4 @@
1
+ v0.0.4. Options validation
1
2
  v0.0.3. Add method “errors” to return errors after cleanup
2
3
  v0.0.2. Do not require matchy, rr and context as development dependencies. A user does not need them.
3
4
  v0.0.1. Options support.
@@ -84,4 +84,108 @@ class TidyFFI::Interface
84
84
  end
85
85
  end
86
86
  private :tidy_buf_object
87
+
88
+ class <<self
89
+ # Returns enumeration for opt.
90
+ #
91
+ # Some tidy options might try to trespass as integer, and in order to caught
92
+ # perpertraitors we need to call tidyOptGetPickList
93
+ def pic_list_for(opt)
94
+ iterator = LibTidy.tidyOptGetPickList(opt)
95
+
96
+ return nil if iterator.null?
97
+
98
+ pic_list = []
99
+
100
+ MemoryPointer.new(:pointer) do |pointer|
101
+ pointer.put_pointer(0, iterator)
102
+ until iterator.null?
103
+ pic_list << LibTidy.tidyOptGetNextPick(opt, pointer)
104
+ iterator = pointer.get_pointer(0)
105
+ end
106
+ end
107
+
108
+ pic_list
109
+ end
110
+ private :pic_list_for
111
+
112
+ # Loads default options.
113
+ def load_default_options
114
+ return if @default_options
115
+
116
+ doc = LibTidy.tidyCreate
117
+ iterator = LibTidy.tidyGetOptionList(doc)
118
+
119
+ @default_options = {}
120
+
121
+ MemoryPointer.new(:pointer) do |pointer|
122
+ pointer.put_pointer(0, iterator)
123
+
124
+ until iterator.null?
125
+ opt = LibTidy.tidyGetNextOption(doc, pointer)
126
+
127
+ option = {}
128
+
129
+ option[:name] = LibTidy.tidyOptGetName(opt).gsub('-', '_').intern
130
+ option[:readonly?] = LibTidy.tidyOptIsReadOnly(opt) != 0
131
+ option[:type] = LibTidy::TIDY_OPTION_TYPE[LibTidy.tidyOptGetType(opt)]
132
+ option[:default] = case option[:type]
133
+ when :string
134
+ (LibTidy.tidyOptGetDefault(opt) rescue "")
135
+ when :integer
136
+ if pic_list = pic_list_for(opt)
137
+ option[:type] = :enum
138
+ option[:values] = pic_list
139
+ pic_list[LibTidy.tidyOptGetDefaultInt(opt)]
140
+ else
141
+ LibTidy.tidyOptGetDefaultInt(opt)
142
+ end
143
+ when :boolean
144
+ LibTidy.tidyOptGetDefaultBool(opt) != 0
145
+ end
146
+
147
+ @default_options[option[:name]] = option
148
+
149
+ iterator = pointer.get_pointer(0)
150
+ end
151
+
152
+ @default_options.freeze
153
+ end
154
+ ensure
155
+ LibTidy.tidyRelease(doc)
156
+ end
157
+ private :load_default_options
158
+ end
159
+
160
+ # Returns a hash that represents default options for tidy.
161
+ # Key for hash is the option name, value is also a hash...
162
+ # Possible values are:
163
+ # * :type - either :string, :integer, :boolean or :enum
164
+ # * :readonly?
165
+ # * :default - default value of an option
166
+ # * :values - possible values for :enum
167
+ # * :name
168
+ def self.default_options
169
+ @default_options ||= load_default_options
170
+ end
171
+
172
+ # Returns true if value is valid for +option+ and false otherwise.
173
+ def self.option_valid?(option, value)
174
+ return false unless spec = default_options[option]
175
+
176
+ case spec[:type]
177
+ when :boolean
178
+ true == value || false == value || value == 0 || value == 1 || %w(on off true false 0 1 yes no).include?(value.downcase)
179
+ when :integer
180
+ Integer === value || !!(value =~ /^\d+$/)
181
+ when :enum
182
+ if Integer === value
183
+ !!spec[:values][value]
184
+ else
185
+ spec[:values].include?(value)
186
+ end
187
+ when :string
188
+ String === value || Symbol === value
189
+ end
190
+ end
87
191
  end
@@ -24,6 +24,30 @@ class TidyFFI::LibTidy #:nodoc:
24
24
  attach_function :tidyGetOptionByName, [:pointer, :string], :pointer
25
25
  attach_function :tidyOptGetId, [:pointer], :int
26
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
+
27
51
  end
28
52
 
29
53
  class TidyFFI::LibTidy::TidyBuf < FFI::Struct #:nodoc:
@@ -1,4 +1,5 @@
1
1
  class TidyFFI::OptionsContainer #:nodoc:
2
+
2
3
  def initialize(ops = nil)
3
4
  if ops
4
5
  @options = ops.to_hash!
@@ -14,6 +15,7 @@ class TidyFFI::OptionsContainer #:nodoc:
14
15
  def merge_with_options(options)
15
16
  options.each do |key, val|
16
17
  key = key.intern unless Symbol === key
18
+ validate_option(key, val)
17
19
  @options[key] = val
18
20
  end
19
21
  end
@@ -35,12 +37,26 @@ class TidyFFI::OptionsContainer #:nodoc:
35
37
 
36
38
  def method_missing(method, *args)
37
39
  if method.to_s =~ /=$/
38
- @options[method.to_s.sub(/=$/, '').intern] = args.first
40
+ key, val = method.to_s.sub(/=$/, '').intern, args.first
41
+ validate_option(key, val)
42
+ @options[key] = val
39
43
  else
40
44
  @options[method]
41
45
  end
42
46
  end
43
-
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
+
44
60
  class Proxy #:nodoc:
45
61
  attr_reader :options
46
62
 
data/lib/tidy_ffi/tidy.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # Clean and simple interface to Tidy
2
2
  class TidyFFI::Tidy
3
3
  OptionsContainer = TidyFFI::OptionsContainer
4
+ class InvalidOptionName < ArgumentError; end
5
+ class InvalidOptionValue < ArgumentError; end
4
6
 
5
7
  #Initializing object.
6
8
  #
@@ -66,5 +68,11 @@ class TidyFFI::Tidy
66
68
  def with_options(options)
67
69
  OptionsContainer::Proxy.new(self, @default_options, options)
68
70
  end
71
+
72
+ # When true it validates name and option type (default is true).
73
+ def validate_options?
74
+ @validate_options != false
75
+ end
76
+ attr_writer :validate_options
69
77
  end
70
78
  end
data/test/test_helper.rb CHANGED
@@ -9,4 +9,5 @@ require 'context'
9
9
 
10
10
  class Test::Unit::TestCase
11
11
  alias method_name name unless instance_methods.include?('method_name')
12
+ include RR::Adapters::TestUnit unless instance_methods.include?('stub')
12
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
data/test/test_options.rb CHANGED
@@ -3,7 +3,7 @@ require File.join(File.dirname(__FILE__), 'test_helper')
3
3
  class TestOptions < Test::Unit::TestCase
4
4
  T = TidyFFI::Tidy
5
5
  context "public interface" do
6
- [:default_options, :default_options=, :with_options].each do |method|
6
+ [:default_options, :default_options=, :with_options, :validate_options].each do |method|
7
7
  it "responds to #{method}" do
8
8
  T.respond_to?(method)
9
9
  end
@@ -13,6 +13,7 @@ class TestOptions < Test::Unit::TestCase
13
13
  context "default_options method" do
14
14
  before :each do
15
15
  T.default_options.clear!
16
+ T.validate_options = false
16
17
  end
17
18
 
18
19
  context "equals version" do
@@ -38,7 +39,7 @@ class TestOptions < Test::Unit::TestCase
38
39
  T.default_options.option = 1
39
40
  T.default_options.option.should == 1
40
41
  end
41
-
42
+
42
43
  it "sets optons after creation" do
43
44
  T.new('test').options.option.should == nil
44
45
  T.default_options.option = 1
@@ -48,6 +49,7 @@ class TestOptions < Test::Unit::TestCase
48
49
 
49
50
  context "options method" do
50
51
  before :each do
52
+ T.validate_options = false
51
53
  T.default_options.clear!
52
54
  @t = T.new('test')
53
55
  end
@@ -68,7 +70,7 @@ class TestOptions < Test::Unit::TestCase
68
70
  @t.options.should == {:test => 1, :test2 => 42, :test3 => 3}
69
71
  end
70
72
  end
71
-
73
+
72
74
  context "clear! method" do
73
75
  it "clears options' options" do
74
76
  @t.options.test = 1
@@ -89,12 +91,12 @@ class TestOptions < Test::Unit::TestCase
89
91
  @t.options.test.should == nil
90
92
  end
91
93
  end
92
-
94
+
93
95
  it "saves options" do
94
96
  @t.options.option = 1
95
97
  @t.options.option.should == 1
96
98
  end
97
-
99
+
98
100
  it "passes options to libtidy" do
99
101
  @t.options.show_body_only = 1
100
102
  @t.clean.should == "test\n"
@@ -102,20 +104,24 @@ class TestOptions < Test::Unit::TestCase
102
104
  end
103
105
 
104
106
  context "with_options proxy class" do
107
+ before :each do
108
+ T.validate_options = false
109
+ end
110
+
105
111
  it "has options method" do
106
112
  T.with_options(:test => 1).options.test.should == 1
107
113
  end
108
-
114
+
109
115
  it "has clear! method" do
110
116
  T.with_options(:test => 1).clear!.options.test.should == nil
111
117
  end
112
-
118
+
113
119
  it "chain methods" do
114
120
  proxy = T.with_options(:test => 1).with_options(:test2 => 2)
115
121
  proxy.options.test.should == 1
116
122
  proxy.options.test2.should == 2
117
123
  end
118
-
124
+
119
125
  it "passes options to object" do
120
126
  T.with_options(:test => 1).new('test').options.test.should == 1
121
127
  end
data/test/test_simple.rb CHANGED
@@ -27,5 +27,22 @@ class TestSimple < Test::Unit::TestCase
27
27
  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"
28
28
  end
29
29
  end
30
+
31
+ # Commented out, because of broken upstream matchy gem :(
32
+ # context "options validation" do
33
+ # it "raises error on invalid option name" do
34
+ # TidyFFI::Tidy.validate_options = true
35
+ # lambda do
36
+ # TidyFFI::Tidy.default_options = {:complete_garbage => true}
37
+ # end.should raise_error(TidyFFI::Tidy::InvalidOptionName)
38
+ # end
39
+ #
40
+ # it "raises error on invalid option value" do
41
+ # TidyFFI::Tidy.validate_options = true
42
+ # lambda do
43
+ # TidyFFI::Tidy.default_options = {:force_output => "utter trash"}
44
+ # end.should raise_error(TidyFFI::Tidy::InvalidOptionValue)
45
+ # end
46
+ # end
30
47
  end
31
48
  end
data/tidy_ffi.gemspec CHANGED
@@ -2,15 +2,15 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{tidy_ffi}
5
- s.version = "0.0.3"
5
+ s.version = "0.0.4"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Eugene Pimenov"]
9
- s.date = %q{2009-03-02}
9
+ s.date = %q{2009-03-14}
10
10
  s.description = %q{Tidy library interface via FFI}
11
11
  s.email = %q{}
12
12
  s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README", "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", "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_options.rb", "test/test_simple.rb", "tidy_ffi.gemspec"]
13
+ s.files = ["CHANGELOG", "LICENSE", "Manifest", "README", "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_options.rb", "test/test_simple.rb", "tidy_ffi.gemspec", "test/test_lowlevel.rb"]
14
14
  s.has_rdoc = true
15
15
  s.homepage = %q{http://github.com/libc/tidy_ffi}
16
16
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Tidy_ffi", "--main", "README"]
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.rubyforge_project = %q{tidy-ffi}
19
19
  s.rubygems_version = %q{1.3.1}
20
20
  s.summary = %q{Tidy library interface via FFI}
21
- s.test_files = ["test/test_helper.rb", "test/test_options.rb", "test/test_simple.rb"]
21
+ s.test_files = ["test/test_helper.rb", "test/test_lowlevel.rb", "test/test_options.rb", "test/test_simple.rb"]
22
22
 
23
23
  if s.respond_to? :specification_version then
24
24
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tidy_ffi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eugene Pimenov
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-03-02 00:00:00 +03:00
12
+ date: 2009-03-14 00:00:00 +03:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -90,5 +90,6 @@ specification_version: 2
90
90
  summary: Tidy library interface via FFI
91
91
  test_files:
92
92
  - test/test_helper.rb
93
+ - test/test_lowlevel.rb
93
94
  - test/test_options.rb
94
95
  - test/test_simple.rb