task_helper 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 51608c11313019a92faf2883d4eccefbff7ee436
4
+ data.tar.gz: 50fbda3969ac23aaeca972c5374f6dbc476a633f
5
+ SHA512:
6
+ metadata.gz: 9c2626aba9b0d710000be2c6d3f4c5fb82fb303dfbf101dd731f45be5fd56246560ac80048118354078d003709d95a0fae6a8c4a5943d1693a75727c664240fe
7
+ data.tar.gz: db5c31c47fcf0fe63196fd1e2bd13c34a66d88147559b2891674fd05c7ba32fcc70697e805b38953f6dc1b70be168561fa12ab25cbb8efbbadb486b1f16f2b7a
data/.gitignore ADDED
@@ -0,0 +1,30 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/support/fixtures/**/*.json
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ ## Documentation cache and generated files:
14
+ /.yardoc/
15
+ /_yardoc/
16
+ /doc/
17
+ /rdoc/
18
+
19
+ ## Environment normalisation:
20
+ /.bundle/
21
+ /lib/bundler/man/
22
+
23
+ # for a library or gem, you might want to ignore these files since the code is
24
+ # intended to run in multiple environments; otherwise, check them in:
25
+ Gemfile.lock
26
+ .ruby-version
27
+ .ruby-gemset
28
+
29
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
30
+ .rvmrc
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format documentation
3
+ --require spec_helper
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ mth
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.1.1
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 JC Wilcox
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,317 @@
1
+ # TaskHelper
2
+
3
+ Offers a clean Ruby interface for interacting with
4
+ the MyTaskHelper.com API.
5
+
6
+ Includes wrapper classes for Database, Form, Field, and Record.
7
+ These classes offer shorthand methods for common API calls, as
8
+ well as convenient methods to read the data returned from the API.
9
+
10
+ Also includes an `API` module, which can be used to directly access
11
+ any available route in the API.
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'task_helper'
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install task_helper
26
+
27
+ ## Usage
28
+
29
+ ### Resources
30
+
31
+ TaskHelper provides a ruby interface to four MyTaskHelper resources:
32
+
33
+ * Database
34
+ * Form
35
+ * Field
36
+ * Record
37
+
38
+ This gem offers classes to interact with each of these resources in
39
+ an ActiveRecord like fashion. Each class contains methods for fetching
40
+ objects from the API, readers for attributes of retrieved objects,
41
+ and methods to navigate between related resources.
42
+
43
+ #### Database
44
+
45
+ This class offers functionality to retrieve and interact with databases
46
+ in MyTaskHelper.
47
+
48
+ ##### API Access Methods
49
+
50
+ The `TaskHelper::Database` class offers singleton methods, which mimic the
51
+ functionality of their ActiveRecord counterparts.
52
+
53
+ ###### All
54
+
55
+ `TaskHelper::Database.all` returns an Array of all databases returned from
56
+ 'mytaskhelper.com/apps.json'.
57
+
58
+ TaskHelper::Database.all # GET 'apps.json'
59
+ => [#<TaskHelper::Database>, ...]
60
+
61
+ ###### Find
62
+
63
+ `TaskHelper::Database.find` accepts a string corresponding to the id of a
64
+ database in MyTaskHelper, and returns the database (if any) found at
65
+ 'mytaskhelper.com/apps/:id.json'.
66
+
67
+ TaskHelper::Database.find('foobar123') # GET 'apps/foobar123.json'
68
+ => #<TaskHelper::Database id: 'foobar123'>
69
+
70
+ ###### Find By
71
+
72
+ `TaskHelper::Database.find_by` accepts a hash of search parameters, passes those
73
+ parameters to the 'mytaskhelper.com/apps/search.json' route, and returns the
74
+ corresponding database (if any are found).
75
+
76
+ TaskHelper::Database.find_by(name: 'Foo Bar') # GET 'apps/search.json?name=Foo%20Bar'
77
+ => #<TaskHelper::Database name: 'Foo Bar'>
78
+
79
+ ##### Attributes
80
+
81
+ A database object contains the following attributes:
82
+
83
+ * id
84
+ * name
85
+ * dtypes_count
86
+ * entities_count
87
+ * properties_count
88
+ * created_at
89
+ * updated_at
90
+
91
+ ##### Relations
92
+
93
+ `TaskHelper::Database` objects respond to the `.forms` method, which returns
94
+ an Array of `TaskHelper::Form`s associated with that database.
95
+
96
+ database = TaskHelper::Database.find('foobar123')
97
+ database.forms # GET 'apps/foobar123/entities.json'
98
+ => [#<TaskHelper::Form app_id: 'foobar123'>, ...]
99
+
100
+ #### Form
101
+
102
+ This class offers functionality to retrieve and interact with forms
103
+ in MyTaskHelper.
104
+
105
+ ##### API Access Methods
106
+
107
+ The `TaskHelper::Form` class offers singleton methods, which mimic the
108
+ functionality of their ActiveRecord counterparts.
109
+
110
+ ###### All
111
+
112
+ `TaskHelper::Form.all` returns an Array of all forms associated with
113
+ any database from 'mytaskhelper.com/apps.json'.
114
+
115
+ TaskHelper::Form.all
116
+ => [#<TaskHelper::Form>, ...]
117
+
118
+ ###### Find
119
+
120
+ `TaskHelper::Form.find` accepts two named parameters: database and form.
121
+ Each parameter should be a string corresponding to the id of that resource,
122
+ and returns the form (if any) found at
123
+ 'mytaskhelper.com/apps/:database/entities/:form.json'.
124
+
125
+ params = { database: 'foobar123', form: 'baz456' }
126
+ TaskHelper::Form.find(params) # GET 'apps/foobar123/entities/baz456.json'
127
+ => #<TaskHelper::Form id: 'baz456'>
128
+
129
+ ###### Find By
130
+
131
+ `TaskHelper::Form.find_by` accepts a hash of search parameters, passes those
132
+ parameters to the 'mytaskhelper.com/apps/search/entities/search.json' route,
133
+ and returns the corresponding form (if any are found).
134
+
135
+ search = { database_name: 'Foo Bar', form_name: 'Baz' }
136
+ TaskHelper::Form.find_by(search) # GET 'apps/search/entities/search.json?database_name=Foo%20Bar&form_name=Baz'
137
+ => #<TaskHelper::Form name: 'Baz'>
138
+
139
+ ##### Attributes
140
+
141
+ A form object contains the following attributes:
142
+
143
+ * id
144
+ * app_id
145
+ * name
146
+ * desc
147
+ * post_action
148
+ * position
149
+ * sort_by
150
+ * asc
151
+ * per_page
152
+ * allow_delete
153
+ * new_widget
154
+ * records_widget
155
+ * target_page
156
+ * allow_database
157
+ * send_emails
158
+ * settings
159
+
160
+ ##### Relations
161
+
162
+ `TaskHelper::Form` objects respond to the `.database` method, which returns
163
+ the database associated with the form.
164
+
165
+ params = { database: 'foobar123', form: 'baz456' }
166
+ form = TaskHelper::Form.find(params)
167
+ form.database # GET 'apps/foobar123.json'
168
+ => #<TaskHelper::Database id: 'foobar123'>
169
+
170
+ `TaskHelper::Form` objects also respond to the `.fields` method, which returns
171
+ an Array of `TaskHelper::Field`s associated with that form.
172
+
173
+ params = { database: 'foobar123', form: 'baz456' }
174
+ form = TaskHelper::Form.find(params)
175
+ form.fields # GET 'apps/foobar123/entities/baz456/properties.json'
176
+ => [#<TaskHelper::Field entity_id: 'baz456'>, ...]
177
+
178
+ `TaskHelper::Form` objects also respond to the `.records` method, which returns
179
+ a Lazy Enumerator for fetching the records associated with the form.
180
+ The enumerator loops through the pages of records found at
181
+ 'mytaskhelper.com/apps/:app_id/dtypes/entity/:entity_id.json',
182
+ and flattens the results into an Enumerator of `TaskHelper::Record`s.
183
+
184
+ params = { database: 'foobar123', form: 'baz456' }
185
+ form = TaskHelper::Form.find(params)
186
+ records = form.records
187
+ => #<Enumerator::Lazy: #<Enumerator::Lazy: 1..5>:flat_map> # 5 pages of records
188
+ records.first(3) # GET 'apps/foobar123/dtypes/entity/baz456.json?page=1'
189
+ => [#<TaskHelper::Record entity_id: 'baz456'>, ...]
190
+
191
+ #### Field
192
+
193
+ This class offers functionality for interacting with fields
194
+ in MyTaskHelper.
195
+
196
+ ##### API Access
197
+
198
+ Presently, `TaskHelper::Field`s can only be retrieved through their
199
+ associated form. (see `TaskHelper::Form#fields` above)
200
+
201
+ ##### Attributes
202
+
203
+ A field object contains the following attributes:
204
+
205
+ * id
206
+ * entity_id
207
+ * name
208
+ * desc
209
+ * type_name
210
+ * default
211
+ * validate_options
212
+ * position
213
+ * visible
214
+ * size
215
+ * cols
216
+ * rows
217
+ * initial
218
+ * pretty_type_name
219
+ * formula_field
220
+ * formula_operation
221
+ * start_from
222
+ * step
223
+
224
+ ##### Relations
225
+
226
+ `TaskHelper::Field` objects respond to the `.form` method, which returns
227
+ the form associated with the field.
228
+
229
+ params = { database: 'foobar123', form: 'baz456' }
230
+ form = TaskHelper::Form.find(params)
231
+ => #<TaskHelper::Form id: 'baz456'>
232
+ field = form.fields.first
233
+ => #<TaskHelper::Field entity_id: 'baz456'>
234
+ field.form
235
+ => #<TaskHelper::Form id: 'baz456'>
236
+
237
+ #### Record
238
+
239
+ This class offers functionality for interacting with form records
240
+ in MyTaskHelper.
241
+
242
+ ##### API Access
243
+
244
+ Presently, `TaskHelper::Records`s can only be retrieved through their
245
+ associated form. (see `TaskHelper::Form#records` above)
246
+
247
+ ##### Attributes
248
+
249
+ A record object contains the following attributes:
250
+
251
+ * id
252
+ * app_id
253
+ * entity_id
254
+ * approved
255
+ * values
256
+ * created_at
257
+ * updated_at
258
+
259
+ ##### Accessing Values
260
+
261
+ `TaskHelper::Record#values` returns a hash, with the ids of fields associated
262
+ with the record's form as keys, and the values of those fields for the record
263
+ as values.
264
+
265
+ params = { database: 'foobar123', form: 'baz456' }
266
+ form = TaskHelper::Form.find(params)
267
+ => #<TaskHelper::Form id: 'baz456'>
268
+ record = form.records.first
269
+ => #<TaskHelper::Record entity_id: 'baz456'>
270
+ record.values
271
+ => { 'abc123' => 'Yes', 'def456' => false, 'ghi789' => 17 }
272
+
273
+ For convenience, `TaskHelper::Record` objects offer a `.pretty_values` method,
274
+ which parses the values hash, and replaces the field ids with field names.
275
+
276
+ form.fields
277
+ => [#<TaskHelper::Field id: 'abc123' name: 'Winner?'>, ...]
278
+ record.pretty_values
279
+ => { 'Winner?' => 'Yes', 'Jackpot?' => false, 'Prize' => 17 }
280
+
281
+ The value of a given field can be also be accessed by `TaskHelper::Record#[]`
282
+ which accepts a field name, and returns the value of that field.
283
+
284
+ record['Prize']
285
+ => 17
286
+
287
+ ##### Relations
288
+
289
+ `TaskHelper::Record` objects respond to the `.form` method, which returns
290
+ the form associated with the record.
291
+
292
+ params = { database: 'foobar123', form: 'baz456' }
293
+ form = TaskHelper::Form.find(params)
294
+ => #<TaskHelper::Form id: 'baz456'>
295
+ record = form.records.first
296
+ => #<TaskHelper::Record entity_id: 'baz456'>
297
+ record.form
298
+ => #<TaskHelper::Form id: 'baz456'>
299
+
300
+ `TaskHelper::Record` objects also respond to the `.fields` method, which
301
+ returns and array of fields associated with the record's form.
302
+
303
+ params = { database: 'foobar123', form: 'baz456' }
304
+ form = TaskHelper::Form.find(params)
305
+ => #<TaskHelper::Form id: 'baz456'>
306
+ record = form.records.first
307
+ => #<TaskHelper::Record entity_id: 'baz456'>
308
+ record.fields
309
+ => [#<TaskHelper::Field entity_id: 'baz456'>, ...]
310
+
311
+ ## Contributing
312
+
313
+ 1. Fork it ( http://github.com/xDAGRONx/task_helper/fork )
314
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
315
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
316
+ 4. Push to the branch (`git push origin my-new-feature`)
317
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require "bundler/gem_tasks"
2
+ require 'task_helper'
3
+
4
+ load './lib/tasks/spec_setup.rake'
@@ -0,0 +1,28 @@
1
+ module TaskHelper
2
+ module API
3
+ class Cache
4
+ def initialize(limit: 10)
5
+ @limit = limit
6
+ @calls = []
7
+ end
8
+
9
+ def get(**args)
10
+ new_call = Call.new(args)
11
+ cached_call = @calls.find { |call| call == new_call }
12
+ if cached_call
13
+ cached_call.run
14
+ else
15
+ @calls << new_call
16
+ sort_calls.pop if @calls.size > @limit
17
+ new_call.run
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def sort_calls
24
+ @calls.sort! { |x, y| y.time <=> x.time }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,48 @@
1
+ module TaskHelper
2
+ module API
3
+ class Call
4
+ MissingAPIKey = Class.new(StandardError)
5
+
6
+ attr_reader :route, :params, :time
7
+ protected :route, :params
8
+
9
+ def initialize(route:, params: {}, timeout: 314, time: Time.now)
10
+ @params = { rest_api_key: API.rest_api_key }.merge(params)
11
+ if @params[:rest_api_key].nil?
12
+ raise MissingAPIKey, "Rest API key not provided. " \
13
+ "Either pass it as a param or use " \
14
+ "'TaskHelper::API.rest_api_key = key' " \
15
+ "to set it for all future calls."
16
+ end
17
+ @route = "#{BASE_URL}/#{route}"
18
+ @timeout = timeout
19
+ @time = time
20
+ set_end_time
21
+ end
22
+
23
+ def run
24
+ @time = Time.now
25
+ if expired? || !@result
26
+ set_end_time
27
+ @result = HTTParty.get(@route, query: @params).parsed_response
28
+ else
29
+ @result
30
+ end
31
+ end
32
+
33
+ def expired?
34
+ @end_time < Time.now
35
+ end
36
+
37
+ def ==(other_call)
38
+ @route == other_call.route && @params == other_call.params
39
+ end
40
+
41
+ private
42
+
43
+ def set_end_time
44
+ @end_time = @time + @timeout
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,23 @@
1
+ module TaskHelper
2
+ module API
3
+ BASE_URL = 'https://mytaskhelper.com'
4
+
5
+ class << self
6
+ attr_accessor :rest_api_key
7
+ end
8
+
9
+ def get(args)
10
+ @cache.get(args)
11
+ end
12
+
13
+ def set_cache(args={})
14
+ @cache = Cache.new(args)
15
+ end
16
+
17
+ def self.extended(base)
18
+ base.set_cache
19
+ end
20
+
21
+ extend self
22
+ end
23
+ end
@@ -0,0 +1,55 @@
1
+ module TaskHelper
2
+ class Base
3
+ class << self
4
+ attr_accessor :data_members
5
+ protected :data_members=
6
+ end
7
+
8
+ def initialize(args = {})
9
+ @info = {}
10
+ (args || {}).each_pair do |k, v|
11
+ @info[k.to_sym] = v
12
+ end
13
+ end
14
+
15
+ def to_h
16
+ @info.dup
17
+ end
18
+
19
+ def id
20
+ fetch(:id)
21
+ end
22
+
23
+ def created_at
24
+ Time.parse(fetch(:created_at)) if fetch(:created_at)
25
+ end
26
+
27
+ def updated_at
28
+ Time.parse(fetch(:updated_at)) if fetch(:updated_at)
29
+ end
30
+
31
+ def ==(other)
32
+ id == other.id
33
+ end
34
+
35
+ protected
36
+
37
+ def fetch(attribute)
38
+ @info[attribute.to_sym]
39
+ end
40
+
41
+ private
42
+
43
+ def self.inherited(base)
44
+ base.extend(API)
45
+ base.data_members = %i(id)
46
+ end
47
+
48
+ def self.data_member(*names)
49
+ names.each do |name|
50
+ define_method(name) { fetch(name.to_sym) }
51
+ data_members << name.to_sym
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,30 @@
1
+ module TaskHelper
2
+ class Database < Base
3
+ data_member :name, :dtypes_count, :entities_count, :properties_count
4
+
5
+ def self.all
6
+ get(route: 'apps.json')['databases'].map { |d| new d }
7
+ end
8
+
9
+ def self.find_by_name(name)
10
+ find_by(name: name)
11
+ end
12
+
13
+ def self.find_by(search)
14
+ if response = get(route: 'apps/search.json', params: search)
15
+ new response['database'] if response['database']
16
+ end
17
+ end
18
+
19
+ def self.find(id)
20
+ if response = get(route: "apps/#{id}.json")
21
+ new response['database'] if response['database']
22
+ end
23
+ end
24
+
25
+ def forms
26
+ @forms ||= Form.get(route: "apps/#{id}/entities.json")['forms']
27
+ .map { |form| Form.new(form, database: self) }
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,20 @@
1
+ module TaskHelper
2
+ class Field < Base
3
+ data_member :entity_id, :name, :desc, :type_name, :default,
4
+ :validate_options, :position, :visible, :size, :cols,
5
+ :rows, :initial, :pretty_type_name, :formula_field,
6
+ :formula_operation, :start_from, :step
7
+
8
+ def initialize(args = {}, form: nil, **params)
9
+ @form = form
10
+ super(args.merge(params))
11
+ end
12
+
13
+ def form
14
+ @form ||=
15
+ Database.all.each do |d|
16
+ break Form.find(database: d.id, form: entity_id) || next
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,57 @@
1
+ module TaskHelper
2
+ class Form < Base
3
+ data_member :app_id, :name, :desc, :post_action, :position, :sort_by, :asc,
4
+ :per_page, :allow_delete, :new_widget, :records_widget,
5
+ :target_page, :allow_database, :send_emails, :settings
6
+
7
+ def self.all
8
+ Database.all.lazy.flat_map { |db| db.forms }
9
+ end
10
+
11
+ def self.find_by(search)
12
+ if response = get(route: 'apps/search/entities/search.json', params: search)
13
+ new response['form'] if response['form']
14
+ end
15
+ end
16
+
17
+ def self.find(database:, form:)
18
+ if response = get(route: "apps/#{database}/entities/#{form}.json")
19
+ new response['form'] if response['form']
20
+ end
21
+ end
22
+
23
+ def initialize(args = {}, database: nil, **params)
24
+ @database = database
25
+ super(args.merge(params))
26
+ end
27
+
28
+ def database
29
+ @database ||= Database.find(app_id)
30
+ end
31
+
32
+ def fields
33
+ @fields ||= Field.get(
34
+ route: "apps/#{app_id}/entities/#{id}/properties.json")['fields']
35
+ .map { |field| Field.new(field, form: self) }
36
+ end
37
+
38
+ def records
39
+ if fields.any?
40
+ @records ||= (1..page_count).lazy.flat_map do |page|
41
+ record_page(page).lazy.map { |record| Record.new(record, form: self) }
42
+ end
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def page_count
49
+ database.dtypes_count / per_page + 1
50
+ end
51
+
52
+ def record_page(page = 1)
53
+ Record.get(route: "apps/#{app_id}/dtypes/entity/#{id}.json",
54
+ params: { page: page })['records']
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,29 @@
1
+ module TaskHelper
2
+ class Record < Base
3
+ data_member :app_id, :entity_id, :approved, :values
4
+
5
+ def initialize(args = {}, form: nil, **params)
6
+ @form = form
7
+ super(args.merge(params))
8
+ end
9
+
10
+ def form
11
+ @form ||= Form.find(database: app_id, form: entity_id)
12
+ end
13
+
14
+ def fields
15
+ form.fields
16
+ end
17
+
18
+ def [](field_name)
19
+ pretty_values[field_name]
20
+ end
21
+
22
+ def pretty_values
23
+ @pretty_values ||= values.each_with_object({}) do |(k,v), r|
24
+ field = fields.find { |f| f.id == k }
25
+ r[field.name] = v
26
+ end
27
+ end
28
+ end
29
+ end