asana 0.0.6 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +9 -9
  2. data/.codeclimate.yml +4 -0
  3. data/.gitignore +12 -20
  4. data/.rspec +4 -0
  5. data/.rubocop.yml +18 -0
  6. data/.travis.yml +12 -0
  7. data/.yardopts +5 -0
  8. data/CODE_OF_CONDUCT.md +13 -0
  9. data/Gemfile +17 -0
  10. data/Guardfile +85 -4
  11. data/LICENSE.txt +21 -0
  12. data/README.md +264 -135
  13. data/Rakefile +62 -7
  14. data/asana.gemspec +27 -21
  15. data/examples/Gemfile +6 -0
  16. data/examples/Gemfile.lock +56 -0
  17. data/examples/api_token.rb +21 -0
  18. data/examples/cli_app.rb +25 -0
  19. data/examples/events.rb +38 -0
  20. data/examples/omniauth_integration.rb +54 -0
  21. data/lib/asana.rb +8 -11
  22. data/lib/asana/authentication.rb +8 -0
  23. data/lib/asana/authentication/oauth2.rb +42 -0
  24. data/lib/asana/authentication/oauth2/access_token_authentication.rb +51 -0
  25. data/lib/asana/authentication/oauth2/bearer_token_authentication.rb +32 -0
  26. data/lib/asana/authentication/oauth2/client.rb +50 -0
  27. data/lib/asana/authentication/token_authentication.rb +20 -0
  28. data/lib/asana/client.rb +124 -0
  29. data/lib/asana/client/configuration.rb +165 -0
  30. data/lib/asana/errors.rb +90 -0
  31. data/lib/asana/http_client.rb +155 -0
  32. data/lib/asana/http_client/environment_info.rb +53 -0
  33. data/lib/asana/http_client/error_handling.rb +103 -0
  34. data/lib/asana/http_client/response.rb +32 -0
  35. data/lib/asana/resources.rb +11 -0
  36. data/lib/asana/resources/attachment.rb +44 -0
  37. data/lib/asana/resources/attachment_uploading.rb +33 -0
  38. data/lib/asana/resources/collection.rb +68 -0
  39. data/lib/asana/resources/event.rb +49 -0
  40. data/lib/asana/resources/event_subscription.rb +12 -0
  41. data/lib/asana/resources/events.rb +101 -0
  42. data/lib/asana/resources/project.rb +145 -19
  43. data/lib/asana/resources/registry.rb +62 -0
  44. data/lib/asana/resources/resource.rb +103 -0
  45. data/lib/asana/resources/response_helper.rb +14 -0
  46. data/lib/asana/resources/story.rb +58 -7
  47. data/lib/asana/resources/tag.rb +111 -19
  48. data/lib/asana/resources/task.rb +284 -57
  49. data/lib/asana/resources/team.rb +55 -0
  50. data/lib/asana/resources/user.rb +65 -10
  51. data/lib/asana/resources/workspace.rb +79 -34
  52. data/lib/asana/ruby2_0_0_compatibility.rb +3 -0
  53. data/lib/asana/version.rb +3 -1
  54. data/lib/templates/index.js +8 -0
  55. data/lib/templates/resource.ejs +225 -0
  56. data/package.json +7 -0
  57. metadata +91 -51
  58. data/LICENSE +0 -22
  59. data/lib/asana/config.rb +0 -23
  60. data/lib/asana/resource.rb +0 -52
  61. data/spec/asana/resources/project_spec.rb +0 -63
  62. data/spec/asana/resources/story_spec.rb +0 -39
  63. data/spec/asana/resources/tag_spec.rb +0 -63
  64. data/spec/asana/resources/task_spec.rb +0 -95
  65. data/spec/asana/resources/user_spec.rb +0 -64
  66. data/spec/asana/resources/workspace_spec.rb +0 -108
  67. data/spec/spec_helper.rb +0 -9
