mavenlink 0.0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +10 -3
- data/Gemfile.lock +38 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +69 -0
- data/Rakefile +50 -24
- data/TODO +18 -0
- data/VERSION +1 -0
- data/lib/mavenlink.rb +2 -134
- data/lib/mavenlink/base.rb +184 -0
- data/lib/mavenlink/client.rb +202 -96
- data/mavenlink.gemspec +85 -19
- data/spec/mavenlink/base_spec.rb +223 -0
- data/spec/mavenlink/client_spec.rb +183 -0
- data/spec/spec_helper.rb +13 -25
- metadata +243 -232
- checksums.yaml +0 -7
- data/.gitignore +0 -11
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/README.md +0 -241
- data/bin/mavenlink-console +0 -18
- data/doc/ml_logo_lb-primary.png +0 -0
- data/lib/config/specification.yml +0 -1665
- data/lib/mavenlink/account_invitation.rb +0 -4
- data/lib/mavenlink/account_membership.rb +0 -4
- data/lib/mavenlink/additional_item.rb +0 -4
- data/lib/mavenlink/assignment.rb +0 -5
- data/lib/mavenlink/attachment.rb +0 -11
- data/lib/mavenlink/backup_approver_association.rb +0 -4
- data/lib/mavenlink/concerns/custom_fieldable.rb +0 -9
- data/lib/mavenlink/concerns/indestructible.rb +0 -11
- data/lib/mavenlink/concerns/locked_record.rb +0 -30
- data/lib/mavenlink/cost_rate.rb +0 -4
- data/lib/mavenlink/custom_field.rb +0 -4
- data/lib/mavenlink/custom_field_choice.rb +0 -5
- data/lib/mavenlink/custom_field_value.rb +0 -4
- data/lib/mavenlink/errors.rb +0 -47
- data/lib/mavenlink/expense.rb +0 -4
- data/lib/mavenlink/expense_category.rb +0 -4
- data/lib/mavenlink/expense_report_submission.rb +0 -7
- data/lib/mavenlink/external_payment.rb +0 -4
- data/lib/mavenlink/external_reference.rb +0 -24
- data/lib/mavenlink/fixed_fee_item.rb +0 -4
- data/lib/mavenlink/holiday.rb +0 -4
- data/lib/mavenlink/holiday_calendar.rb +0 -4
- data/lib/mavenlink/holiday_calendar_association.rb +0 -4
- data/lib/mavenlink/holiday_calendar_membership.rb +0 -4
- data/lib/mavenlink/invoice.rb +0 -4
- data/lib/mavenlink/logger.rb +0 -62
- data/lib/mavenlink/model.rb +0 -279
- data/lib/mavenlink/organization.rb +0 -4
- data/lib/mavenlink/organization_membership.rb +0 -4
- data/lib/mavenlink/participation.rb +0 -4
- data/lib/mavenlink/post.rb +0 -4
- data/lib/mavenlink/project_template.rb +0 -4
- data/lib/mavenlink/project_template_assignment.rb +0 -4
- data/lib/mavenlink/railtie.rb +0 -7
- data/lib/mavenlink/rate_card.rb +0 -4
- data/lib/mavenlink/rate_card_role.rb +0 -4
- data/lib/mavenlink/rate_card_set.rb +0 -4
- data/lib/mavenlink/rate_card_set_version.rb +0 -30
- data/lib/mavenlink/rate_card_version.rb +0 -4
- data/lib/mavenlink/request.rb +0 -241
- data/lib/mavenlink/resolution.rb +0 -4
- data/lib/mavenlink/response.rb +0 -22
- data/lib/mavenlink/role.rb +0 -5
- data/lib/mavenlink/settings.rb +0 -11
- data/lib/mavenlink/skill.rb +0 -4
- data/lib/mavenlink/skill_category.rb +0 -4
- data/lib/mavenlink/skill_membership.rb +0 -4
- data/lib/mavenlink/specificators/association.rb +0 -13
- data/lib/mavenlink/specificators/attribute.rb +0 -13
- data/lib/mavenlink/specificators/base.rb +0 -24
- data/lib/mavenlink/specificators/validation.rb +0 -27
- data/lib/mavenlink/status_report.rb +0 -4
- data/lib/mavenlink/story.rb +0 -8
- data/lib/mavenlink/story_allocation_day.rb +0 -5
- data/lib/mavenlink/story_dependency.rb +0 -5
- data/lib/mavenlink/story_task.rb +0 -5
- data/lib/mavenlink/tag.rb +0 -5
- data/lib/mavenlink/time_adjustment.rb +0 -4
- data/lib/mavenlink/time_entry.rb +0 -4
- data/lib/mavenlink/time_off_entry.rb +0 -4
- data/lib/mavenlink/timesheet_submission.rb +0 -4
- data/lib/mavenlink/user.rb +0 -5
- data/lib/mavenlink/vendor.rb +0 -4
- data/lib/mavenlink/workspace.rb +0 -21
- data/lib/mavenlink/workspace_group.rb +0 -5
- data/lib/mavenlink/workspace_invoice_preference.rb +0 -4
- data/lib/mavenlink/workweek.rb +0 -4
- data/lib/mavenlink/workweek_membership.rb +0 -4
- data/spec/lib/mavenlink/account_membership_spec.rb +0 -8
- data/spec/lib/mavenlink/assignment_spec.rb +0 -17
- data/spec/lib/mavenlink/attachment_spec.rb +0 -30
- data/spec/lib/mavenlink/backup_approver_association_spec.rb +0 -9
- data/spec/lib/mavenlink/client_spec.rb +0 -187
- data/spec/lib/mavenlink/concerns/indestructible_spec.rb +0 -13
- data/spec/lib/mavenlink/concerns/locked_record_spec.rb +0 -28
- data/spec/lib/mavenlink/cost_rate_spec.rb +0 -9
- data/spec/lib/mavenlink/custom_field_value_spec.rb +0 -10
- data/spec/lib/mavenlink/expense_report_submission_spec.rb +0 -16
- data/spec/lib/mavenlink/expense_spec.rb +0 -23
- data/spec/lib/mavenlink/external_references_spec.rb +0 -144
- data/spec/lib/mavenlink/holiday_calendar_association_spec.rb +0 -8
- data/spec/lib/mavenlink/holiday_calendar_membership_spec.rb +0 -8
- data/spec/lib/mavenlink/holiday_spec.rb +0 -7
- data/spec/lib/mavenlink/invalid_request_error_spec.rb +0 -9
- data/spec/lib/mavenlink/invoice_spec.rb +0 -176
- data/spec/lib/mavenlink/model_spec.rb +0 -439
- data/spec/lib/mavenlink/post_spec.rb +0 -23
- data/spec/lib/mavenlink/rate_card_set_version_spec.rb +0 -119
- data/spec/lib/mavenlink/record_invalid_error_spec.rb +0 -16
- data/spec/lib/mavenlink/record_not_found_error_spec.rb +0 -9
- data/spec/lib/mavenlink/request_spec.rb +0 -381
- data/spec/lib/mavenlink/response_spec.rb +0 -50
- data/spec/lib/mavenlink/role_spec.rb +0 -9
- data/spec/lib/mavenlink/settings_spec.rb +0 -23
- data/spec/lib/mavenlink/skill_category_spec.rb +0 -7
- data/spec/lib/mavenlink/skill_membership_spec.rb +0 -9
- data/spec/lib/mavenlink/skill_spec.rb +0 -8
- data/spec/lib/mavenlink/specificators/association_spec.rb +0 -25
- data/spec/lib/mavenlink/specificators/attribute_spec.rb +0 -25
- data/spec/lib/mavenlink/specificators/validation_spec.rb +0 -39
- data/spec/lib/mavenlink/story_allocation_day_spec.rb +0 -64
- data/spec/lib/mavenlink/story_dependency_spec.rb +0 -16
- data/spec/lib/mavenlink/story_spec.rb +0 -69
- data/spec/lib/mavenlink/time_adjustment_spec.rb +0 -13
- data/spec/lib/mavenlink/time_entry_spec.rb +0 -43
- data/spec/lib/mavenlink/time_off_entry_spec.rb +0 -9
- data/spec/lib/mavenlink/user_spec.rb +0 -138
- data/spec/lib/mavenlink/workspace_group_spec.rb +0 -25
- data/spec/lib/mavenlink/workspace_spec.rb +0 -431
- data/spec/lib/mavenlink_spec.rb +0 -43
- data/spec/support/shared_examples.rb +0 -148
data/lib/mavenlink/client.rb
CHANGED
@@ -1,116 +1,222 @@
|
|
1
1
|
module Mavenlink
|
2
|
-
class
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
def initialize(settings = Mavenlink.default_settings)
|
7
|
-
@settings = settings
|
8
|
-
@oauth_token = settings[:oauth_token] or raise ArgumentError, 'OAuth token is not set'
|
9
|
-
@endpoint = settings[:endpoint] || ENDPOINT
|
10
|
-
@use_json = settings[:use_json]
|
11
|
-
if settings.key?(:user_agent_override)
|
12
|
-
@user_agent_override = settings[:user_agent_override]
|
13
|
-
end
|
2
|
+
class Base
|
3
|
+
base_uri ENV['TESTING'] ? 'https://mavenlink.local/api/v0' : 'https://www.mavenlink.com/api/v0'
|
4
|
+
debug ENV['DEBUG'] || false
|
5
|
+
end
|
14
6
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
7
|
+
# Wrapping objects that have no API endpoints yet
|
8
|
+
|
9
|
+
class User < Base
|
10
|
+
request_path "/not_available_yet"
|
11
|
+
class_name :user
|
12
|
+
end
|
13
|
+
|
14
|
+
class Asset < Base
|
15
|
+
request_path "/not_available_yet"
|
16
|
+
class_name :asset
|
17
|
+
end
|
18
|
+
|
19
|
+
# Normal API objects
|
20
|
+
|
21
|
+
class Client < Base
|
22
|
+
def initialize(user_id, token)
|
23
|
+
super({}, :basic_auth => {:username => user_id, :password => token})
|
24
24
|
end
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
Faraday.new(connection_options) do |builder|
|
29
|
-
if @use_json
|
30
|
-
builder.headers['Content-Type'] = 'application/json'
|
31
|
-
else
|
32
|
-
builder.use Faraday::Request::UrlEncoded
|
33
|
-
end
|
34
|
-
builder.adapter(*Mavenlink.adapter)
|
35
|
-
end
|
26
|
+
def workspaces(options = {})
|
27
|
+
fetch('workspaces', Workspace, options)
|
36
28
|
end
|
37
29
|
|
38
|
-
|
39
|
-
|
40
|
-
# @param [Hash] arguments
|
41
|
-
def get(path, arguments = {})
|
42
|
-
Mavenlink.logger.note "Started GET /#{path} with #{arguments.inspect}"
|
43
|
-
parse_request(connection.get(path, arguments).body)
|
30
|
+
def workspace(workspace_id)
|
31
|
+
fetch("workspaces/#{workspace_id}", Workspace, {})
|
44
32
|
end
|
45
33
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
def post(path, arguments = {})
|
50
|
-
Mavenlink.logger.note "Started POST /#{path} with #{arguments.inspect}"
|
51
|
-
parse_request(connection.post(path, arguments).body)
|
34
|
+
|
35
|
+
def time_entries(options = {})
|
36
|
+
fetch('time_entries', TimeEntry, options, lambda { |time_entry| {:workspace_id => time_entry['workspace_id']} })
|
52
37
|
end
|
53
38
|
|
54
|
-
|
55
|
-
|
56
|
-
# @param [Hash] arguments
|
57
|
-
def put(path, arguments = {})
|
58
|
-
Mavenlink.logger.note "Started PUT /#{path} with #{arguments.inspect}"
|
59
|
-
parse_request(connection.put(path, arguments).body)
|
39
|
+
def time_entry(time_entry_id)
|
40
|
+
fetch("time_entries/#{time_entry_id}", TimeEntry, {}, lambda { |time_entry| {:workspace_id => time_entry['workspace_id']} })
|
60
41
|
end
|
61
42
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
def delete(path, arguments = {})
|
66
|
-
Mavenlink.logger.note "Started DELETE /#{path} with #{arguments.inspect}"
|
67
|
-
parse_request(connection.delete(path, arguments).body)
|
43
|
+
|
44
|
+
def expenses(options = {})
|
45
|
+
fetch('expenses', Expense, options, lambda { |expense| {:workspace_id => expense['workspace_id']} })
|
68
46
|
end
|
69
47
|
|
70
|
-
|
48
|
+
def expense(expense_id)
|
49
|
+
fetch("expenses/#{expense_id}", Expense, {}, lambda { |expense| {:workspace_id => expense['workspace_id']} })
|
50
|
+
end
|
71
51
|
|
72
|
-
attr_reader :oauth_token, :endpoint
|
73
52
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
user_agent = "#{@user_agent_override}"
|
78
|
-
else
|
79
|
-
user_agent = "Mavenlink Ruby Gem"
|
80
|
-
end
|
81
|
-
{
|
82
|
-
headers: { 'Accept' => "application/json",
|
83
|
-
'User-Agent' => "#{user_agent}",
|
84
|
-
'Authorization' => "Bearer #{oauth_token}" },
|
85
|
-
ssl: { verify: false },
|
86
|
-
url: endpoint
|
87
|
-
}.freeze
|
88
|
-
end
|
89
|
-
|
90
|
-
def parse_request(response)
|
91
|
-
if response.present?
|
92
|
-
parsed_response = JSON.parse(response)
|
93
|
-
else
|
94
|
-
return
|
95
|
-
end
|
53
|
+
def invoices(options = {})
|
54
|
+
fetch('invoices', Invoice, options, lambda { |invoice| {:workspace_id => invoice['workspace_id']} })
|
55
|
+
end
|
96
56
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
57
|
+
def invoice(invoice_id)
|
58
|
+
fetch("invoices/#{invoice_id}", Invoice, {}, lambda { |invoice| {:workspace_id => invoice['workspace_id']} })
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def events(options = {})
|
63
|
+
fetch('events', Event, options)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Workspace < Base
|
68
|
+
request_path "/workspaces/:id"
|
69
|
+
class_name :workspace
|
70
|
+
|
71
|
+
def posts(options = {})
|
72
|
+
fetch('posts', Post, options, :workspace_id => id)
|
73
|
+
end
|
74
|
+
|
75
|
+
def post(post_id, options = {})
|
76
|
+
fetch("posts/#{post_id}", Post, options, :workspace_id => id)
|
77
|
+
end
|
78
|
+
|
79
|
+
def create_post(options)
|
80
|
+
build("posts", Post, options, :workspace_id => id)
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
def time_entries(options = {})
|
85
|
+
fetch("time_entries", TimeEntry, options, :workspace_id => id)
|
86
|
+
end
|
87
|
+
|
88
|
+
def time_entry(time_entry_id, options = {})
|
89
|
+
fetch("time_entries/#{time_entry_id}", TimeEntry, options, :workspace_id => id)
|
90
|
+
end
|
91
|
+
|
92
|
+
def create_time_entry(options)
|
93
|
+
build("time_entries", TimeEntry, options, :workspace_id => id)
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
def expenses(options = {})
|
98
|
+
fetch("expenses", Expense, options, :workspace_id => id)
|
99
|
+
end
|
100
|
+
|
101
|
+
def expense(expense_id, options = {})
|
102
|
+
fetch("expenses/#{expense_id}", Expense, options, :workspace_id => id)
|
103
|
+
end
|
104
|
+
|
105
|
+
def create_expense(options)
|
106
|
+
build("expenses", Expense, options, :workspace_id => id)
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
def invoices(options = {})
|
111
|
+
fetch("invoices", Invoice, options, :workspace_id => id)
|
112
|
+
end
|
113
|
+
|
114
|
+
def invoice(invoice_id, options = {})
|
115
|
+
fetch("invoice/#{invoice_id}", Invoice, options, :workspace_id => id)
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
def stories(options = {})
|
120
|
+
fetch("stories", Story, options, :workspace_id => id)
|
114
121
|
end
|
122
|
+
|
123
|
+
def story(story_id, options = {})
|
124
|
+
fetch("stories/#{story_id}", Story, options, :workspace_id => id)
|
125
|
+
end
|
126
|
+
|
127
|
+
def create_story(options)
|
128
|
+
build("stories", Story, options, :workspace_id => id)
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
def participants(options = {})
|
133
|
+
fetch("participants", Participant, options, :workspace_id => id)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class Participant < Base
|
138
|
+
request_path "/workspaces/:workspace_id/participants/:id"
|
139
|
+
class_name :participant
|
140
|
+
end
|
141
|
+
|
142
|
+
class Post < Base
|
143
|
+
request_path "/workspaces/:workspace_id/posts/:id"
|
144
|
+
contains :user => User,
|
145
|
+
:google_documents => Asset,
|
146
|
+
:assets => Asset,
|
147
|
+
:replies => lambda {|parent, child| { :class => Post, :path_params => { :workspace_id => child['workspace_id'] } } }
|
148
|
+
class_name :post
|
149
|
+
|
150
|
+
def story(options = {})
|
151
|
+
fetch("../../stories/#{json['story_id']}", Story, options, :workspace_id => workspace_id) if json['story_id']
|
152
|
+
end
|
153
|
+
|
154
|
+
def workspace(options = {})
|
155
|
+
fetch("../..", Workspace, options)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
class TimeEntry < Base
|
160
|
+
request_path "/workspaces/:workspace_id/time_entries/:id"
|
161
|
+
class_name :time_entry
|
162
|
+
|
163
|
+
def story(options = {})
|
164
|
+
fetch("../../stories/#{json['story_id']}", Story, options, :workspace_id => workspace_id) if json['story_id']
|
165
|
+
end
|
166
|
+
|
167
|
+
def workspace(options = {})
|
168
|
+
fetch("../..", Workspace, options)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
class Expense < Base
|
173
|
+
request_path "/workspaces/:workspace_id/expenses/:id"
|
174
|
+
class_name :expense
|
175
|
+
|
176
|
+
def workspace(options = {})
|
177
|
+
fetch("../..", Workspace, options)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
class AdditionalItem < Base
|
182
|
+
request_path "/not_available_yet"
|
183
|
+
class_name :additional_item
|
184
|
+
end
|
185
|
+
|
186
|
+
class Invoice < Base
|
187
|
+
request_path "/workspaces/:workspace_id/invoices/:id"
|
188
|
+
class_name :invoice
|
189
|
+
contains :time_entries => lambda { |time_entry, json| { :class => TimeEntry, :path_params => { :id => json['id'], :workspace_id => json['workspace_id'] } } },
|
190
|
+
:expenses => lambda { |expense, json| { :class => Expense, :path_params => { :id => json['id'], :workspace_id => json['workspace_id'] } } },
|
191
|
+
:additional_items => AdditionalItem
|
192
|
+
|
193
|
+
def workspace(options = {})
|
194
|
+
fetch("../..", Workspace, options)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
class Story < Base
|
199
|
+
request_path "/workspaces/:workspace_id/stories/:id"
|
200
|
+
contains :creator => User, :assignee => User
|
201
|
+
class_name :story
|
202
|
+
|
203
|
+
def workspace(options = {})
|
204
|
+
fetch("../..", Workspace, options)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
class Event < Base
|
209
|
+
request_path "/events/:id"
|
210
|
+
contains :subject => lambda { |event, json|
|
211
|
+
case event.subject_type
|
212
|
+
when "Post"
|
213
|
+
{ :class => Post, :path_params => { :workspace_id => json['workspace_id'] } }
|
214
|
+
when "Story"
|
215
|
+
{ :class => Story, :path_params => { :workspace_id => json['workspace_id'] } }
|
216
|
+
else
|
217
|
+
raise "Unknown event subject type: #{event.subject_type}"
|
218
|
+
end
|
219
|
+
}
|
220
|
+
class_name :event
|
115
221
|
end
|
116
222
|
end
|
data/mavenlink.gemspec
CHANGED
@@ -1,29 +1,95 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
1
4
|
# -*- encoding: utf-8 -*-
|
2
5
|
|
3
6
|
Gem::Specification.new do |s|
|
4
7
|
s.name = %q{mavenlink}
|
5
|
-
s.version = "0.0
|
6
|
-
|
7
|
-
s.date = %q{2014-04-01}
|
8
|
-
s.authors = ["Mavenlink"]
|
9
|
-
s.email = %q{opensource@mavenlink.com}
|
10
|
-
s.homepage = %q{http://github.com/mavenlink/mavenlink_gem}
|
8
|
+
s.version = "0.2.0"
|
11
9
|
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Roger Neel", "Andrew Cantino"]
|
12
|
+
s.date = %q{2011-06-30}
|
13
|
+
s.description = %q{This is a Ruby API client for Mavenlink. Mavenlink's project collaboration suite allows you to manage your business relationships, share files, and track project activity online from anywhere in the world. Within a project workspace in Mavenlink, you can agree on budget & schedule, track time, send invoices, get paid via PayPal, and complete work.}
|
14
|
+
s.email = %q{support@mavenlink.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc",
|
18
|
+
"TODO"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
".document",
|
22
|
+
".rspec",
|
23
|
+
"Gemfile",
|
24
|
+
"Gemfile.lock",
|
25
|
+
"LICENSE.txt",
|
26
|
+
"README.rdoc",
|
27
|
+
"Rakefile",
|
28
|
+
"TODO",
|
29
|
+
"VERSION",
|
30
|
+
"lib/mavenlink.rb",
|
31
|
+
"lib/mavenlink/base.rb",
|
32
|
+
"lib/mavenlink/client.rb",
|
33
|
+
"mavenlink.gemspec",
|
34
|
+
"spec/mavenlink/base_spec.rb",
|
35
|
+
"spec/mavenlink/client_spec.rb",
|
36
|
+
"spec/spec_helper.rb"
|
37
|
+
]
|
38
|
+
s.homepage = %q{http://github.com/mavenlink/mavenlink_ruby_api}
|
12
39
|
s.licenses = ["MIT"]
|
13
|
-
|
14
|
-
s.files = `git ls-files`.split("\n")
|
15
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
40
|
s.require_paths = ["lib"]
|
17
|
-
s.
|
41
|
+
s.rubygems_version = %q{1.3.7}
|
42
|
+
s.summary = %q{Ruby client for Mavenlink's API}
|
43
|
+
s.test_files = [
|
44
|
+
"spec/mavenlink/base_spec.rb",
|
45
|
+
"spec/mavenlink/client_spec.rb",
|
46
|
+
"spec/spec_helper.rb"
|
47
|
+
]
|
18
48
|
|
19
|
-
s.
|
20
|
-
|
49
|
+
if s.respond_to? :specification_version then
|
50
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
51
|
+
s.specification_version = 3
|
21
52
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
53
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
54
|
+
s.add_runtime_dependency(%q<httparty>, ["= 0.7.4"])
|
55
|
+
s.add_runtime_dependency(%q<webmock>, ["~> 1.6.2"])
|
56
|
+
s.add_runtime_dependency(%q<rspec>, ["~> 2.3.0"])
|
57
|
+
s.add_runtime_dependency(%q<jeweler>, ["~> 1.5.2"])
|
58
|
+
s.add_runtime_dependency(%q<rr>, ["~> 1.0.2"])
|
59
|
+
s.add_runtime_dependency(%q<json>, ["~> 1.5.1"])
|
60
|
+
s.add_runtime_dependency(%q<httparty>, ["= 0.7.4"])
|
61
|
+
s.add_development_dependency(%q<webmock>, ["~> 1.6.2"])
|
62
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
|
63
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
|
64
|
+
s.add_development_dependency(%q<rr>, ["~> 1.0.2"])
|
65
|
+
s.add_development_dependency(%q<json>, ["~> 1.5.1"])
|
66
|
+
else
|
67
|
+
s.add_dependency(%q<httparty>, ["= 0.7.4"])
|
68
|
+
s.add_dependency(%q<webmock>, ["~> 1.6.2"])
|
69
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
70
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
71
|
+
s.add_dependency(%q<rr>, ["~> 1.0.2"])
|
72
|
+
s.add_dependency(%q<json>, ["~> 1.5.1"])
|
73
|
+
s.add_dependency(%q<httparty>, ["= 0.7.4"])
|
74
|
+
s.add_dependency(%q<webmock>, ["~> 1.6.2"])
|
75
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
76
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
77
|
+
s.add_dependency(%q<rr>, ["~> 1.0.2"])
|
78
|
+
s.add_dependency(%q<json>, ["~> 1.5.1"])
|
79
|
+
end
|
80
|
+
else
|
81
|
+
s.add_dependency(%q<httparty>, ["= 0.7.4"])
|
82
|
+
s.add_dependency(%q<webmock>, ["~> 1.6.2"])
|
83
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
84
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
85
|
+
s.add_dependency(%q<rr>, ["~> 1.0.2"])
|
86
|
+
s.add_dependency(%q<json>, ["~> 1.5.1"])
|
87
|
+
s.add_dependency(%q<httparty>, ["= 0.7.4"])
|
88
|
+
s.add_dependency(%q<webmock>, ["~> 1.6.2"])
|
89
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
90
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
91
|
+
s.add_dependency(%q<rr>, ["~> 1.0.2"])
|
92
|
+
s.add_dependency(%q<json>, ["~> 1.5.1"])
|
93
|
+
end
|
29
94
|
end
|
95
|
+
|
@@ -0,0 +1,223 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
describe Mavenlink::Base do
|
4
|
+
describe "request_path and join_paths" do
|
5
|
+
class Example < Mavenlink::Base
|
6
|
+
request_path "/:something/blah/foo/:id"
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "request_path" do
|
10
|
+
it "should interpolate id and any path_params into the request_path string" do
|
11
|
+
Example.new({ 'id' => 2 }, :path_params => { :something => "hello" }).request_path.should == "/hello/blah/foo/2"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "join_paths" do
|
16
|
+
it "should combine paths, respecting leading ..'s" do
|
17
|
+
Example.new({}).join_paths("/hello/world", "").should == "/hello/world"
|
18
|
+
Example.new({}).join_paths("", "hi").should == "/hi"
|
19
|
+
Example.new({}).join_paths("", "/hi").should == "/hi"
|
20
|
+
Example.new({}).join_paths("/hello/world", "../there").should == "/hello/there"
|
21
|
+
Example.new({}).join_paths("/", "there").should == "/there"
|
22
|
+
Example.new({}).join_paths("/hello/world", "/again").should == "/hello/world/again"
|
23
|
+
Example.new({}).join_paths("/hello/world/", "/again").should == "/hello/world/again"
|
24
|
+
Example.new({}).join_paths("/hello/world/", "again").should == "/hello/world/again"
|
25
|
+
Example.new({}).join_paths("/hello/world", "../../../there").should == "/there"
|
26
|
+
Example.new({}).join_paths("hi", "there").should == "/hi/there"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "example usage with a test client and some widgets with many prongs" do
|
32
|
+
class TestBase < Mavenlink::Base
|
33
|
+
base_uri 'http://www.example.com/api/v0'
|
34
|
+
debug false
|
35
|
+
end
|
36
|
+
|
37
|
+
class TestClient < TestBase
|
38
|
+
def initialize(username, password)
|
39
|
+
super({}, :basic_auth => { :username => username, :password => password })
|
40
|
+
end
|
41
|
+
|
42
|
+
def widgets(options = {})
|
43
|
+
fetch('widgets', Widget, options)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class User < TestBase; end
|
48
|
+
|
49
|
+
class Prong < TestBase
|
50
|
+
request_path "/widgets/:widget_id/prongs/:id"
|
51
|
+
class_name "prong"
|
52
|
+
end
|
53
|
+
|
54
|
+
class Widget < TestBase
|
55
|
+
request_path "/widgets/:id"
|
56
|
+
contains :owners => User
|
57
|
+
class_name "widget"
|
58
|
+
|
59
|
+
def prongs(options = {})
|
60
|
+
fetch("prongs", Prong, options, :widget_id => id)
|
61
|
+
end
|
62
|
+
|
63
|
+
def prong(prong_id)
|
64
|
+
fetch("prongs/#{prong_id}", Prong, {}, :widget_id => id)
|
65
|
+
end
|
66
|
+
|
67
|
+
def create_prong(options)
|
68
|
+
build("prongs", Prong, options, :widget_id => id)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
before do
|
73
|
+
stub_request(:get, "http://user:password@www.example.com/api/v0/widgets?limit=5").
|
74
|
+
with(:headers => {'Accept'=>'application/json'}).
|
75
|
+
to_return(:status => 200, :body => [{ :kind => "sprocket", :id => 1, :owners => [{ :name => "Bob" }, { :name => "Sam" }] }, { :kind => "gizmo", :id => 2 }].to_json, :headers => {})
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "the test client" do
|
79
|
+
it "should do basic auth" do
|
80
|
+
client = TestClient.new("user", "password")
|
81
|
+
client.widgets(:limit => 5)
|
82
|
+
WebMock.should have_requested(:get, "http://user:password@www.example.com/api/v0/widgets?limit=5")
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should have many widgets" do
|
86
|
+
client = TestClient.new("user", "password")
|
87
|
+
widgets = client.widgets(:limit => 5)
|
88
|
+
widgets.length.should == 2
|
89
|
+
widgets.first.kind.should == "sprocket"
|
90
|
+
widgets.first.owners.length.should == 2
|
91
|
+
widgets.first.owners.first.should be_a(User)
|
92
|
+
widgets.first.owners.first.name.should == "Bob"
|
93
|
+
widgets.last.owners.should be_nil
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "widgets" do
|
98
|
+
before do
|
99
|
+
@client = TestClient.new("user", "password")
|
100
|
+
@widget = @client.widgets(:limit => 5).first
|
101
|
+
@widget.should be_a(Widget)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should be reloadable" do
|
105
|
+
stub_request(:get, "http://user:password@www.example.com/api/v0/widgets/1?some=option").
|
106
|
+
with(:headers => {'Accept'=>'application/json'}).
|
107
|
+
to_return(:status => 200, :body => { :kind => "sprocket_updated", :id => 1, :owners => [{ :name => "Bob_updated" }, { :name => "Sam" }] }.to_json, :headers => {})
|
108
|
+
|
109
|
+
@widget.reload(:some => :option)
|
110
|
+
@widget.kind.should == "sprocket_updated"
|
111
|
+
@widget.owners.first.name.should == "Bob_updated"
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "#prongs" do
|
115
|
+
before do
|
116
|
+
stub_request(:get, "http://user:password@www.example.com/api/v0/widgets/1/prongs?exclude=gears").
|
117
|
+
with(:headers => {'Accept'=>'application/json'}).
|
118
|
+
to_return(:status => 200, :body => [{ :price => "$5", :id => 100 }, { :price => "$10", :id => 101 }].to_json, :headers => {})
|
119
|
+
|
120
|
+
@prongs = @widget.prongs(:exclude => "gears")
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should return prongs" do
|
124
|
+
@prongs.length.should == 2
|
125
|
+
@prongs.first.should be_a(Prong)
|
126
|
+
@prongs.first.price.should == "$5"
|
127
|
+
@prongs.first.id.should == 100
|
128
|
+
@prongs.first.request_path.should == "/widgets/1/prongs/100"
|
129
|
+
@prongs.first.path_params.should == { :widget_id => 1 }
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "#update" do
|
133
|
+
it "updates the local model when successful" do
|
134
|
+
stub_request(:put, "http://user:password@www.example.com/api/v0/widgets/1/prongs/100").
|
135
|
+
with(:headers => {'Accept'=>'application/json'}, :body => "prong[price]=%248.50").
|
136
|
+
to_return(:status => 200, :body => { :price => "$8.50", :id => 100 }.to_json, :headers => {})
|
137
|
+
|
138
|
+
prong = @prongs.first
|
139
|
+
prong.update(:price => "$8.50")
|
140
|
+
prong.price.should == "$8.50"
|
141
|
+
prong.errors.should be_empty
|
142
|
+
end
|
143
|
+
|
144
|
+
it "doesn't update the local model but adds errors when unsuccessful" do
|
145
|
+
stub_request(:put, "http://user:password@www.example.com/api/v0/widgets/1/prongs/100").
|
146
|
+
with(:headers => {'Accept'=>'application/json'}, :body => "prong[price]=%248.50").
|
147
|
+
to_return(:status => 422, :body => { :errors => ["error 1", "error 2"] }.to_json, :headers => {})
|
148
|
+
|
149
|
+
prong = @prongs.first
|
150
|
+
prong.update(:price => "$8.50")
|
151
|
+
prong.price.should == "$5"
|
152
|
+
prong.errors.should == ["error 1", "error 2"]
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe "#destroy" do
|
157
|
+
it "returns true when successful" do
|
158
|
+
stub_request(:delete, "http://user:password@www.example.com/api/v0/widgets/1/prongs/100?").
|
159
|
+
with(:headers => {'Accept'=>'application/json'}).
|
160
|
+
to_return(:status => 200, :body => '', :headers => {})
|
161
|
+
|
162
|
+
prong = @prongs.first
|
163
|
+
prong.destroy.should be_true
|
164
|
+
end
|
165
|
+
|
166
|
+
it "returns false and sets errors on failure" do
|
167
|
+
stub_request(:delete, "http://user:password@www.example.com/api/v0/widgets/1/prongs/100?").
|
168
|
+
with(:headers => {'Accept'=>'application/json'}).
|
169
|
+
to_return(:status => 422, :body => { :errors => ["error 1", "error 2"] }.to_json, :headers => {})
|
170
|
+
|
171
|
+
prong = @prongs.first
|
172
|
+
prong.destroy.should be_false
|
173
|
+
prong.errors.should == ["error 1", "error 2"]
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe "#prong" do
|
179
|
+
it "should return a single prong by id" do
|
180
|
+
stub_request(:get, "http://user:password@www.example.com/api/v0/widgets/1/prongs/100?").
|
181
|
+
with(:headers => {'Accept'=>'application/json'}).
|
182
|
+
to_return(:status => 200, :body => { :price => "$5", :id => 100 }.to_json, :headers => {})
|
183
|
+
prong = @widget.prong(100)
|
184
|
+
prong.should be_a(Prong)
|
185
|
+
prong.price.should == "$5"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe "#create_prong" do
|
190
|
+
it "should return the new prong object when successful" do
|
191
|
+
stub_request(:post, "http://user:password@www.example.com/api/v0/widgets/1/prongs").
|
192
|
+
with(:headers => {'Accept'=>'application/json'}, :body => "prong[price]=%24100").
|
193
|
+
to_return(:status => 200, :body => { :price => "$100", :id => 102 }.to_json, :headers => {})
|
194
|
+
|
195
|
+
prong = @widget.create_prong(:price => "$100")
|
196
|
+
prong.request_path.should == "/widgets/1/prongs/102"
|
197
|
+
prong.should be_a(Prong)
|
198
|
+
prong.price.should == "$100"
|
199
|
+
prong.id.should == 102
|
200
|
+
prong.errors.should be_empty
|
201
|
+
|
202
|
+
stub_request(:put, "http://user:password@www.example.com/api/v0/widgets/1/prongs/102").
|
203
|
+
with(:headers => {'Accept'=>'application/json'}, :body => "prong[price]=%2490").
|
204
|
+
to_return(:status => 200, :body => { :price => "$90", :id => 102 }.to_json, :headers => {})
|
205
|
+
prong.update(:price => "$90")
|
206
|
+
prong.price.should == "$90"
|
207
|
+
prong.errors.should be_empty
|
208
|
+
prong.request_path.should == "/widgets/1/prongs/102"
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should return the new prong object with errors when unsuccessful" do
|
212
|
+
stub_request(:post, "http://user:password@www.example.com/api/v0/widgets/1/prongs").
|
213
|
+
with(:headers => {'Accept'=>'application/json'}, :body => "prong[price]=%24100").
|
214
|
+
to_return(:status => 422, :body => { :errors => ["error 1", "error 2"] }.to_json, :headers => {})
|
215
|
+
|
216
|
+
prong = @widget.create_prong(:price => "$100")
|
217
|
+
prong.should be_a(Prong)
|
218
|
+
prong.errors.should == ["error 1", "error 2"]
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|