graft 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -18,8 +18,10 @@ spec = Gem::Specification.new do |s|
18
18
  s.homepage = 'http://sneaq.net/'
19
19
  s.files = %w(README.rdoc Rakefile) + Dir.glob("{lib,test}/**/*")
20
20
 
21
- s.add_dependency('hpricot', '~> 0.6.0')
22
- s.add_dependency('activesupport', '~> 2.0')
21
+ s.add_dependency('hpricot', '>= 0.6.164')
22
+ s.add_dependency('tzinfo', '>= 0.3.12')
23
+ s.add_dependency('builder', '>= 2.1.2')
24
+ s.add_dependency('activesupport', '>= 2.0')
23
25
  end
24
26
 
25
27
  Rake::GemPackageTask.new(spec) do |pkg|
@@ -1,7 +1,13 @@
1
1
  $:.unshift File.dirname(__FILE__)
2
2
 
3
3
  require 'hpricot'
4
+ require 'builder'
5
+ require 'tzinfo'
4
6
  require 'active_support/core_ext/blank'
7
+ require 'active_support/time_with_zone'
8
+ require 'active_support/inflector'
9
+
5
10
 
6
11
  require 'graft/attribute'
7
- require 'graft/model'
12
+ require 'graft/model'
13
+ require 'graft/type'
@@ -5,12 +5,17 @@ module Graft
5
5
 
6
6
  attr_reader :name, :sources
7
7
 
8
- def initialize(name, sources = nil)
8
+ def initialize(name, type = :string, sources = nil)
9
9
  @name = name.to_sym
10
+ @type = type
10
11
 
11
12
  @sources = Array(sources)
12
13
  @sources << @name.to_s if @sources.empty?
13
14
  end
15
+
16
+ def type_class
17
+ "Graft::Type::#{@type.to_s.camelize}".constantize
18
+ end
14
19
 
15
20
  def split(source)
16
21
  location, attribute = source.split('@')
@@ -37,7 +42,8 @@ module Graft
37
42
  node = node_for(document, source)
38
43
  (node.attributes[attribute(source)] || node.inner_text) unless node.nil?
39
44
  end
40
- values.compact.first
45
+
46
+ type_class.new(values.compact.first).value
41
47
  end
42
48
 
43
49
  end
@@ -8,9 +8,16 @@ module Graft
8
8
  end
9
9
 
10
10
  def attribute(name, options = {})
11
- self.attributes << Attribute.new(name, options[:from])
11
+ source = options[:from]
12
+ type = options[:type] || :string
13
+
14
+ self.attributes << Attribute.new(name, type, source)
12
15
  class_eval "attr_accessor :#{name}"
13
16
  end
17
+
18
+ def collection_from(xml, node)
19
+ (Hpricot.XML(xml)/node).map {|n| new n.to_s }
20
+ end
14
21
 
15
22
  end
16
23
 
@@ -36,6 +43,22 @@ module Graft
36
43
  end
37
44
  end
38
45
 
46
+ def to_hash
47
+ self.class.attributes.inject({}) {|h,a| h.merge(a.name.to_s => send(a.name)) }
48
+ end
49
+
50
+ def to_xml(tag_name)
51
+ xml = Builder::XmlMarkup.new
52
+ xml.instruct!
53
+ xml.tag! tag_name do
54
+ to_hash.each do |attribute, value|
55
+ xml.tag! attribute, value
56
+ end
57
+ end
58
+ xml.target!
59
+ end
60
+
61
+
39
62
  end
40
63
 
41
64
  def self.included(other)