@@ -0,0 +1,55 @@
1
+ ### WARNING: This file is auto-generated by the asana-api-meta repo. Do not
2
+ ### edit it manually.
3
+
4
+ module Asana
5
+ module Resources
6
+ # A _team_ is used to group related projects and people together within an
7
+ # organization. Each project in an organization is associated with a team.
8
+ class Team < Resource
9
+
10
+
11
+ attr_reader :id
12
+
13
+ attr_reader :name
14
+
15
+ class << self
16
+ # Returns the plural name of the resource.
17
+ def plural_name
18
+ 'teams'
19
+ end
20
+
21
+ # Returns the full record for a single team.
22
+ #
23
+ # id - [Id] Globally unique identifier for the team.
24
+ #
25
+ # options - [Hash] the request I/O options.
26
+ def find_by_id(client, id, options: {})
27
+
28
+ self.new(parse(client.get("/teams/#{id}", options: options)).first, client: client)
29
+ end
30
+
31
+ # Returns the compact records for all teams in the organization visible to
32
+ # the authorized user.
33
+ #
34
+ # organization - [Id] Globally unique identifier for the workspace or organization.
35
+ #
36
+ # per_page - [Integer] the number of records to fetch per page.
37
+ # options - [Hash] the request I/O options.
38
+ def find_by_organization(client, organization: required("organization"), per_page: 20, options: {})
39
+ params = { limit: per_page }.reject { |_,v| v.nil? || Array(v).empty? }
40
+ Collection.new(parse(client.get("/organizations/#{organization}/teams", params: params, options: options)), type: self, client: client)
41
+ end
42
+ end
43
+
44
+ # Returns the compact records for all users that are members of the team.
45
+ #
46
+ # per_page - [Integer] the number of records to fetch per page.
47
+ # options - [Hash] the request I/O options.
48
+ def users(per_page: 20, options: {})
49
+ params = { limit: per_page }.reject { |_,v| v.nil? || Array(v).empty? }
50
+ Collection.new(parse(client.get("/teams/#{id}/users", params: params, options: options)), type: User, client: client)
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -1,17 +1,72 @@
1
+ ### WARNING: This file is auto-generated by the asana-api-meta repo. Do not
2
+ ### edit it manually.
3
+
1
4
  module Asana
2
- class User < Asana::Resource
5
+ module Resources
6
+ # A _user_ object represents an account in Asana that can be given access to
7
+ # various workspaces, projects, and tasks.
8
+ #
9
+ # Like other objects in the system, users are referred to by numerical IDs.
10
+ # However, the special string identifier `me` can be used anywhere
11
+ # a user ID is accepted, to refer to the current authenticated user.
12
+ class User < Resource
3
13
 
4
- alias :save :method_not_allowed
5
- alias :destroy :method_not_allowed
6
14
 
7
- def self.all_by_workspace(*args)
8
- parent_resources :workspace
9
- all(*args)
10
- end
15
+ attr_reader :id
11
16
 
12
- def self.me
13
- User.new(get(:me))
14
- end
17
+ attr_reader :email
18
+
19
+ attr_reader :photo
20
+
21
+ attr_reader :workspaces
22
+
23
+ class << self
24
+ # Returns the plural name of the resource.
25
+ def plural_name
26
+ 'users'
27
+ end
28
+
29
+ # Returns the full user record for the currently authenticated user.
30
+ #
31
+ # options - [Hash] the request I/O options.
32
+ def me(client, options: {})
15
33
 
34
+ Resource.new(parse(client.get("/users/me", options: options)).first, client: client)
35
+ end
36
+
37
+ # Returns the full user record for a single user.
38
+ #
39
+ # id - [Id] Globally unique identifier for the user.
40
+ #
41
+ # options - [Hash] the request I/O options.
42
+ def find_by_id(client, id, options: {})
43
+
44
+ self.new(parse(client.get("/users/#{id}", options: options)).first, client: client)
45
+ end
46
+
47
+ # Returns the user records for all users in all workspaces and organizations
48
+ # accessible to the authenticated user.
49
+ #
50
+ # workspace - [Id] The workspace in which to get users.
51
+ # per_page - [Integer] the number of records to fetch per page.
52
+ # options - [Hash] the request I/O options.
53
+ def find_by_workspace(client, workspace: required("workspace"), per_page: 20, options: {})
54
+ params = { limit: per_page }.reject { |_,v| v.nil? || Array(v).empty? }
55
+ Collection.new(parse(client.get("/workspaces/#{workspace}/users", params: params, options: options)), type: self, client: client)
56
+ end
57
+
58
+ # Returns the user records for all users in the specified workspace or
59
+ # organization.
60
+ #
61
+ # workspace - [Id] The workspace or organization to filter users on.
62
+ # per_page - [Integer] the number of records to fetch per page.
63
+ # options - [Hash] the request I/O options.
64
+ def find_all(client, workspace: nil, per_page: 20, options: {})
65
+ params = { workspace: workspace, limit: per_page }.reject { |_,v| v.nil? || Array(v).empty? }
66
+ Collection.new(parse(client.get("/users", params: params, options: options)), type: self, client: client)
67
+ end
68
+ end
69
+
70
+ end
16
71
  end
