mavenlink 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.
Files changed (126) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +3 -0
  6. data/README.md +241 -0
  7. data/Rakefile +28 -0
  8. data/bin/mavenlink-console +18 -0
  9. data/doc/ml_logo_lb-primary.png +0 -0
  10. data/lib/config/specification.yml +1665 -0
  11. data/lib/mavenlink.rb +134 -0
  12. data/lib/mavenlink/account_invitation.rb +4 -0
  13. data/lib/mavenlink/account_membership.rb +4 -0
  14. data/lib/mavenlink/additional_item.rb +4 -0
  15. data/lib/mavenlink/assignment.rb +5 -0
  16. data/lib/mavenlink/attachment.rb +11 -0
  17. data/lib/mavenlink/backup_approver_association.rb +4 -0
  18. data/lib/mavenlink/client.rb +116 -0
  19. data/lib/mavenlink/concerns/custom_fieldable.rb +9 -0
  20. data/lib/mavenlink/concerns/indestructible.rb +11 -0
  21. data/lib/mavenlink/concerns/locked_record.rb +30 -0
  22. data/lib/mavenlink/cost_rate.rb +4 -0
  23. data/lib/mavenlink/custom_field.rb +4 -0
  24. data/lib/mavenlink/custom_field_choice.rb +5 -0
  25. data/lib/mavenlink/custom_field_value.rb +4 -0
  26. data/lib/mavenlink/errors.rb +47 -0
  27. data/lib/mavenlink/expense.rb +4 -0
  28. data/lib/mavenlink/expense_category.rb +4 -0
  29. data/lib/mavenlink/expense_report_submission.rb +7 -0
  30. data/lib/mavenlink/external_payment.rb +4 -0
  31. data/lib/mavenlink/external_reference.rb +24 -0
  32. data/lib/mavenlink/fixed_fee_item.rb +4 -0
  33. data/lib/mavenlink/holiday.rb +4 -0
  34. data/lib/mavenlink/holiday_calendar.rb +4 -0
  35. data/lib/mavenlink/holiday_calendar_association.rb +4 -0
  36. data/lib/mavenlink/holiday_calendar_membership.rb +4 -0
  37. data/lib/mavenlink/invoice.rb +4 -0
  38. data/lib/mavenlink/logger.rb +62 -0
  39. data/lib/mavenlink/model.rb +279 -0
  40. data/lib/mavenlink/organization.rb +4 -0
  41. data/lib/mavenlink/organization_membership.rb +4 -0
  42. data/lib/mavenlink/participation.rb +4 -0
  43. data/lib/mavenlink/post.rb +4 -0
  44. data/lib/mavenlink/project_template.rb +4 -0
  45. data/lib/mavenlink/project_template_assignment.rb +4 -0
  46. data/lib/mavenlink/railtie.rb +7 -0
  47. data/lib/mavenlink/rate_card.rb +4 -0
  48. data/lib/mavenlink/rate_card_role.rb +4 -0
  49. data/lib/mavenlink/rate_card_set.rb +4 -0
  50. data/lib/mavenlink/rate_card_set_version.rb +30 -0
  51. data/lib/mavenlink/rate_card_version.rb +4 -0
  52. data/lib/mavenlink/request.rb +241 -0
  53. data/lib/mavenlink/resolution.rb +4 -0
  54. data/lib/mavenlink/response.rb +22 -0
  55. data/lib/mavenlink/role.rb +5 -0
  56. data/lib/mavenlink/settings.rb +11 -0
  57. data/lib/mavenlink/skill.rb +4 -0
  58. data/lib/mavenlink/skill_category.rb +4 -0
  59. data/lib/mavenlink/skill_membership.rb +4 -0
  60. data/lib/mavenlink/specificators/association.rb +13 -0
  61. data/lib/mavenlink/specificators/attribute.rb +13 -0
  62. data/lib/mavenlink/specificators/base.rb +24 -0
  63. data/lib/mavenlink/specificators/validation.rb +27 -0
  64. data/lib/mavenlink/status_report.rb +4 -0
  65. data/lib/mavenlink/story.rb +8 -0
  66. data/lib/mavenlink/story_allocation_day.rb +5 -0
  67. data/lib/mavenlink/story_dependency.rb +5 -0
  68. data/lib/mavenlink/story_task.rb +5 -0
  69. data/lib/mavenlink/tag.rb +5 -0
  70. data/lib/mavenlink/time_adjustment.rb +4 -0
  71. data/lib/mavenlink/time_entry.rb +4 -0
  72. data/lib/mavenlink/time_off_entry.rb +4 -0
  73. data/lib/mavenlink/timesheet_submission.rb +4 -0
  74. data/lib/mavenlink/user.rb +5 -0
  75. data/lib/mavenlink/vendor.rb +4 -0
  76. data/lib/mavenlink/workspace.rb +21 -0
  77. data/lib/mavenlink/workspace_group.rb +5 -0
  78. data/lib/mavenlink/workspace_invoice_preference.rb +4 -0
  79. data/lib/mavenlink/workweek.rb +4 -0
  80. data/lib/mavenlink/workweek_membership.rb +4 -0
  81. data/mavenlink.gemspec +29 -0
  82. data/spec/lib/mavenlink/account_membership_spec.rb +8 -0
  83. data/spec/lib/mavenlink/assignment_spec.rb +17 -0
  84. data/spec/lib/mavenlink/attachment_spec.rb +30 -0
  85. data/spec/lib/mavenlink/backup_approver_association_spec.rb +9 -0
  86. data/spec/lib/mavenlink/client_spec.rb +187 -0
  87. data/spec/lib/mavenlink/concerns/indestructible_spec.rb +13 -0
  88. data/spec/lib/mavenlink/concerns/locked_record_spec.rb +28 -0
  89. data/spec/lib/mavenlink/cost_rate_spec.rb +9 -0
  90. data/spec/lib/mavenlink/custom_field_value_spec.rb +10 -0
  91. data/spec/lib/mavenlink/expense_report_submission_spec.rb +16 -0
  92. data/spec/lib/mavenlink/expense_spec.rb +23 -0
  93. data/spec/lib/mavenlink/external_references_spec.rb +144 -0
  94. data/spec/lib/mavenlink/holiday_calendar_association_spec.rb +8 -0
  95. data/spec/lib/mavenlink/holiday_calendar_membership_spec.rb +8 -0
  96. data/spec/lib/mavenlink/holiday_spec.rb +7 -0
  97. data/spec/lib/mavenlink/invalid_request_error_spec.rb +9 -0
  98. data/spec/lib/mavenlink/invoice_spec.rb +176 -0
  99. data/spec/lib/mavenlink/model_spec.rb +439 -0
  100. data/spec/lib/mavenlink/post_spec.rb +23 -0
  101. data/spec/lib/mavenlink/rate_card_set_version_spec.rb +119 -0
  102. data/spec/lib/mavenlink/record_invalid_error_spec.rb +16 -0
  103. data/spec/lib/mavenlink/record_not_found_error_spec.rb +9 -0
  104. data/spec/lib/mavenlink/request_spec.rb +381 -0
  105. data/spec/lib/mavenlink/response_spec.rb +50 -0
  106. data/spec/lib/mavenlink/role_spec.rb +9 -0
  107. data/spec/lib/mavenlink/settings_spec.rb +23 -0
  108. data/spec/lib/mavenlink/skill_category_spec.rb +7 -0
  109. data/spec/lib/mavenlink/skill_membership_spec.rb +9 -0
  110. data/spec/lib/mavenlink/skill_spec.rb +8 -0
  111. data/spec/lib/mavenlink/specificators/association_spec.rb +25 -0
  112. data/spec/lib/mavenlink/specificators/attribute_spec.rb +25 -0
  113. data/spec/lib/mavenlink/specificators/validation_spec.rb +39 -0
  114. data/spec/lib/mavenlink/story_allocation_day_spec.rb +64 -0
  115. data/spec/lib/mavenlink/story_dependency_spec.rb +16 -0
  116. data/spec/lib/mavenlink/story_spec.rb +69 -0
  117. data/spec/lib/mavenlink/time_adjustment_spec.rb +13 -0
  118. data/spec/lib/mavenlink/time_entry_spec.rb +43 -0
  119. data/spec/lib/mavenlink/time_off_entry_spec.rb +9 -0
  120. data/spec/lib/mavenlink/user_spec.rb +138 -0
  121. data/spec/lib/mavenlink/workspace_group_spec.rb +25 -0
  122. data/spec/lib/mavenlink/workspace_spec.rb +431 -0
  123. data/spec/lib/mavenlink_spec.rb +43 -0
  124. data/spec/spec_helper.rb +31 -0
  125. data/spec/support/shared_examples.rb +148 -0
  126. metadata +267 -0