@@ -0,0 +1,89 @@
1
+ module Graft
2
+
3
+ # = Type
4
+ #
5
+ class Type
6
+
7
+ class ConversionError < StandardError; end
8
+
9
+ def initialize(source)
10
+ @source = source
11
+ end
12
+
13
+ def convertible?
14
+ true
15
+ end
16
+
17
+ def value
18
+ raise ConversionError unless (@source.blank? || convertible?)
19
+ @source.blank? ? nil : convert
20
+ end
21
+
22
+ # = String
23
+ #
24
+ class String < Type
25
+
26
+ def convert
27
+ @source
28
+ end
29
+
30
+ end
31
+
32
+ # = Boolean
33
+ #
34
+ class Boolean < Type
35
+ def true_values
36
+ ['true', '1']
37
+ end
38
+
39
+ def false_values
40
+ ['false', '0']
41
+ end
42
+
43
+ def convertible?
44
+ (true_values + false_values).include?(@source)
45
+ end
46
+
47
+ def convert
48
+ true_values.include?(@source) ? true : false
49
+ end
50
+ end
51
+
52
+ # = Integer
53
+ #
54
+ class Integer < Type
55
+ def convertible?
56
+ !@source.match(/\d+/).nil?
57
+ end
58
+
59
+ def convert
60
+ @source.to_i
61
+ end
62
+ end
63
+
64
+ # = Time
65
+ #
66
+ class Time < Type
67
+
68
+ def timestamp?
69
+ !@source.match(/^\d+$/).nil?
70
+ end
71
+
72
+ def convert
73
+ timestamp? ? ::Time.at(@source.to_i) : ::Time.parse(@source)
74
+ end
75
+
76
+ end
77
+
78
+ # = Date
79
+ #
80
+ class Date < Type
81
+
82
+ def convert
83
+ ::Date.parse(@source)
84
+ end
85
+
86
+ end
87
+
88
+ end
89
+ end
@@ -3,7 +3,7 @@ module Graft
3
3
 
4
4
  MAJOR = 0
5
5
  MINOR = 1
6
- TINY = 0
6
+ TINY = 1
7
7
 
8
8
  def self.to_s # :nodoc:
9
9
  [MAJOR, MINOR, TINY].join('.')
@@ -4,4 +4,34 @@ $:.reject! { |e| e.include? 'TextMate' }
4
4
  require 'rubygems'
5
5
  require 'throat_punch'
6
6
 
7
- require File.dirname(__FILE__) + '/../lib/graft'
7
+ require File.dirname(__FILE__) + '/../lib/graft'
8
+
9
+ class Test::Unit::TestCase
10
+
11
+ def self.implementation_klass
12
+ class_name = self.to_s.match(/([a-zA-Z]+)Test$/)[1]
13
+ klass = Graft::Type.const_get(class_name)
14
+
15
+ klass
16
+ end
17
+
18
+ def self.should_convert(source, options)
19
+ klass = self.implementation_klass
20
+ target = options[:to]
21
+
22
+ should "be able to convert '#{source}' to #{target}" do
23
+ o = klass.new(source)
24
+ o.value.should == target
25
+ end
26
+ end
27
+
28
+ def self.should_fail_when_converting(source)
29
+ klass = self.implementation_klass
30
+
31
+ should "fail when converting '#{source}'" do
32
+ o = klass.new(source)
33
+ lambda { o.value }.should raise_error(Graft::Type::ConversionError)
34
+ end
35
+ end
36
+
37
+ end
@@ -2,59 +2,64 @@ require File.dirname(__FILE__) + '/../test_helper'
2
2
 
3
3
  module Graft
4
4
  class AttributeTest < Test::Unit::TestCase
5
- describe "An instance of the Attribute class" do
5
+ context "An instance of the Attribute class" do
6
6
 
7
- it "should know the name of the attribute" do
7
+ should "know the name of the attribute" do
8
8
  attr = Attribute.new('foo')
9
9
  attr.name.should == :foo
10
10
  end
11
+
12
+ should "have a default type class" do
13
+ attr = Attribute.new('foo')
14
+ attr.type_class.should == Graft::Type::String
15
+ end
11
16
 
12
- it "should have a default source" do
17
+ should "have a default source" do
13
18
  attr = Attribute.new(:foo)
14
19
  attr.sources.should == ['foo']
15
20
  end
16
21
 
17
- it "should be able to assign multiple sources" do
18
- attr = Attribute.new(:foo, ['foo1', 'foo2'])
22
+ should "be able to assign multiple sources" do
23
+ attr = Attribute.new(:foo, :string, ['foo1', 'foo2'])
19
24
  attr.sources.should == ['foo1', 'foo2']
20
25
  end
21
26
 
