harvest 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +3 -0
- data/LICENSE +23 -0
- data/README.rdoc +173 -0
- data/Rakefile +44 -0
- data/lib/harvest.rb +35 -0
- data/lib/harvest/base.rb +77 -0
- data/lib/harvest/harvest_resource.rb +15 -0
- data/lib/harvest/plugins/active_resource_inheritable_headers.rb +36 -0
- data/lib/harvest/plugins/toggleable.rb +12 -0
- data/lib/harvest/resources/client.rb +8 -0
- data/lib/harvest/resources/entry.rb +34 -0
- data/lib/harvest/resources/expense.rb +22 -0
- data/lib/harvest/resources/expense_category.rb +7 -0
- data/lib/harvest/resources/person.rb +44 -0
- data/lib/harvest/resources/project.rb +51 -0
- data/lib/harvest/resources/task.rb +8 -0
- data/lib/harvest/resources/task_assignment.rb +27 -0
- data/lib/harvest/resources/user_assignment.rb +27 -0
- data/test/integration/client_integration.rb +17 -0
- data/test/integration/client_teardown.rb +11 -0
- data/test/integration/expense_category_integration.rb +16 -0
- data/test/integration/expense_category_teardown.rb +12 -0
- data/test/integration/harvest_integration_test.rb +47 -0
- data/test/integration/project_integration.rb +19 -0
- data/test/integration/project_teardown.rb +12 -0
- data/test/integration/task_integration.rb +19 -0
- data/test/integration/task_teardown.rb +12 -0
- data/test/test_helper.rb +51 -0
- data/test/unit/base_test.rb +88 -0
- data/test/unit/resources/client_test.rb +82 -0
- data/test/unit/resources/expense_category_test.rb +49 -0
- data/test/unit/resources/expense_test.rb +14 -0
- data/test/unit/resources/person_test.rb +150 -0
- data/test/unit/resources/project_test.rb +154 -0
- data/test/unit/resources/task_assignment_test.rb +72 -0
- data/test/unit/resources/task_test.rb +82 -0
- data/test/unit/resources/user_assignment_test.rb +71 -0
- metadata +111 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "..", "test_helper")
|
2
|
+
|
3
|
+
class ClientTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup_resources
|
6
|
+
@client = {:id => 1, :name => "Widgets&Co"}.to_xml(:root => "client")
|
7
|
+
@clients = [{:id => 1, :name => "Widgets&Co"}].to_xml(:root => "clients")
|
8
|
+
end
|
9
|
+
|
10
|
+
def mock_client_responses
|
11
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
12
|
+
mock.get "/clients.xml", {}, @clients
|
13
|
+
mock.get "/clients/1.xml", {}, @client
|
14
|
+
mock.post "/clients.xml", {}, @client, 201, "Location" => "/clients/5.xml"
|
15
|
+
mock.put "/clients/1.xml", {}, nil, 200
|
16
|
+
mock.delete "/clients/1.xml", {}, nil, 200
|
17
|
+
mock.put "/clients/1/toggle.xml", {}, nil, 200
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "Client actions" do
|
22
|
+
setup do
|
23
|
+
setup_resources
|
24
|
+
mock_client_responses
|
25
|
+
end
|
26
|
+
|
27
|
+
should "get index" do
|
28
|
+
Harvest::Resources::Client.find(:all)
|
29
|
+
expected_request = ActiveResource::Request.new(:get, "/clients.xml")
|
30
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
31
|
+
end
|
32
|
+
|
33
|
+
should "get a single client" do
|
34
|
+
Harvest::Resources::Client.find(1)
|
35
|
+
expected_request = ActiveResource::Request.new(:get, "/clients/1.xml")
|
36
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
37
|
+
end
|
38
|
+
|
39
|
+
should "create a new client" do
|
40
|
+
client = Harvest::Resources::Client.new(:name => "Widgets&Co")
|
41
|
+
client.save
|
42
|
+
expected_request = ActiveResource::Request.new(:post, "/clients.xml")
|
43
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
44
|
+
end
|
45
|
+
|
46
|
+
should "update an existing client" do
|
47
|
+
client = Harvest::Resources::Client.find(1)
|
48
|
+
client.name = "Sprockets & Co"
|
49
|
+
client.save
|
50
|
+
expected_request = ActiveResource::Request.new(:put, "/clients/1.xml")
|
51
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
52
|
+
end
|
53
|
+
|
54
|
+
should "delete an existing client" do
|
55
|
+
Harvest::Resources::Client.delete(1)
|
56
|
+
expected_request = ActiveResource::Request.new(:delete, "/clients/1.xml")
|
57
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
context "Toggling active/inactive status" do
|
63
|
+
setup do
|
64
|
+
setup_resources
|
65
|
+
mock_client_responses
|
66
|
+
end
|
67
|
+
|
68
|
+
should "hit the toggle method" do
|
69
|
+
client = Harvest::Resources::Client.find(1)
|
70
|
+
client.toggle
|
71
|
+
expected_request = ActiveResource::Request.new(:put, "/clients/1/toggle.xml")
|
72
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
73
|
+
end
|
74
|
+
|
75
|
+
should "return 200 Success toggle method" do
|
76
|
+
client = Harvest::Resources::Client.find(1)
|
77
|
+
assert client.toggle.code == 200
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "..", "test_helper")
|
2
|
+
|
3
|
+
class ExpenseCategoryTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup_resources
|
6
|
+
@expense_category = {:id => 1, :name => "Mileage"}.to_xml(:root => "expense_category")
|
7
|
+
@expense_categories = [{:id => 1, :name => "Mileage"}].to_xml(:root => "expense_categories")
|
8
|
+
end
|
9
|
+
|
10
|
+
def mock_responses
|
11
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
12
|
+
mock.get "/expense_categories.xml", {}, @expense_categories
|
13
|
+
mock.post "/expense_categories.xml", {}, @expence_categories, 201, "Location" => "/expense_categories/5.xml"
|
14
|
+
mock.put "/expense_categories/1.xml", {}, nil, 200
|
15
|
+
mock.delete "/expense_categories/1.xml", {}, nil, 200
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "expense_category actions" do
|
20
|
+
setup do
|
21
|
+
setup_resources
|
22
|
+
mock_responses
|
23
|
+
end
|
24
|
+
|
25
|
+
# Note: The api technically supports updates; however, it doesn't support a show and
|
26
|
+
# therefore doesn't work well with activeresource.
|
27
|
+
|
28
|
+
should "get index" do
|
29
|
+
Harvest::Resources::ExpenseCategory.find(:all)
|
30
|
+
expected_request = ActiveResource::Request.new(:get, "/expense_categories.xml")
|
31
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
32
|
+
end
|
33
|
+
|
34
|
+
should "create a new expense_category" do
|
35
|
+
expense_category = Harvest::Resources::ExpenseCategory.new(:name => "Widgets&Co")
|
36
|
+
expense_category.save
|
37
|
+
expected_request = ActiveResource::Request.new(:post, "/expense_categories.xml")
|
38
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
39
|
+
end
|
40
|
+
|
41
|
+
should "delete an existing expense_category" do
|
42
|
+
Harvest::Resources::ExpenseCategory.delete(1)
|
43
|
+
expected_request = ActiveResource::Request.new(:delete, "/expense_categories/1.xml")
|
44
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "..", "test_helper")
|
2
|
+
|
3
|
+
class ExpenseAssignmentTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "ExpenseAssignment class" do
|
6
|
+
|
7
|
+
should "adjust the site setting when the person_id is set" do
|
8
|
+
expense_class = Harvest::Resources::Expense.clone
|
9
|
+
expense_class.person_id = 5
|
10
|
+
assert_equal "http://example.com/people/5", expense_class.site.to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "..", "test_helper")
|
2
|
+
|
3
|
+
class PersonTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup_resources
|
6
|
+
@person_xml = {:id => 1, :name => "Joe Coder"}.to_xml(:root => "person")
|
7
|
+
@people = [{:id => 1, :name => "Joe Coder"}].to_xml(:root => "people")
|
8
|
+
@entry = [{:id => 2, :project_id => 50}].to_xml(:root => "day-entries")
|
9
|
+
@entries = [{:id => 1, :project_id => "25"},
|
10
|
+
{:id => 2, :project_id => 50} ].to_xml(:root => "day-entries")
|
11
|
+
@expenses = [{:id => 1, :project_id => "25"},
|
12
|
+
{:id => 2, :project_id => 50} ].to_xml(:root => "expenses")
|
13
|
+
end
|
14
|
+
|
15
|
+
def mock_responses
|
16
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
17
|
+
mock.get "/people.xml", {}, @people
|
18
|
+
mock.get "/people/1.xml", {}, @person_xml
|
19
|
+
mock.post "/people.xml", {}, @person_xml, 201, "Location" => "/people/5.xml"
|
20
|
+
mock.put "/people/1.xml", {}, nil, 200
|
21
|
+
mock.delete "/people/1.xml", {}, nil, 200
|
22
|
+
mock.put "/people/1/toggle.xml", {}, nil, 200
|
23
|
+
mock.get "/people/1/entries.xml?from=20080101&to=20080131", {}, @entries
|
24
|
+
mock.get "/people/1/entries.xml?from=20080101&project_id=3&to=20080131", {}, @entry
|
25
|
+
mock.get "/people/1/expenses.xml?from=20080101&to=20080131", {}, @expenses
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "People CRUD actions -- " do
|
30
|
+
setup do
|
31
|
+
setup_resources
|
32
|
+
mock_responses
|
33
|
+
end
|
34
|
+
|
35
|
+
should "get index" do
|
36
|
+
Harvest::Resources::Person.find(:all)
|
37
|
+
expected_request = ActiveResource::Request.new(:get, "/people.xml")
|
38
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
39
|
+
end
|
40
|
+
|
41
|
+
should "get a single project" do
|
42
|
+
Harvest::Resources::Person.find(1)
|
43
|
+
expected_request = ActiveResource::Request.new(:get, "/people/1.xml")
|
44
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
45
|
+
end
|
46
|
+
|
47
|
+
should "create a new project" do
|
48
|
+
project = Harvest::Resources::Person.new(:name => "Widgets&Co")
|
49
|
+
project.save
|
50
|
+
expected_request = ActiveResource::Request.new(:post, "/people.xml")
|
51
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
52
|
+
end
|
53
|
+
|
54
|
+
should "update an existing project" do
|
55
|
+
project = Harvest::Resources::Person.find(1)
|
56
|
+
project.name = "Joe Coder"
|
57
|
+
project.save
|
58
|
+
expected_request = ActiveResource::Request.new(:put, "/people/1.xml")
|
59
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
60
|
+
end
|
61
|
+
|
62
|
+
should "delete an existing project" do
|
63
|
+
Harvest::Resources::Person.delete(1)
|
64
|
+
expected_request = ActiveResource::Request.new(:delete, "/people/1.xml")
|
65
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
context "Toggling active/inactive status" do
|
72
|
+
setup do
|
73
|
+
setup_resources
|
74
|
+
mock_responses
|
75
|
+
end
|
76
|
+
|
77
|
+
should "hit the toggle method" do
|
78
|
+
person = Harvest::Resources::Person.find(1)
|
79
|
+
person.toggle
|
80
|
+
expected_request = ActiveResource::Request.new(:put, "/people/1/toggle.xml")
|
81
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
82
|
+
end
|
83
|
+
|
84
|
+
should "return 200 Success toggle method" do
|
85
|
+
person = Harvest::Resources::Person.find(1)
|
86
|
+
assert person.toggle.code == 200
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
context "Getting entries" do
|
92
|
+
setup do
|
93
|
+
setup_resources
|
94
|
+
mock_responses
|
95
|
+
@person = Harvest::Resources::Person.find(1)
|
96
|
+
end
|
97
|
+
|
98
|
+
should "raise an error if start and end are not included" do
|
99
|
+
assert_raises ArgumentError, "Must specify :start and :end as dates." do
|
100
|
+
@person.entries
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
should "raise an error if end date precedes start date" do
|
105
|
+
assert_raises ArgumentError, "Must specify :start and :end as dates." do
|
106
|
+
@person.entries(:from => Time.utc(2007, 1, 1), :to => Time.utc(2006, 1, 1))
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
should "return the project site prefix" do
|
111
|
+
entry_class = Harvest::Resources::Entry.clone
|
112
|
+
entry_class.person_id = @person.id
|
113
|
+
assert_equal "http://example.com/people/1", entry_class.site.to_s
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
context "Reports -- " do
|
119
|
+
setup do
|
120
|
+
setup_resources
|
121
|
+
mock_responses
|
122
|
+
@from = Time.utc(2008, 1, 1)
|
123
|
+
@to = Time.utc(2008, 1, 31)
|
124
|
+
@person = Harvest::Resources::Person.find(1)
|
125
|
+
@request_path = "/people/1/entries.xml?from=20080101&to=20080131"
|
126
|
+
@expense_path = "/people/1/expenses.xml?from=20080101&to=20080131"
|
127
|
+
end
|
128
|
+
|
129
|
+
should "get all entries for a given date range" do
|
130
|
+
@person.entries(:from => @from, :to => @to)
|
131
|
+
expected_request = ActiveResource::Request.new(:get, @request_path)
|
132
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
133
|
+
end
|
134
|
+
|
135
|
+
should "get all entries for the given date range and user" do
|
136
|
+
path = "/people/1/entries.xml?from=20080101&project_id=3&to=20080131"
|
137
|
+
@person.entries(:from => @from, :to => @to, :project_id => 3)
|
138
|
+
expected_request = ActiveResource::Request.new(:get, path)
|
139
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
140
|
+
end
|
141
|
+
|
142
|
+
should "return all expense entries logged by the given user" do
|
143
|
+
@person.expenses(:from => @from, :to => @to)
|
144
|
+
expected_request = ActiveResource::Request.new(:get, @expense_path)
|
145
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "..", "test_helper")
|
2
|
+
|
3
|
+
class ProjectTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup_resources
|
6
|
+
@project_xml = {:id => 1, :name => "Widget Redesign"}.to_xml(:root => "project")
|
7
|
+
@projects = [{:id => 1, :name => "Widget Redesign"}].to_xml(:root => "projects")
|
8
|
+
@entry = [{:id => 2, :project_id => 50}].to_xml(:root => "day-entries")
|
9
|
+
@entries = [{:id => 1, :project_id => "25"},
|
10
|
+
{:id => 2, :project_id => 50}].to_xml(:root => "day-entries")
|
11
|
+
@user_assignment = {:id => 1, :user_id => 5}.to_xml(:root => "user-assignment")
|
12
|
+
end
|
13
|
+
|
14
|
+
def mock_responses
|
15
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
16
|
+
mock.get "/projects.xml", {}, @projects
|
17
|
+
mock.get "/projects/1.xml", {}, @project_xml
|
18
|
+
mock.post "/projects.xml", {}, @project, 201, "Location" => "/projects/5.xml"
|
19
|
+
mock.put "/projects/1.xml", {}, nil, 200
|
20
|
+
mock.delete "/projects/1.xml", {}, nil, 200
|
21
|
+
mock.put "/projects/1/toggle.xml", {}, nil, 200
|
22
|
+
mock.get "/projects/1/entries.xml?from=20080101&to=20080131", {}, @entries
|
23
|
+
mock.get "/projects/1/entries.xml?from=20080101&to=20080131&user_id=3", {}, @entry
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "Project CRUD actions -- " do
|
28
|
+
setup do
|
29
|
+
setup_resources
|
30
|
+
mock_responses
|
31
|
+
end
|
32
|
+
|
33
|
+
should "get index" do
|
34
|
+
Harvest::Resources::Project.find(:all)
|
35
|
+
expected_request = ActiveResource::Request.new(:get, "/projects.xml")
|
36
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
37
|
+
end
|
38
|
+
|
39
|
+
should "get a single project" do
|
40
|
+
Harvest::Resources::Project.find(1)
|
41
|
+
expected_request = ActiveResource::Request.new(:get, "/projects/1.xml")
|
42
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
43
|
+
end
|
44
|
+
|
45
|
+
should "create a new project" do
|
46
|
+
project = Harvest::Resources::Project.new(:name => "Widgets&Co")
|
47
|
+
project.save
|
48
|
+
expected_request = ActiveResource::Request.new(:post, "/projects.xml")
|
49
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
50
|
+
end
|
51
|
+
|
52
|
+
should "update an existing project" do
|
53
|
+
project = Harvest::Resources::Project.find(1)
|
54
|
+
project.name = "Sprockets & Co"
|
55
|
+
project.save
|
56
|
+
expected_request = ActiveResource::Request.new(:put, "/projects/1.xml")
|
57
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
58
|
+
end
|
59
|
+
|
60
|
+
should "delete an existing project" do
|
61
|
+
Harvest::Resources::Project.delete(1)
|
62
|
+
expected_request = ActiveResource::Request.new(:delete, "/projects/1.xml")
|
63
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
context "Toggling active/inactive status" do
|
69
|
+
setup do
|
70
|
+
setup_resources
|
71
|
+
mock_responses
|
72
|
+
end
|
73
|
+
|
74
|
+
should "hit the toggle method" do
|
75
|
+
project = Harvest::Resources::Project.find(1)
|
76
|
+
project.toggle
|
77
|
+
expected_request = ActiveResource::Request.new(:put, "/projects/1/toggle.xml")
|
78
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
79
|
+
end
|
80
|
+
|
81
|
+
should "return 200 Success toggle method" do
|
82
|
+
project = Harvest::Resources::Project.find(1)
|
83
|
+
assert project.toggle.code == 200
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
context "Nesting " do
|
89
|
+
setup do
|
90
|
+
setup_resources
|
91
|
+
mock_responses
|
92
|
+
@project = Harvest::Resources::Project.find(1)
|
93
|
+
end
|
94
|
+
|
95
|
+
should "get user assignments" do
|
96
|
+
end
|
97
|
+
|
98
|
+
should "get task assignments" do
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
context "Listing entries" do
|
104
|
+
setup do
|
105
|
+
setup_resources
|
106
|
+
mock_responses
|
107
|
+
@project = Harvest::Resources::Project.find(1)
|
108
|
+
end
|
109
|
+
|
110
|
+
should "raise an error if start and end are not included" do
|
111
|
+
assert_raises ArgumentError, "Must specify :start and :end as dates." do
|
112
|
+
@project.entries
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
should "raise an error if end date precedes start date" do
|
117
|
+
assert_raises ArgumentError, "Must specify :start and :end as dates." do
|
118
|
+
@project.entries(:from => Time.utc(2007, 1, 1), :to => Time.utc(2006, 1, 1))
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
should "return the project site prefix" do
|
123
|
+
entry_class = Harvest::Resources::Entry.clone
|
124
|
+
entry_class.project_id = @project.id
|
125
|
+
assert_equal "http://example.com/projects/1", entry_class.site.to_s
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
context "Reports -- " do
|
131
|
+
setup do
|
132
|
+
setup_resources
|
133
|
+
mock_responses
|
134
|
+
@from = Time.utc(2008, 1, 1)
|
135
|
+
@to = Time.utc(2008, 1, 31)
|
136
|
+
@project = Harvest::Resources::Project.find(1)
|
137
|
+
@request_path = "/projects/1/entries.xml?from=20080101&to=20080131"
|
138
|
+
end
|
139
|
+
|
140
|
+
should "get all entries for a given date range" do
|
141
|
+
@project.entries(:from => @from, :to => @to)
|
142
|
+
expected_request = ActiveResource::Request.new(:get, @request_path)
|
143
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
144
|
+
end
|
145
|
+
|
146
|
+
should "get all entries for the given date range and user" do
|
147
|
+
@project.entries(:from => @from, :to => @to, :user_id => 3)
|
148
|
+
expected_request = ActiveResource::Request.new(:get, @request_path + "&user_id=3")
|
149
|
+
assert ActiveResource::HttpMock.requests.include?(expected_request)
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|