17
72
  end
@@ -1,46 +1,91 @@
1
+ ### WARNING: This file is auto-generated by the asana-api-meta repo. Do not
2
+ ### edit it manually.
3
+
1
4
  module Asana
2
- class Workspace < Asana::Resource
5
+ module Resources
6
+ # A _workspace_ is the highest-level organizational unit in Asana. All projects
7
+ # and tasks have an associated workspace.
8
+ #
9
+ # An _organization_ is a special kind of workspace that represents a company.
10
+ # In an organization, you can group your projects into teams. You can read
11
+ # more about how organizations work on the Asana Guide.
12
+ # To tell if your workspace is an organization or not, check its
13
+ # `is_organization` property.
14
+ #
15
+ # Over time, we intend to migrate most workspaces into organizations and to
16
+ # release more organization-specific functionality. We may eventually deprecate
17
+ # using workspace-based APIs for organizations. Currently, and until after
18
+ # some reasonable grace period following any further announcements, you can
19
+ # still reference organizations in any `workspace` parameter.
20
+ class Workspace < Resource
3
21
 
4
- alias :create :method_not_allowed
5
- alias :destroy :method_not_allowed
6
22
 
7
- def projects
8
- Project.all_by_workspace(:params => { :workspace_id => self.id })
9
- end
23
+ attr_reader :id
10
24
 
11
- def create_project(*args)
12
- options = { :workspace => self.id }
13
- project = Project.new(options.merge(args.first))
14
- response = Project.post(nil, nil, project.to_json)
15
- Project.new(connection.format.decode(response.body))
16
- end
25
+ attr_reader :name
17
26
 
18
- def tasks(assignee)
19
- query_options = { :workspace => self.id, :assignee => assignee }
20
- Task.all_by_workspace(:params => query_options)
21
- end
27
+ attr_reader :is_organization
22
28
 
23
- def create_task(*args)
24
- options = { :workspace => self.id, :assignee => 'me' }
25
- task = Task.new(options.merge(args.first))
26
- response = Task.post(nil, nil, task.to_json)
27
- Task.new(connection.format.decode(response.body))
28
- end
29
+ class << self
30
+ # Returns the plural name of the resource.
31
+ def plural_name
32
+ 'workspaces'
33
+ end
29
34
 
30
- def tags
31
- Tag.all_by_workspace(:params => { :workspace_id => self.id })
32
- end
35
+ # Returns the full workspace record for a single workspace.
36
+ #
37
+ # id - [Id] Globally unique identifier for the workspace or organization.
38
+ #
39
+ # options - [Hash] the request I/O options.
40
+ def find_by_id(client, id, options: {})
33
41
 
34
- def create_tag(*args)
35
- options = { :workspace => self.id }
36
- tag = Tag.new(options.merge(args.first))
37
- response = Tag.post(nil, nil, tag.to_json)
38
- Tag.new(connection.format.decode(response.body))
39
- end
42
+ self.new(parse(client.get("/workspaces/#{id}", options: options)).first, client: client)
43
+ end
40
44
 
41
- def users
42
- User.all_by_workspace(:params => { :workspace_id => self.id })
43
- end
45
+ # Returns the compact records for all workspaces visible to the authorized user.
46
+ #
47
+ # per_page - [Integer] the number of records to fetch per page.
48
+ # options - [Hash] the request I/O options.
49
+ def find_all(client, per_page: 20, options: {})
50
+ params = { limit: per_page }.reject { |_,v| v.nil? || Array(v).empty? }
51
+ Collection.new(parse(client.get("/workspaces", params: params, options: options)), type: self, client: client)
52
+ end
53
+ end
54
+
55
+ # Update properties on a workspace. Returns the complete, updated workspace record.
56
+ #
57
+ # options - [Hash] the request I/O options.
58
+ # data - [Hash] the attributes to post.
59
+ def update(options: {}, **data)
44
60
 