22
- it "should pull the location from the source" do
27
+ should "pull the location from the source" do
23
28
  attr = Attribute.new('foo')
24
29
  attr.location('foo').should == 'foo'
25
30
  end
26
31
 
27
- it "should return the location when splitting" do
32
+ should "return the location when splitting" do
28
33
  attr = Attribute.new('foo')
29
34
  attr.split('foo').should == ['foo', nil]
30
35
  end
31
36
 
32
- it "should return the name for the location when splitting if the location isn't specified" do
37
+ should "return the name for the location when splitting if the location isn't specified" do
33
38
  attr = Attribute.new('foo')
34
39
  attr.split('@bar').should == ['foo', 'bar']
35
40
  end
36
41
 
37
- it "should allow the setting of the location information" do
38
- attr = Attribute.new('foo', 'bar')
42
+ should "allow the setting of the location information" do
43
+ attr = Attribute.new('foo', :string, 'bar')
39
44
  attr.sources.should == ['bar']
40
45
  end
41
46
 
42
- it "should allow the setting of the attribute value" do
47
+ should "allow the setting of the attribute value" do
43
48
  attr = Attribute.new('foo')
44
49
  attr.attribute('@bogon').should == 'bogon'
45
50
  end
46
51
 
47
- it "should use the location as the attribute" do
52
+ should "use the location as the attribute" do
48
53
  attr = Attribute.new('foo')
49
54
  attr.attribute('foo').should == 'foo'
50
55
  end
51
56
 
52
- it "should use the attribute for the attribute if specified" do
53
- attr = Attribute.new(:id, '@nsid')
57
+ should "use the attribute for the attribute if specified" do
58
+ attr = Attribute.new(:id, :string, '@nsid')
54
59
  attr.attribute('@nsid').should == 'nsid'
55
60
  end
56
61
 
57
- it "should be able to retrieve the node from the path" do
62
+ should "be able to retrieve the node from the path" do
58
63
  document = Hpricot.XML('<name>Bassdrive</name>')
59
64
  expected = document.at('name')
60
65
 
@@ -62,7 +67,7 @@ module Graft
62
67
  attr.node_for(document, 'name').should == expected
63
68
  end
64
69
 
65
- it "should be able to retrieve the node that contains the specified attribute" do
70
+ should "be able to retrieve the node that contains the specified attribute" do
66
71
  document = Hpricot.XML('<user id="1337" />')
67
72
  expected = document.at('user')
68
73
 
@@ -70,63 +75,84 @@ module Graft
70
75
  attr.node_for(document, '@id').should == expected
71
76
  end
72
77
 
73
- it "should be able to retrieve the node for the specified attribute" do
78
+ should "be able to retrieve the node for the specified attribute" do
74
79
  document = Hpricot.XML('<user nsid="1337" />')
75
80
  expected = document.at('user')
76
81
 
77
- attr = Attribute.new(:id, '@nsid')
82
+ attr = Attribute.new(:id, :string, '@nsid')
78
83
  attr.node_for(document, '@nsid').should == expected
79
84
  end
80
85
 
81
- it "should be able to pull simple values from an XML document" do
86
+ should "be able to pull simple values from an XML document" do
82
87
  document = Hpricot.XML('<name>Bassdrive</name>')
83
88
  attr = Attribute.new(:name)
84
89
  attr.value_from(document).should == 'Bassdrive'
85
90
  end
86
91
 
87
- it "should be able to pull an attribute value from the current XML node" do
92
+ should "be able to pull an attribute value from the current XML node" do
88
93
  document = Hpricot.XML('<user id="1337" />')
89
94
  attr = Attribute.new(:id)
90
95
  attr.value_from(document).should == '1337'
91
96
  end
92
97
 
93
- it "should be able to pull a specific attribute value from the current XML node" do
98
+ should "be able to pull a specific attribute value from the current XML node" do
94
99
  document = Hpricot.XML('<user nsid="1337" />')
95
- attr = Attribute.new(:id, '@nsid')
100
+ attr = Attribute.new(:id, :string, '@nsid')
96
101
  attr.value_from(document).should == '1337'
97
102
  end
98
103
 
