task_helper 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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