61
+ refresh_with(parse(client.put("/workspaces/#{id}", body: data, options: options)).first)
62
+ end
63
+
64
+ # Retrieves objects in the workspace based on an auto-completion/typeahead
65
+ # search algorithm. This feature is meant to provide results quickly, so do
66
+ # not rely on this API to provide extremely accurate search results. The
67
+ # result set is limited to a single page of results with a maximum size,
68
+ # so you won't be able to fetch large numbers of results.
69
+ #
70
+ # type - [Enum] The type of values the typeahead should return.
71
+ # Note that unlike in the names of endpoints, the types listed here are
72
+ # in singular form (e.g. `task`). Using multiple types is not yet supported.
73
+ #
74
+ # query - [String] The string that will be used to search for relevant objects. If an
75
+ # empty string is passed in, the API will currently return an empty
76
+ # result set.
77
+ #
78
+ # count - [Number] The number of results to return. The default is `20` if this
79
+ # parameter is omitted, with a minimum of `1` and a maximum of `100`.
80
+ # If there are fewer results found than requested, all will be returned.
81
+ #
82
+ # per_page - [Integer] the number of records to fetch per page.
83
+ # options - [Hash] the request I/O options.
84
+ def typeahead(type: required("type"), query: nil, count: nil, per_page: 20, options: {})
85
+ params = { type: type, query: query, count: count, limit: per_page }.reject { |_,v| v.nil? || Array(v).empty? }
86
+ Collection.new(parse(client.get("/workspaces/#{id}/typeahead", params: params, options: options)), type: Resource, client: client)
87
+ end
88
+
89
+ end
45
90
  end
46
91
  end
@@ -0,0 +1,3 @@
1
+ def required(name)
2
+ fail(ArgumentError, "#{name} is a required keyword argument")
3
+ end
@@ -1,3 +1,5 @@
1
+ #:nodoc:
1
2
  module Asana