99
- it "should be able to pull an attribute value for a node and attribute" do
104
+ should "be able to pull an attribute value for a node and attribute" do
100
105
  document = Hpricot.XML('<station><genre slug="dnb">Drum & Bass</genre></station>')
101
- attr = Attribute.new(:slug, 'station/genre@slug')
106
+ attr = Attribute.new(:slug, :string, 'station/genre@slug')
102
107
  attr.value_from(document).should == 'dnb'
103
108
  end
104
109
 
105
- it "should be able to pull a value from a nested XML node" do
110
+ should "be able to pull a value from a nested XML node" do
106
111
  document = Hpricot.XML('<rsp><user>blip</user></rsp>')
107
112
  attr = Attribute.new(:user)
108
113
  attr.value_from(document).should == 'blip'
109
114
  end
110
115
 
111
- # it "should be able to pull a value from an attribute once it's found a node by another attribute" do
112
- # document = Hpricot.XML('<node type="blip" value="bleep" />')
113
- # attr = Attribute.new(:blip, "node[@type='blip]@value")
114
- #
115
- # attr.value_from(document).should == 'bleep'
116
- # end
117
-
118
- it "should return nil if it cannot find the specified node" do
116
+ should "return nil if it cannot find the specified node" do
119
117
  document = Hpricot.XML('<user id="1" />')
120
- attr = Attribute.new(:photoset, '@nsid')
118
+ attr = Attribute.new(:photoset, :string, '@nsid')
121
119
  attr.value_from(document).should be(nil)
122
120
  end
123
121
 
124
- it "should be able to try a series of nodes to find a value" do
122
+ should "be able to try a series of nodes to find a value" do
125
123
  document = Hpricot.XML('<photoid>123</photoid>')
126
124
 
127
- attr = Attribute.new(:id, ['photo@nsid', 'photoid'])
125
+ attr = Attribute.new(:id, :string, ['photo@nsid', 'photoid'])
128
126
  attr.value_from(document).should == '123'
129
127
  end
128
+
129
+ should "be able to convert an integer value" do
130
+ document = Hpricot.XML('<id>1</id>')
131
+
132
+ attr = Attribute.new(:id, :integer)
133
+ attr.value_from(document).should == 1
134
+ end
135
+
136
+ should "be able to convert a boolean value" do
137
+ document = Hpricot.XML('<active>true</active>')
138
+
139
+ attr = Attribute.new(:active, :boolean)
140
+ attr.value_from(document).should == true
141
+ end
142
+
143
+ should "be able to convert a date value" do
144
+ document = Hpricot.XML('<due_on>2009-08-01</due_on>')
145
+
146
+ attr = Attribute.new(:due_on, :date)
147
+ attr.value_from(document).should == Date.parse('2009-08-01')
148
+ end
149
+
150
+ should "be able to convert a time value" do
151
+ document = Hpricot.XML('<created_at>2009-08-01 00:00:00</created_at>')
152
+
153
+ attr = Attribute.new(:created_at, :time)
154
+ attr.value_from(document).should == Time.parse('2009-08-01 00:00:00')
155
+ end
130
156
 
131
157
  end
132
158
  end
@@ -14,46 +14,113 @@ class ModelWithAttributes
14
14
 
15
15
  end
16
16
 
17
+ class ModelWithAttributeType
18
+ include Graft::Model
19
+
20
+ attribute :id, :type => :integer
21
+
22
+ end
23
+
17
24
  class ModelTest < Test::Unit::TestCase
18
25
 
19
- describe "The EmptyModel class" do
20
- it "should have an empty list of attributes if none are supplied" do
26
+ context "The EmptyModel class" do
27
+ should "have an empty list of attributes if none are supplied" do
21
28
  EmptyModel.attributes.should == []
22
29
  end
