confuddle 0.0.1
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/.gitignore +18 -0
- data/.passwd_to_unfuddle.example.yml +7 -0
- data/README.md +40 -0
- data/bin/un +833 -0
- data/bin/un.cmd +1 -0
- data/confuddle.gemspec +22 -0
- data/lib/graft/README.rdoc +138 -0
- data/lib/graft/Rakefile +43 -0
- data/lib/graft/lib/graft/core_ext/hash.rb +9 -0
- data/lib/graft/lib/graft/json.rb +14 -0
- data/lib/graft/lib/graft/json/attribute.rb +18 -0
- data/lib/graft/lib/graft/json/model.rb +28 -0
- data/lib/graft/lib/graft/model.rb +43 -0
- data/lib/graft/lib/graft/version.rb +13 -0
- data/lib/graft/lib/graft/xml.rb +19 -0
- data/lib/graft/lib/graft/xml/attribute.rb +55 -0
- data/lib/graft/lib/graft/xml/model.rb +49 -0
- data/lib/graft/lib/graft/xml/type.rb +91 -0
- data/lib/graft/test/test_helper.rb +38 -0
- data/lib/graft/test/unit/core_ext/hash_test.rb +29 -0
- data/lib/graft/test/unit/json/attribute_test.rb +51 -0
- data/lib/graft/test/unit/json/model_test.rb +86 -0
- data/lib/graft/test/unit/xml/attribute_test.rb +161 -0
- data/lib/graft/test/unit/xml/model_test.rb +173 -0
- data/lib/graft/test/unit/xml/type_test.rb +65 -0
- data/lib/unfuzzle/.gitignore +4 -0
- data/lib/unfuzzle/README.rdoc +129 -0
- data/lib/unfuzzle/Rakefile +39 -0
- data/lib/unfuzzle/lib/unfuzzle.rb +87 -0
- data/lib/unfuzzle/lib/unfuzzle/comment.rb +37 -0
- data/lib/unfuzzle/lib/unfuzzle/component.rb +31 -0
- data/lib/unfuzzle/lib/unfuzzle/milestone.rb +54 -0
- data/lib/unfuzzle/lib/unfuzzle/person.rb +20 -0
- data/lib/unfuzzle/lib/unfuzzle/priority.rb +30 -0
- data/lib/unfuzzle/lib/unfuzzle/project.rb +62 -0
- data/lib/unfuzzle/lib/unfuzzle/request.rb +75 -0
- data/lib/unfuzzle/lib/unfuzzle/response.rb +25 -0
- data/lib/unfuzzle/lib/unfuzzle/severity.rb +31 -0
- data/lib/unfuzzle/lib/unfuzzle/ticket.rb +156 -0
- data/lib/unfuzzle/lib/unfuzzle/ticket_report.rb +29 -0
- data/lib/unfuzzle/lib/unfuzzle/time_entry.rb +75 -0
- data/lib/unfuzzle/lib/unfuzzle/version.rb +13 -0
- data/lib/unfuzzle/test/fixtures/component.xml +8 -0
- data/lib/unfuzzle/test/fixtures/components.xml +17 -0
- data/lib/unfuzzle/test/fixtures/milestone.xml +12 -0
- data/lib/unfuzzle/test/fixtures/milestones.xml +25 -0
- data/lib/unfuzzle/test/fixtures/project.xml +17 -0
- data/lib/unfuzzle/test/fixtures/projects.xml +35 -0
- data/lib/unfuzzle/test/fixtures/severities.xml +24 -0
- data/lib/unfuzzle/test/fixtures/severity.xml +8 -0
- data/lib/unfuzzle/test/fixtures/ticket.xml +25 -0
- data/lib/unfuzzle/test/fixtures/tickets.xml +51 -0
- data/lib/unfuzzle/test/test_helper.rb +60 -0
- data/lib/unfuzzle/test/unit/unfuzzle/component_test.rb +36 -0
- data/lib/unfuzzle/test/unit/unfuzzle/milestone_test.rb +100 -0
- data/lib/unfuzzle/test/unit/unfuzzle/priority_test.rb +25 -0
- data/lib/unfuzzle/test/unit/unfuzzle/project_test.rb +87 -0
- data/lib/unfuzzle/test/unit/unfuzzle/request_test.rb +104 -0
- data/lib/unfuzzle/test/unit/unfuzzle/response_test.rb +37 -0
- data/lib/unfuzzle/test/unit/unfuzzle/severity_test.rb +36 -0
- data/lib/unfuzzle/test/unit/unfuzzle/ticket_test.rb +181 -0
- data/lib/unfuzzle/test/unit/unfuzzle_test.rb +39 -0
- data/lib/unfuzzle/unfuzzle.gemspec +31 -0
- data/lib/version.rb +3 -0
- metadata +176 -0
@@ -0,0 +1,91 @@
|
|
1
|
+
module Graft
|
2
|
+
module Xml
|
3
|
+
|
4
|
+
# = Type
|
5
|
+
#
|
6
|
+
class Type
|
7
|
+
|
8
|
+
class ConversionError < StandardError; end
|
9
|
+
|
10
|
+
def initialize(source)
|
11
|
+
@source = source
|
12
|
+
end
|
13
|
+
|
14
|
+
def convertible?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def value
|
19
|
+
raise ConversionError unless (@source.blank? || convertible?)
|
20
|
+
@source.blank? ? nil : convert
|
21
|
+
end
|
22
|
+
|
23
|
+
# = String
|
24
|
+
#
|
25
|
+
class String < Type
|
26
|
+
|
27
|
+
def convert
|
28
|
+
@source
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
# = Boolean
|
34
|
+
#
|
35
|
+
class Boolean < Type
|
36
|
+
def true_values
|
37
|
+
['true', '1']
|
38
|
+
end
|
39
|
+
|
40
|
+
def false_values
|
41
|
+
['false', '0']
|
42
|
+
end
|
43
|
+
|
44
|
+
def convertible?
|
45
|
+
(true_values + false_values).include?(@source)
|
46
|
+
end
|
47
|
+
|
48
|
+
def convert
|
49
|
+
true_values.include?(@source) ? true : false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# = Integer
|
54
|
+
#
|
55
|
+
class Integer < Type
|
56
|
+
def convertible?
|
57
|
+
!@source.match(/\d+/).nil?
|
58
|
+
end
|
59
|
+
|
60
|
+
def convert
|
61
|
+
@source.to_i
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# = Time
|
66
|
+
#
|
67
|
+
class Time < Type
|
68
|
+
|
69
|
+
def timestamp?
|
70
|
+
!@source.match(/^\d+$/).nil?
|
71
|
+
end
|
72
|
+
|
73
|
+
def convert
|
74
|
+
timestamp? ? ::Time.at(@source.to_i) : ::Time.parse(@source)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
# = Date
|
80
|
+
#
|
81
|
+
class Date < Type
|
82
|
+
|
83
|
+
def convert
|
84
|
+
::Date.parse(@source)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# http://sneaq.net/textmate-wtf
|
2
|
+
$:.reject! { |e| e.include? 'TextMate' }
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'throat_punch'
|
6
|
+
|
7
|
+
require File.dirname(__FILE__) + '/../lib/graft/xml'
|
8
|
+
require File.dirname(__FILE__) + '/../lib/graft/json'
|
9
|
+
|
10
|
+
class Test::Unit::TestCase
|
11
|
+
|
12
|
+
def self.implementation_klass
|
13
|
+
class_name = self.to_s.match(/([a-zA-Z]+)Test$/)[1]
|
14
|
+
klass = Graft::Xml::Type.const_get(class_name)
|
15
|
+
|
16
|
+
klass
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.should_convert(source, options)
|
20
|
+
klass = self.implementation_klass
|
21
|
+
target = options[:to]
|
22
|
+
|
23
|
+
should "be able to convert '#{source}' to #{target}" do
|
24
|
+
o = klass.new(source)
|
25
|
+
o.value.should == target
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.should_fail_when_converting(source)
|
30
|
+
klass = self.implementation_klass
|
31
|
+
|
32
|
+
should "fail when converting '#{source}'" do
|
33
|
+
o = klass.new(source)
|
34
|
+
lambda { o.value }.should raise_error(Graft::Xml::Type::ConversionError)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../test_helper'
|
2
|
+
|
3
|
+
class HashTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "An instance of Hash" do
|
6
|
+
|
7
|
+
should "be able to extract a value using the / operator" do
|
8
|
+
hash = {'key' => 'value'}
|
9
|
+
(hash/'key').should == 'value'
|
10
|
+
end
|
11
|
+
|
12
|
+
should "be able to extract nested values using the / operator" do
|
13
|
+
hash = {'user' => {'name' => 'luser'}}
|
14
|
+
(hash/'user/name').should == 'luser'
|
15
|
+
end
|
16
|
+
|
17
|
+
should "return nil when attempting to extract a value that doesn't exist" do
|
18
|
+
hash = {'user' => {'name' => 'luser'}}
|
19
|
+
(hash/'user/username').should be(nil)
|
20
|
+
end
|
21
|
+
|
22
|
+
should "be able to fetch a value using a symbol" do
|
23
|
+
hash = {'key' => 'value'}
|
24
|
+
(hash/:key).should == 'value'
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../test_helper'
|
2
|
+
|
3
|
+
module Graft
|
4
|
+
module Json
|
5
|
+
class AttributeTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
context "An instance of the Graft::Json::Attribute class" do
|
8
|
+
|
9
|
+
should "know its name" do
|
10
|
+
attr = Graft::Json::Attribute.new(:foo)
|
11
|
+
attr.name.should == :foo
|
12
|
+
end
|
13
|
+
|
14
|
+
should "have a default source" do
|
15
|
+
attr = Graft::Json::Attribute.new(:foo)
|
16
|
+
attr.source.should == 'foo'
|
17
|
+
end
|
18
|
+
|
19
|
+
should "be able to extract a value from a hash" do
|
20
|
+
data = {"first_name" => "Richerd"}
|
21
|
+
|
22
|
+
attr = Graft::Json::Attribute.new(:first_name)
|
23
|
+
attr.value_from(data).should == 'Richerd'
|
24
|
+
end
|
25
|
+
|
26
|
+
should "be able to extract a value from a hash when provided with a symbol for the source" do
|
27
|
+
data = {"firstname" => "Richerd"}
|
28
|
+
|
29
|
+
attr = Graft::Json::Attribute.new(:first_name, :firstname)
|
30
|
+
attr.value_from(data).should == 'Richerd'
|
31
|
+
end
|
32
|
+
|
33
|
+
should "be able to extract a nested value from a JSON string" do
|
34
|
+
data = {"user" => {"first_name" => "Richerd"}}
|
35
|
+
|
36
|
+
attr = Graft::Json::Attribute.new(:first_name, 'user/first_name')
|
37
|
+
attr.value_from(data).should == 'Richerd'
|
38
|
+
end
|
39
|
+
|
40
|
+
should "return nil when the value doesn't exist" do
|
41
|
+
data = {"first_name" => "Richerd"}
|
42
|
+
|
43
|
+
attr = Graft::Json::Attribute.new(:last_name, 'last_name')
|
44
|
+
attr.value_from(data).should be(nil)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../test_helper'
|
2
|
+
|
3
|
+
class EmptyJsonModel
|
4
|
+
include Graft::Json::Model
|
5
|
+
end
|
6
|
+
|
7
|
+
class JsonModelWithAttributes
|
8
|
+
include Graft::Json::Model
|
9
|
+
|
10
|
+
attribute :name
|
11
|
+
attribute :description, :from => 'desc'
|
12
|
+
attribute :rating, :from => 'rsp/rating'
|
13
|
+
end
|
14
|
+
|
15
|
+
class JsonModelTest < Test::Unit::TestCase
|
16
|
+
|
17
|
+
context "The EmptyJsonModel class" do
|
18
|
+
should "have an empty list of attributes if none are supplied" do
|
19
|
+
EmptyJsonModel.attributes.should == []
|
20
|
+
end
|
21
|
+
|
22
|
+
should "be able to return a collection of instances" do
|
23
|
+
json_data = '{"results":[{"name":"one"},{"name":"two"}]}'
|
24
|
+
|
25
|
+
EmptyJsonModel.expects(:new).with({'name' => 'one'}).returns('model_1')
|
26
|
+
EmptyJsonModel.expects(:new).with({'name' => 'two'}).returns('model_2')
|
27
|
+
|
28
|
+
collection = EmptyJsonModel.collection_from(json_data, 'results')
|
29
|
+
collection.should == ['model_1', 'model_2']
|
30
|
+
end
|
31
|
+
|
32
|
+
should "be able to retrieve data from the source JSON string" do
|
33
|
+
json_data = '{"name":"Graft"}'
|
34
|
+
EmptyJsonModel.data_from(json_data).should == {'name' => 'Graft'}
|
35
|
+
end
|
36
|
+
|
37
|
+
should "be able to retrieve data from the source hash data" do
|
38
|
+
data = {'name' => 'Graft'}
|
39
|
+
EmptyJsonModel.data_from(data).should == {'name' => 'Graft'}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "The JsonModelWithAttributes class" do
|
44
|
+
should "know the names of all its attributes" do
|
45
|
+
JsonModelWithAttributes.attributes.map {|a| a.name.to_s }.should == %w(name description rating)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "An instance of the JsonModelWithAttributes class" do
|
50
|
+
|
51
|
+
should "assign attributes upon initialization" do
|
52
|
+
json_data = '{"name":"Graft"}'
|
53
|
+
JsonModelWithAttributes.new(json_data).name.should == 'Graft'
|
54
|
+
end
|
55
|
+
|
56
|
+
should "have a reference to the original source data" do
|
57
|
+
json_data = '{"name":"Graft"}'
|
58
|
+
data = JSON.parse(json_data)
|
59
|
+
|
60
|
+
m = JsonModelWithAttributes.new(json_data)
|
61
|
+
m.source_data.should == data
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when populating from JSON data" do
|
65
|
+
setup do
|
66
|
+
json_data = '{"name":"Graft", "desc":"library", "rsp":{"rating":10}}'
|
67
|
+
@model = JsonModelWithAttributes.new
|
68
|
+
@model.populate_from(json_data)
|
69
|
+
end
|
70
|
+
|
71
|
+
should "have a value for :name" do
|
72
|
+
@model.name.should == 'Graft'
|
73
|
+
end
|
74
|
+
|
75
|
+
should "have a value for :description" do
|
76
|
+
@model.description.should == 'library'
|
77
|
+
end
|
78
|
+
|
79
|
+
should "have a value for :rating" do
|
80
|
+
@model.rating.should == 10
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../test_helper'
|
2
|
+
|
3
|
+
module Graft
|
4
|
+
module Xml
|
5
|
+
class AttributeTest < Test::Unit::TestCase
|
6
|
+
context "An instance of the Graft::Xml::Attribute class" do
|
7
|
+
|
8
|
+
should "know the name of the attribute" do
|
9
|
+
attr = Graft::Xml::Attribute.new('foo')
|
10
|
+
attr.name.should == :foo
|
11
|
+
end
|
12
|
+
|
13
|
+
should "have a default type class" do
|
14
|
+
attr = Graft::Xml::Attribute.new('foo')
|
15
|
+
attr.type_class.should == Graft::Xml::Type::String
|
16
|
+
end
|
17
|
+
|
18
|
+
should "have a default source" do
|
19
|
+
attr = Graft::Xml::Attribute.new(:foo)
|
20
|
+
attr.sources.should == ['foo']
|
21
|
+
end
|
22
|
+
|
23
|
+
should "be able to assign multiple sources" do
|
24
|
+
attr = Graft::Xml::Attribute.new(:foo, :string, ['foo1', 'foo2'])
|
25
|
+
attr.sources.should == ['foo1', 'foo2']
|
26
|
+
end
|
27
|
+
|
28
|
+
should "pull the location from the source" do
|
29
|
+
attr = Graft::Xml::Attribute.new('foo')
|
30
|
+
attr.location('foo').should == 'foo'
|
31
|
+
end
|
32
|
+
|
33
|
+
should "return the location when splitting" do
|
34
|
+
attr = Graft::Xml::Attribute.new('foo')
|
35
|
+
attr.split('foo').should == ['foo', nil]
|
36
|
+
end
|
37
|
+
|
38
|
+
should "return the name for the location when splitting if the location isn't specified" do
|
39
|
+
attr = Graft::Xml::Attribute.new('foo')
|
40
|
+
attr.split('@bar').should == ['foo', 'bar']
|
41
|
+
end
|
42
|
+
|
43
|
+
should "allow the setting of the location information" do
|
44
|
+
attr = Graft::Xml::Attribute.new('foo', :string, 'bar')
|
45
|
+
attr.sources.should == ['bar']
|
46
|
+
end
|
47
|
+
|
48
|
+
should "allow the setting of the attribute value" do
|
49
|
+
attr = Graft::Xml::Attribute.new('foo')
|
50
|
+
attr.attribute('@bogon').should == 'bogon'
|
51
|
+
end
|
52
|
+
|
53
|
+
should "use the location as the attribute" do
|
54
|
+
attr = Graft::Xml::Attribute.new('foo')
|
55
|
+
attr.attribute('foo').should == 'foo'
|
56
|
+
end
|
57
|
+
|
58
|
+
should "use the attribute for the attribute if specified" do
|
59
|
+
attr = Graft::Xml::Attribute.new(:id, :string, '@nsid')
|
60
|
+
attr.attribute('@nsid').should == 'nsid'
|
61
|
+
end
|
62
|
+
|
63
|
+
should "be able to retrieve the node from the path" do
|
64
|
+
document = Hpricot.XML('<name>Bassdrive</name>')
|
65
|
+
expected = document.at('name')
|
66
|
+
|
67
|
+
attr = Graft::Xml::Attribute.new(:name)
|
68
|
+
attr.node_for(document, 'name').should == expected
|
69
|
+
end
|
70
|
+
|
71
|
+
should "be able to retrieve the node that contains the specified attribute" do
|
72
|
+
document = Hpricot.XML('<user id="1337" />')
|
73
|
+
expected = document.at('user')
|
74
|
+
|
75
|
+
attr = Graft::Xml::Attribute.new(:id)
|
76
|
+
attr.node_for(document, '@id').should == expected
|
77
|
+
end
|
78
|
+
|
79
|
+
should "be able to retrieve the node for the specified attribute" do
|
80
|
+
document = Hpricot.XML('<user nsid="1337" />')
|
81
|
+
expected = document.at('user')
|
82
|
+
|
83
|
+
attr = Graft::Xml::Attribute.new(:id, :string, '@nsid')
|
84
|
+
attr.node_for(document, '@nsid').should == expected
|
85
|
+
end
|
86
|
+
|
87
|
+
should "be able to pull simple values from an XML document" do
|
88
|
+
document = Hpricot.XML('<name>Bassdrive</name>')
|
89
|
+
attr = Graft::Xml::Attribute.new(:name)
|
90
|
+
attr.value_from(document).should == 'Bassdrive'
|
91
|
+
end
|
92
|
+
|
93
|
+
should "be able to pull an attribute value from the current XML node" do
|
94
|
+
document = Hpricot.XML('<user id="1337" />')
|
95
|
+
attr = Graft::Xml::Attribute.new(:id)
|
96
|
+
attr.value_from(document).should == '1337'
|
97
|
+
end
|
98
|
+
|
99
|
+
should "be able to pull a specific attribute value from the current XML node" do
|
100
|
+
document = Hpricot.XML('<user nsid="1337" />')
|
101
|
+
attr = Graft::Xml::Attribute.new(:id, :string, '@nsid')
|
102
|
+
attr.value_from(document).should == '1337'
|
103
|
+
end
|
104
|
+
|
105
|
+
should "be able to pull an attribute value for a node and attribute" do
|
106
|
+
document = Hpricot.XML('<station><genre slug="dnb">Drum & Bass</genre></station>')
|
107
|
+
attr = Graft::Xml::Attribute.new(:slug, :string, 'station/genre@slug')
|
108
|
+
attr.value_from(document).should == 'dnb'
|
109
|
+
end
|
110
|
+
|
111
|
+
should "be able to pull a value from a nested XML node" do
|
112
|
+
document = Hpricot.XML('<rsp><user>blip</user></rsp>')
|
113
|
+
attr = Graft::Xml::Attribute.new(:user)
|
114
|
+
attr.value_from(document).should == 'blip'
|
115
|
+
end
|
116
|
+
|
117
|
+
should "return nil if it cannot find the specified node" do
|
118
|
+
document = Hpricot.XML('<user id="1" />')
|
119
|
+
attr = Graft::Xml::Attribute.new(:photoset, :string, '@nsid')
|
120
|
+
attr.value_from(document).should be(nil)
|
121
|
+
end
|
122
|
+
|
123
|
+
should "be able to try a series of nodes to find a value" do
|
124
|
+
document = Hpricot.XML('<photoid>123</photoid>')
|
125
|
+
|
126
|
+
attr = Graft::Xml::Attribute.new(:id, :string, ['photo@nsid', 'photoid'])
|
127
|
+
attr.value_from(document).should == '123'
|
128
|
+
end
|
129
|
+
|
130
|
+
should "be able to convert an integer value" do
|
131
|
+
document = Hpricot.XML('<id>1</id>')
|
132
|
+
|
133
|
+
attr = Graft::Xml::Attribute.new(:id, :integer)
|
134
|
+
attr.value_from(document).should == 1
|
135
|
+
end
|
136
|
+
|
137
|
+
should "be able to convert a boolean value" do
|
138
|
+
document = Hpricot.XML('<active>true</active>')
|
139
|
+
|
140
|
+
attr = Graft::Xml::Attribute.new(:active, :boolean)
|
141
|
+
attr.value_from(document).should == true
|
142
|
+
end
|
143
|
+
|
144
|
+
should "be able to convert a date value" do
|
145
|
+
document = Hpricot.XML('<due_on>2009-08-01</due_on>')
|
146
|
+
|
147
|
+
attr = Graft::Xml::Attribute.new(:due_on, :date)
|
148
|
+
attr.value_from(document).should == Date.parse('2009-08-01')
|
149
|
+
end
|
150
|
+
|
151
|
+
should "be able to convert a time value" do
|
152
|
+
document = Hpricot.XML('<created_at>2009-08-01 00:00:00</created_at>')
|
153
|
+
|
154
|
+
attr = Graft::Xml::Attribute.new(:created_at, :time)
|
155
|
+
attr.value_from(document).should == Time.parse('2009-08-01 00:00:00')
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|