2
- VERSION = "0.0.6"
3
+ # Public: Version of the gem.
4
+ VERSION = '0.1.1'
3
5
  end
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ resource: {
3
+ template: 'resource.ejs',
4
+ filename: function(resource, helpers) {
5
+ return resource.name + '.rb';
6
+ }
7
+ }
8
+ };
@@ -0,0 +1,225 @@
1
+ <%
2
+ var singularName = resource.name,
3
+ pluralName = plural(singularName),
4
+ mixins = {
5
+ task: ["AttachmentUploading", "EventSubscription"],
6
+ project: ["EventSubscription"]
7
+ },
8
+ skip = { attachment: ["createOnTask"] },
9
+ formatComment = function formatComment(text, indentation) {
10
+ var indent = Array(indentation + 1).join(" ")
11
+ return text.trim().split("\n").map(function(line) {
12
+ return indent + (line.length > 0 ? "# " : "#") + line
13
+ }).join("\n")
14
+ }
15
+
16
+
17
+ function Action(action) {
18
+ var that = this
19
+ this.action = action
20
+ this.collection = action.collection == true
21
+ this.requiresData = action.method == "POST" || action.method == "PUT"
22
+ this.isInstanceAction = (action.method == "PUT" || action.method == "DELETE")
23
+ this.method = action.method
24
+ this.methodName = snake(action.name)
25
+ this.clientMethod = action.method.toLowerCase()
26
+ this.returnsUpdatedRecord = action.method == 'PUT' || action.comment.match(/Returns[a-z\W]+updated/) !== null
27
+ this.returnsNothing = action.comment.match(/Returns an empty/) !== null
28
+
29
+ // Params and idParams
30
+ var params = action.params || []
31
+ this.idParams = _.filter(params, function(p) { return p.type == "Id" })
32
+
33
+ // If it looks like an instance action but it's not, make it one
34
+ if (!this.isInstanceAction) {
35
+ var mainIdParam = _.find(this.idParams, function(p) { return p.name == singularName })
36
+ if (mainIdParam !== undefined && !action.name.match(/Id/)) {
37
+ this.isInstanceAction = true
38
+ this.mainIdParam = mainIdParam
39
+ }
40
+ }
41
+
42
+ if (this.idParams.length == 1 &&
43
+ action.path.match(/%d/) &&
44
+ (action.name.match(/Id/) || (this.isInstanceAction && this.mainIdParam == undefined))) {
45
+ var mainIdParam = this.idParams[0]
46
+ this.mainIdParam = mainIdParam
47
+ this.inferredReturnType = this.isInstanceAction ? 'self.class' : 'self'
48
+ }
49
+
50
+ if (mainIdParam !== undefined) {
51
+ this.params = _.reject(params, function(p) { return p.name == mainIdParam.name })
52
+ } else {
53
+ this.params = params
54
+ }
55
+
56
+ if (!this.inferredReturnType) {
57
+ // Infer return type
58
+ var name = action.path.match(/\/([a-zA-Z]+)$/)
59
+ if (name !== null) {
60
+ name = name[1]
61
+
62
+ // Desugarize 'addProject' to 'project'
63
+ var camelCaseTail = name.match(/.*([A-Z][a-z]+)$/)
64
+ if (camelCaseTail !== null) { name = decap(camelCaseTail[1]) }
65
+
66
+ name = single(name)
67
+
68
+ var explicit = _.find(resources, function(p) { return p == name })
69
+
70
+ if (name == singularName || name == 'parent' || name == 'children' || name.match(/^sub/) !== null) {
71
+ this.inferredReturnType = this.isInstanceAction ? 'self.class' : 'self'
72
+ } else if (explicit !== undefined) {
73
+ this.inferredReturnType = cap(explicit)
74
+ } else {
75
+ this.inferredReturnType = 'Resource'
76
+ }
77
+ } else {
78
+ this.inferredReturnType = 'Resource'
79
+ }
80
+ }
81
+
82
+ // Endpoint path
83
+ this.path = _.reduce(this.idParams, function(acc, id) {
84
+ var localName = that.mainIdParam == id ? "id" : id.name
85
+ return acc.replace("\%d", "#{" + localName + "}")
86
+ }, action.path)
87
+
88
+ // Extra params (not in the URL) to be passed in the body of the call
89
+ this.extraParams = _.reject(this.params, function(p) {
90
+ return that.path.match(new RegExp("#{" + p.name + "}"))
91
+ })
92
+
93
+ // Params processing
94
+ var paramsLocal = "data"
95
+ if (this.collection) { this.extraParams.push({ name: "per_page", apiParamName: "limit" }) }
96
+ if (this.extraParams.length > 0) {
97
+ var paramNames = _.map(this.extraParams, function(p) { return (p.apiParamName || p.name) + ": " + p.name })
98
+ if (this.requiresData) {
99
+ var paramsProcessing = "with_params = data.merge(" + paramNames.join(", ") + ")"
100
+ paramsLocal = "with_params"
101
+ } else {
102
+ var paramsProcessing = "params = { " + paramNames.join(", ") + " }"
103
+ paramsLocal = "params"
104
+ }
105
+ paramsProcessing += ".reject { |_,v| v.nil? || Array(v).empty? }"
106
+ }
107
+
108
+ this.paramsProcessing = paramsProcessing
109
+ this.paramsLocal = paramsLocal
110
+
111
+ // Method argument names
112
+ var argumentNames = Array()
113
+ if (!this.isInstanceAction) { argumentNames.push("client") }
114
+ if (this.mainIdParam !== undefined && !this.isInstanceAction) { argumentNames.push("id") }
115
+ _.forEach(this.params, function(param) {
116
+ argumentNames.push(param.name + ":" + (param.required ? " required(\"" + param.name + "\")" : " nil"))
117
+ })
118
+ if (this.collection) { argumentNames.push("per_page: 20") }
119
+ if (this.method != 'DELETE') { argumentNames.push("options: {}") }
120
+ if (this.requiresData) { argumentNames.push("**data") }
121
+ this.argumentNames = argumentNames
122
+
123
+ // API request params
124
+ var requestParams = Array()
125
+ requestParams.push('"' + this.path + '"')
126
+ if (this.paramsProcessing || this.argumentNames.indexOf("**data") != -1) {
127
+ var argument = this.requiresData ? "body" : "params"
128
+ requestParams.push(argument + ": " + paramsLocal)
129
+ }
130
+ if (this.method != 'DELETE') { requestParams.push("options: options") }
131
+ this.requestParams = requestParams
132
+ this.documentation = this.renderDocumentation()
133
+
134
+ // Constructor
135
+ this.constructor = function(body) {
136
+ var pre = '', post = ''
137
+ var wrapWithParsing = function(body) {
138
+ var pre = '', post = ''
139
+ if (!that.returnsNothing) {
140
+ pre = 'parse('
141
+ post = ')' + (that.collection ? '' : '.first')
142
+ }
143
+ return pre + body + post
144
+ }
145
+
146
+ if (!that.returnsNothing) {
147
+ if (that.isInstanceAction && that.returnsUpdatedRecord) {
148
+ pre = "refresh_with("
149
+ post = ')'
150
+ } else {
151
+ pre = that.collection ? "Collection.new(" : that.inferredReturnType + ".new("
152
+ post = (that.collection ? ', type: ' + that.inferredReturnType : '') + ', client: client)'
153
+ }
154
+ } else { post = ' && true' }
155
+ return pre + wrapWithParsing(body) + post
156
+ }
157
+
158
+ this.request = this.constructor("client." + this.clientMethod + "(" + this.requestParams.join(", ") + ")")
159
+ }
160
+
161
+ Action.prototype.renderDocumentation = function () {
162
+ var formatParamNotes = function(params) {
163
+ var trimmed = _.flatten(_.map(params, function(p) {
164
+ return _.map(p.notes, function(note) { return note.trim() })
165
+ }))
166
+ return (trimmed.length > 0 ? "\nNotes:\n\n" + trimmed.join("\n\n") : "")
167
+ }
168
+
169
+ var formatParam = function(p, name) {
170
+ return (name !== undefined ? name : p.name) + " - [" + p.type + "] " + p.comment
171
+ }
172
+ var lines = _.map(this.params, function(p) { return formatParam(p) })
173
+ if (this.mainIdParam !== undefined && !this.isInstanceAction) { lines.unshift(formatParam(this.mainIdParam, "id")) }
174
+ if (this.collection) { lines.push("per_page - [Integer] the number of records to fetch per page.") }
175
+ if (this.method != 'DELETE') { lines.push("options - [Hash] the request I/O options.") }
176
+ if (this.requiresData) { lines.push("data - [Hash] the attributes to post.") }
177
+ return this.action.comment + "\n" + lines.join("\n") + formatParamNotes(this.params)
178
+ }
179
+
180
+ var actionsToSkip = skip[resource.name] || []
181
+ var actionsToGen = _.reject(resource.actions, function(action) {
182
+ return actionsToSkip.indexOf(action.name) != -1
183
+ })
184
+
185
+ var allActions = _.map(actionsToGen, function(action) { return new Action(action) }),
186
+ instanceActions = _.filter(allActions, function(action) { return action.isInstanceAction }),
187
+ classActions = _.reject(allActions, function(action) { return action.isInstanceAction })
188
+
189
+ var mixinsToInclude = mixins[resource.name] || []
190
+
191
+ %>### WARNING: This file is auto-generated by the asana-api-meta repo. Do not
192
+ ### edit it manually.
193
+
194
+ module Asana
195
+ module Resources
196
+ <%= formatComment(resource.comment, 4) %>
197
+ class <%= cap(singularName) %> < Resource
198
+ <% _.forEach(mixinsToInclude, function(mixin) { %>
199
+ include <%= mixin %>
200
+ <% }) %>
201
+ <% _.forEach(resource.properties, function(property) { %>
202
+ attr_reader :<%= property.name %>
203
+ <% }) %>
204
+ class << self
205
+ # Returns the plural name of the resource.
206
+ def plural_name
207
+ '<%= pluralName %>'
208
+ end
209
+ <% _.forEach(classActions, function(action) { %>
210
+ <%= formatComment(action.documentation, 8) %>
211
+ def <%= action.methodName %>(<%= action.argumentNames.join(", ") %>)
212
+ <% if (action.paramsProcessing) { %> <%= action.paramsProcessing %><% } %>
213
+ <%= action.request %>
214
+ end
215
+ <% }) %> end
216
+ <% _.forEach(instanceActions, function(action) { %>
217
+ <%= formatComment(action.documentation, 6) %>
218
+ def <%= action.methodName %>(<%= action.argumentNames.join(", ") %>)
219
+ <% if (action.paramsProcessing) { %> <%= action.paramsProcessing %><% } %>
220
+ <%= action.request %>
221
+ end
222
+ <% }) %>
223
+ end
224
+ end
225
+ end