30
+
31
+ should "be able to return a collection of XML nodes" do
32
+ xml =<<-XML
33
+ <?xml version="1.0" encoding="UTF-8"?>
34
+ <things>
35
+ <thing><id>1</id></thing>
36
+ <thing><id>2</id></thing>
37
+ </things>
38
+ XML
39
+
40
+ EmptyModel.expects(:new).with('<thing><id>1</id></thing>').returns('model_1')
41
+ EmptyModel.expects(:new).with('<thing><id>2</id></thing>').returns('model_2')
42
+
43
+ collection = EmptyModel.collection_from(xml, 'things/thing')
44
+ collection.should == ['model_1', 'model_2']
45
+ end
46
+
47
+ should "return an empty hash when calling :to_hash" do
48
+ m = EmptyModel.new
49
+ m.to_hash.should == {}
50
+ end
23
51
 
24
52
  end
25
-
26
- describe "The ModelWithAttributes class" do
27
- it "should know the names of all its attributes" do
53
+
54
+ context "The ModelWithAttributes class" do
55
+ should "know the names of all its attributes" do
28
56
  ModelWithAttributes.attributes.map {|a| a.name.to_s }.should == %w(name description rating size)
29
57
  end
58
+
59
+ should "return a hash representation of itself" do
60
+ m = ModelWithAttributes.new
61
+
62
+ m.name = 'name'
63
+ m.description = 'description'
64
+ m.rating = '5'
65
+ m.size = 'large'
66
+
67
+ m.to_hash.should == {
68
+ 'name' => 'name',
69
+ 'description' => 'description',
70
+ 'rating' => '5',
71
+ 'size' => 'large'
72
+ }
73
+
74
+ end
75
+ end
76
+
77
+ context "The ModelWithAttributeType class" do
78
+ should "know that it's attribute is of type :integer" do
79
+ attribute = ModelWithAttributeType.attributes.first
80
+ attribute.type_class.should == Graft::Type::Integer
81
+ end
82
+
83
+ should "be able to generate an XML representation of itself" do
84
+
85
+ m = ModelWithAttributeType.new
86
+ m.id = 1
87
+
88
+ xml = String.new
89
+ xml << '<?xml version="1.0" encoding="UTF-8"?>'
90
+ xml << '<model>'
91
+ xml << '<id>1</id>'
92
+ xml << '</model>'
93
+
94
+ m.to_xml('model').should == xml
95
+
96
+ end
30
97
  end
31
98
 
32
- describe "An instance of the ModelWithAttributes class" do
99
+ context "An instance of the ModelWithAttributes class" do
33
100
 
34
- before { @simple_xml = '<name>Graft</name>' }
101
+ setup { @simple_xml = '<name>Graft</name>' }
35
102
 
36
- it "should have default reader method for :name" do
103
+ should "have default reader method for :name" do
37
104
  ModelWithAttributes.new.respond_to?(:name).should be(true)
38
105
  end
39
106
 
40
- it "should be able to populate its data on initialization" do
107
+ should "be able to populate its data on initialization" do
41
108
  xml = Hpricot.XML(@simple_xml)
42
109
  ModelWithAttributes.new(xml).name.should == 'Graft'
43
110
  end
44
111
 
45
- it "should have a reference to the original document" do
112
+ should "have a reference to the original document" do
46
113
  xml = Hpricot.XML(@simple_xml)
47
114
  ModelWithAttributes.new(xml).document.should == xml
48
115
  end
49
116
 
50
- it "should be able to populate from an XML string" do
117
+ should "be able to populate from an XML string" do
51
118
  ModelWithAttributes.new(@simple_xml).name.should == 'Graft'
52
119
  end
53
120
 
54
121
  context "when populating data from an XML document" do
55
122
 
56
- before do
123
+ setup do
57
124
  xml = <<-XML
58
125
  <name>Graft</name>
59
126
  <desc>A sweet Ruby library</desc>
@@ -66,19 +133,19 @@ class ModelTest < Test::Unit::TestCase
66
133
  @model.populate_from(Hpricot.XML(xml))
67
134
  end
68
135
 
69
- it "should have the correct value for :name" do
136
+ should "have the correct value for :name" do
70
137
  @model.name.should == 'Graft'
71
138
  end
72
139
 
73
- it "should have the correct value for :description" do
140
+ should "have the correct value for :description" do
74
141
  @model.description.should == 'A sweet Ruby library'
75
142
  end
76
143
 
77
- it "should have the correct value for :rating" do
144
+ should "have the correct value for :rating" do
78
145
  @model.rating.should == '100'