@@ -0,0 +1,7 @@
1
+ module Mavenlink
2
+ class Railtie < Rails::Railtie
3
+ initializer 'mavenlink.initialize' do
4
+ Mavenlink.oauth_token = ENV['MAVENLINK_OAUTH_TOKEN']
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ module Mavenlink
2
+ class RateCard < Model
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Mavenlink
2
+ class RateCardRole < Model
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Mavenlink
2
+ class RateCardSet < Model
3
+ end
4
+ end
@@ -0,0 +1,30 @@
1
+ module Mavenlink
2
+ class RateCardSetVersion < Model
3
+ def publish
4
+ if persisted?
5
+ client.put("rate_card_set_versions/#@id/publish")
6
+ true
7
+ else
8
+ false
9
+ end
10
+ rescue Faraday::Error
11
+ false
12
+ end
13
+
14
+ def clone_version(effective_date = nil)
15
+ return false unless persisted?
16
+
17
+ request.perform do
18
+ client.post(collection_name, {
19
+ clone_id: self.id,
20
+ rate_card_set_version: {
21
+ rate_card_set_id: self.rate_card_set_id,
22
+ effective_date: effective_date
23
+ }
24
+ })
25
+ end.results.first
26
+ rescue Mavenlink::Error
27
+ false
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,4 @@
1
+ module Mavenlink
2
+ class RateCardVersion < Model
3
+ end
4
+ end
@@ -0,0 +1,241 @@
1
+ module Mavenlink
2
+ class Request
3
+ include Enumerable
4
+
5
+ attr_reader :client, :collection_name
6
+ attr_accessor :scope
7
+
8
+ # @param collection_name [String, Symbol]
9
+ # @param client [Mavenlink::Client]
10
+ def initialize(collection_name, client = Mavenlink.client)
11
+ @collection_name = collection_name
12
+ @client = client
13
+ @scope = ActiveSupport::HashWithIndifferentAccess.new
14
+ end
15
+
16
+ # Returns new chained request
17
+ # @param new_scope [Hash]
18
+ # @return [Mavenlink::Request]
19
+ def chain(new_scope = {})
20
+ self.class.new(collection_name, client).tap do |new_request|
21
+ new_request.scope.merge!(scope)
22
+ new_request.scope.merge!(new_scope)
23
+ end
24
+ end
25
+
26
+ # @param ids [String, Array]
27
+ # @return [Mavenlink::Request]
28
+ def only(*ids)
29
+ if ids.try(:flatten).present?
30
+ chain(only: param_to_request_array(ids))
31
+ else
32
+ without(:only)
33
+ end
34
+ end
35
+
36
+ # @param id [Integer, String]
37
+ # @return [BrainstemAdaptor::Record, nil]
38
+ def find(id)
39
+ only(id).perform.results.first
40
+ end
41
+
42
+ # @param text [String]
43
+ # @return [Mavenlink::Request]
44
+ def search(text)
45
+ chain(search: text)
46
+ end
47
+
48
+ # @param options [Hash]
49
+ def filter(options)
50
+ chain(options)
51
+ end
52
+
53
+ # @param associations [String, Array]
54
+ # @return [Mavenlink::Request]
55
+ def includes(*associations)
56
+ chain(include: param_to_request_array(associations))
57
+ end
58
+ alias_method :include, :includes
59
+
60
+ # @param [Integer, String]
61
+ # @return [Mavenlink::Request]
62
+ def page(number)
63
+ chain(page: number)
64
+ end
65
+
66
+ def current_page
67
+ @scope[:page].try(:to_i) || 1
68
+ end
69
+
70
+ # @return [Integer]
71
+ def total_count
72
+ response.total_count
73
+ end
74
+
75
+ # Kaminari support
76
+ # @return [Integer]
77
+ def total_pages
78
+ (total_count / (@scope[:per_page].try(:to_f) || 200.0)).ceil
79
+ end
80
+
81
+ # Kaminari support
82
+ # @return [Integer]
83
+ def limit_value
84
+ @scope[:limit].try(:to_i) || total_count
85
+ end
86
+
87
+ # @param [Integer, String]
88
+ # @return [Mavenlink::Request]
89
+ def per_page(number)
90
+ chain(per_page: number)
91
+ end
92
+
93
+ # @param [Integer, String]
94
+ # @return [Mavenlink::Request]
95
+ def limit(number)
96
+ chain(limit: number)
97
+ end
98
+
99
+ # @param [Integer, String]
100
+ # @return [Mavenlink::Request]
101
+ def offset(number)
102
+ chain(offset: number)
103
+ end
104
+
105
+ # Removes condition from the scope
106
+ # @param key [String, Symbol]
107
+ # @return [Mavenlink::Request] new request
108
+ def without(key)
109
+ chain.tap { |new_scope| new_scope.scope.delete(key) }
110
+ end
111
+
112
+ # @param field [String, Symbol]
113
+ # @param direction_or_desc [String, nil, true, false]
114
+ # @example
115
+ # workspaces.order(:created_at)
116
+ # workspaces.order(:created_at, :desc)
117
+ # workspaces.order(:created_at, :asc)
118
+ # workspaces.order(:created_at, 'DESC')
119
+ # workspaces.order('created_at:desc')
120
+ # @return [Mavenlink::Request]
121
+ def order(field, direction_or_desc = nil)
122
+ case direction_or_desc
123
+ when :desc, 'DESC', 'desc', true
124
+ field = "#{field}:desc"
125
+ when :asc, nil, 'ASC', 'asc', false
126
+ else
127
+ raise ArgumentError, "Invalid request ordering set '#{direction_or_desc}'"
128
+ end
129
+
130
+ chain(order: field.to_s)
131
+ end
132
+
133
+ # @param attributes [Hash]
134
+ # @return [Mavenlink::Model]
135
+ def build(attributes)
136
+ "Mavenlink::#{collection_name.classify}".constantize.new(attributes, nil, client)
137
+ end
138
+
139
+ # @param attributes [Hash]
140
+ # @return [Mavenlink::Response]
141
+ def create(attributes)
142
+ perform { client.post(collection_name, {collection_name.singularize => attributes}) }
143
+ end
144
+
145
+ # @param attributes [Hash]
146
+ # @return [Mavenlink::Response]
147
+ def update(attributes)
148
+ perform { client.put(resource_path, {collection_name.singularize => attributes}) }
149
+ end
150
+
151
+ # @note Weird non-json response?
152
+ # @return [nil]
153
+ def delete
154
+ client.delete(resource_path)
155
+ end
156
+
157
+ # @return [Mavenlink::Response]
158
+ def perform
159
+ response = block_given? ? yield : client.get(collection_name, scope)
160
+ Mavenlink::Response.new(response, client)
161
+ end
162
+
163
+ # Returns cached response
164
+ # @return [Mavenlink::Response]
165
+ def response
166
+ @response ||= perform
167
+ end
168
+
169
+ def results
170
+ @results ||= response.results
171
+ end
172
+ alias_method :all, :results
173
+
174
+ def reload
175
+ @results = nil
176
+ results
177
+ end
178
+
179
+ def each(&block)
180
+ results.each(&block)
181
+ end
182
+
183
+ # Iterates through each page, similar to ActiveRecord's #find_in_batches.
184
+ # @todo replace with lazy enumerator for ruby 2.0
185
+ # @param batch_size [Integer]
186
+ # @return [Enumerator<Array<Mavenlink::Model>>]
187
+ def each_page(batch_size = 200, &block)
188
+ Enumerator.new do |result|
189
+ i = 0
190
+ records_passed = 0
191
+ request = per_page(batch_size)
192
+ page_records = []
193
+ total_count = Float::INFINITY
194
+
195
+ while (records_passed += page_records.count) < total_count
196
+ response = request.page(i+=1).perform
197
+ total_count = response.total_count
198
+ result << page_records = response.results
199
+ end
200
+ end.each(&block)
201
+ end
202
+
203
+ def scoped
204
+ self
205
+ end
206
+
207
+ def to_hash
208
+ scope
209
+ end
210
+
211
+ # @see https://github.com/rails/rails/blob/682d7c7035fed76c42ba6fefa38973387e80409e/activerecord/lib/active_record/relation.rb#L572
212
+ # @return [String]
213
+ def inspect
214
+ entries = to_a.map { |e| "<#{e.class.name}:#{e['id']}>" }
215
+ "#<#{self.class.name} [#{entries.join(', ')}]>"
216
+ end
217
+
218
+ # Returns "show" path for the resource
219
+ # @raise [ArgumentError] when ID is not included in criteria
220
+ # @return [String]
221
+ def resource_path
222
+ id = @scope[:only] or raise ArgumentError, 'No route matches source path without an ID'
223
+ "#{collection_name}/#{id}"
224
+ end
225
+
226
+ private
227
+
228
+ # Builds comma-separated query param
229
+ # @param param [Array, String]
230
+ def param_to_request_array(param)
231
+ case param
232
+ when Array
233
+ param.join(',').gsub(/\s+/, '')
234
+ when String, Symbol
235
+ param.to_s.gsub(/\s+/, '')
236
+ else
237
+ raise ArgumentError, "Expected Array, got #{param.class.name}"
238
+ end
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,4 @@
1
+ module Mavenlink
2
+ class Resolution < Model
3
+ end
4
+ end
@@ -0,0 +1,22 @@
1
+ module Mavenlink
2
+ class Response < BrainstemAdaptor::Response
3
+
4
+ attr_reader :client
5
+
6
+ # @param response_data [String, Hash]
7
+ # @param specification [BrainstemAdaptor::Specification]
8
+ # @param client [Mavenlink::Client]
9
+ def initialize(response_data, client = Mavenlink.client, specification = Mavenlink.specification)
10
+ @client = client
11
+ super(response_data, specification)
12
+ end
13
+
14
+ # Returns collection records
15
+ # Wraps brainstem records into mavenlink models
16
+ # @override
17
+ # @return [Array<MavenLink::Model>]
18
+ def results
19
+ super.map { |record| Mavenlink::Model.models[record.collection_name].wrap(record, client) }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,5 @@
1
+ module Mavenlink
2
+ class Role < Model
3
+ validates :name, presence: true
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ module Mavenlink
2
+ class Settings < ActiveSupport::HashWithIndifferentAccess
3
+ DEFAULTS = {perform_validations: false}.freeze
4
+
5
+ def self.[](key)
6
+ (@instances ||= {})[key] ||= self.new.tap do |defaults|
7
+ defaults.merge!(DEFAULTS)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,4 @@
1
+ module Mavenlink
2
+ class Skill < Model
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Mavenlink
2
+ class SkillCategory < Model
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Mavenlink
2
+ class SkillMembership < Model
3
+ end
4
+ end
@@ -0,0 +1,13 @@
1
+ module Mavenlink
2
+ module Specificators
3
+ class Association < Base
4
+
5
+ # Defines attributes in model described in specification
6
+ def apply
7
+ (model_class.specification['associations'] || {}).keys.each do |association_name|
8
+ model_class.association association_name
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Mavenlink
2
+ module Specificators
3
+ class Attribute < Base
4
+
5
+ # Defines attributes in model described in specification
6
+ def apply
7
+ (model_class.available_attributes).each do |attr|
8
+ model_class.attribute attr
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ module Mavenlink
2
+ module Specificators
3
+
4
+ # Specificator is used to inject anything described in specification file into your model.
5
+ class Base
6
+ attr_reader :model_class
7
+
8
+ # @param model_class [Class]
9
+ def initialize(model_class)
10
+ @model_class = model_class
11
+ end
12
+
13
+ # Injects things described in specification file into the model
14
+ def apply
15
+ raise NotImplementedError
16
+ end
17
+
18
+ # @param model_class [Class]
19
+ def self.apply(model_class)
20
+ self.new(model_class).apply
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ module Mavenlink
2
+ module Specificators
3
+ class Validation < Base
4
+
5
+ # Defines attributes in model described in specification
6
+ def apply
7
+ (model_class.specification['validations'] || {}).each do |fields, options|
8
+ model_class.validates(*fields, to_validation_options(options))
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ # Returns hash for validation options
15
+ # @param options [Hash]
16
+ # @return [Hash]
17
+ def to_validation_options(options)
18
+ options.tap do
19
+ if options.is_a?(Hash)
20
+ options.symbolize_keys!
21
+ options.keys.each { |key| to_validation_options(options[key]) }
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end