79
146
  end
80
147
 
81
- # it "should have the correct value for :size" do
148
+ # should "have the correct value for :size" do
82
149
  # @model.size.should == 'large'
83
150
  # end
84
151
 
@@ -3,8 +3,11 @@ require File.dirname(__FILE__) + '/../test_helper'
3
3
  module Graft
4
4
  class SourceTest < Test::Unit::TestCase
5
5
 
6
- describe "An instance of the Source class" do
6
+ context "An instance of the Source class" do
7
7
 
8
+ should "be defined" do
9
+ true.should == true
10
+ end
8
11
 
9
12
 
10
13
  end
@@ -0,0 +1,63 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ module Graft
4
+
5
+ class StringTest < Test::Unit::TestCase
6
+ context "An instance of the Graft::Type::String class" do
7
+
8
+ should_convert 'a string', :to => 'a string'
9
+ should_convert '', :to => nil
10
+
11
+ end
12
+ end
13
+
14
+ class BooleanTest < Test::Unit::TestCase
15
+ context "An instance of the Graft::Type::Boolean class" do
16
+
17
+ should_convert 'true', :to => true
18
+ should_convert 'false', :to => false
19
+ should_convert '0', :to => false
20
+ should_convert '1', :to => true
21
+ should_convert '', :to => nil
22
+
23
+ should_fail_when_converting 'foo'
24
+
25
+ end
26
+ end
27
+
28
+ class IntegerTest < Test::Unit::TestCase
29
+ context "An instance of the Graft::Type::Integer class" do
30
+
31
+ should_convert '1', :to => 1
32
+ should_convert '', :to => nil
33
+
34
+ should_fail_when_converting 'foo'
35
+
36
+ end
37
+ end
38
+
39
+ class DateTest < Test::Unit::TestCase
40
+
41
+ context "An instance of the Graft::Type::Date class" do
42
+
43
+ should_convert '2008-08-01', :to => Date.parse('2008-08-01')
44
+ should_convert '', :to => nil
45
+
46
+ end
47
+
48
+ end
49
+
50
+ class TimeTest < Test::Unit::TestCase
51
+
52
+ context "An instance of the Graft::Type::Time class" do
53
+
54
+ should_convert '2008-07-28T16:57:10Z', :to => Time.parse('2008-07-28T16:57:10Z')
55
+ should_convert '2008-12-25 18:26:55', :to => Time.parse('2008-12-25 18:26:55')
56
+ should_convert '1230274722', :to => Time.at(1230274722)
57
+ should_convert '', :to => nil
58
+
59
+ end
60
+
61
+ end
62
+
63
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graft
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick Reagan
@@ -18,9 +18,29 @@ dependencies:
18
18
  version_requirement:
19
19
  version_requirements: !ruby/object:Gem::Requirement
20
20
  requirements:
21
- - - ~>
21
+ - - ">="
22
22
  - !ruby/object:Gem::Version
23
- version: 0.6.0
23
+ version: 0.6.164
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: tzinfo
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.3.12
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: builder
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 2.1.2
24
44
  version:
25
45
  - !ruby/object:Gem::Dependency
26
46
  name: activesupport
@@ -28,7 +48,7 @@ dependencies:
28
48
  version_requirement:
29
49
  version_requirements: !ruby/object:Gem::Requirement
30
50
  requirements:
31
- - - ~>
51
+ - - ">="
32
52
  - !ruby/object:Gem::Version
33
53
  version: "2.0"
34
54
  version:
@@ -45,12 +65,14 @@ files:
45
65
  - Rakefile
46
66
  - lib/graft/attribute.rb
47
67
  - lib/graft/model.rb
68
+ - lib/graft/type.rb
48
69
  - lib/graft/version.rb
49
70
  - lib/graft.rb
50
71
  - test/test_helper.rb
51
72
  - test/unit/attribute_test.rb
52
73
  - test/unit/model_test.rb
53
74
  - test/unit/source_test.rb
75
+ - test/unit/type_test.rb
54
76
  has_rdoc: true
55
77
  homepage: http://sneaq.net/
56
78
  licenses: []