app_rail-airtable 0.3.6 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/Gemfile +7 -5
- data/Gemfile.lock +19 -33
- data/README.md +39 -5
- data/Rakefile +5 -3
- data/app_rail-airtable.gemspec +22 -18
- data/bin/ara_generator +4 -2
- data/bin/console +4 -3
- data/examples/schemas/daily_logs.json +86 -0
- data/examples/schemas/locations.json +85 -0
- data/examples/schemas/users.json +51 -0
- data/lib/app_rail/airtable/application_record.rb +24 -21
- data/lib/app_rail/airtable/authenticatable.rb +31 -18
- data/lib/app_rail/airtable/authentication_helpers.rb +10 -7
- data/lib/app_rail/airtable/generator.rb +17 -2
- data/lib/app_rail/airtable/sinatra.rb +37 -25
- data/lib/app_rail/airtable/string_ext.rb +4 -2
- data/lib/app_rail/airtable/version.rb +3 -1
- data/lib/app_rail/airtable.rb +9 -7
- data/templates/project/Gemfile +2 -1
- data/templates/project/config.ru +2 -0
- data/templates/project/lib/server.rb.tt +6 -0
- data/templates/project/spec/server_spec.rb.tt +34 -0
- data/templates/project/spec/spec_helper.rb +56 -56
- metadata +40 -9
- data/templates/project/Gemfile.lock +0 -96
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 635c1b4e200ee31f032f974ca3da84df53b766825c10ac3d4c867ac5b1533c2b
|
4
|
+
data.tar.gz: 5949f7165d856d5bfab33fb42cf157b859346651c5b927aa548b098841a5f5a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 352a2c5566fa8b24966d8ce524a6d9b8fc0ebc91b3559b79d0bff16fd9c5436a9bbe6a0893084f15c4fbdd071382da25b455e7b915a271a5a45fcf8c11f9f5c6
|
7
|
+
data.tar.gz: 2febff28b74cf2b538b6d8767fc308946d48f7e75bff35991d8e30e4ac29a6794e21d75d37e213ef51a3d9dcf791a078c4890ec62fd931904d7199f74fd54d69
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# 0.4.0
|
2
|
+
|
3
|
+
### Breaking changes
|
4
|
+
* `AppRail::Airtable::Sinatra` `.resources` and `.authenticable_resources` `only` argument defaults to `[:index, :show, :create, :update]` instead of `[:index, :show, :create]`
|
5
|
+
|
6
|
+
### New features
|
7
|
+
* Add `Authenticatable#initialize` method to initialize an Airrecord record without persisting it to airtable
|
8
|
+
* Add `update` route (`PUT`), which points to the corresponding model method `self.update_as_json`
|
9
|
+
|
data/Gemfile
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source 'https://rubygems.org'
|
2
4
|
|
3
5
|
# Specify your gem's dependencies in app_rail-airtable.gemspec
|
4
6
|
gemspec
|
5
7
|
|
6
|
-
gem
|
7
|
-
gem
|
8
|
-
gem
|
9
|
-
gem
|
8
|
+
gem 'byebug'
|
9
|
+
gem 'rake', '~> 12.0'
|
10
|
+
gem 'rspec', '~> 3.0'
|
11
|
+
gem 'thor'
|
data/Gemfile.lock
CHANGED
@@ -1,62 +1,48 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
app_rail-airtable (0.
|
4
|
+
app_rail-airtable (0.4.1)
|
5
5
|
activesupport
|
6
6
|
airrecord
|
7
7
|
bcrypt
|
8
|
+
faraday (~> 2.2)
|
9
|
+
faraday-net_http_persistent (~> 2.0)
|
8
10
|
sinatra
|
9
11
|
thor
|
10
12
|
|
11
13
|
GEM
|
12
14
|
remote: https://rubygems.org/
|
13
15
|
specs:
|
14
|
-
activesupport (7.0.2.
|
16
|
+
activesupport (7.0.2.3)
|
15
17
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
16
18
|
i18n (>= 1.6, < 2)
|
17
19
|
minitest (>= 5.1)
|
18
20
|
tzinfo (~> 2.0)
|
19
|
-
airrecord (1.0.
|
20
|
-
faraday (>= 0.10, <
|
21
|
+
airrecord (1.0.9)
|
22
|
+
faraday (>= 0.10, < 3.0)
|
23
|
+
faraday-net_http_persistent
|
21
24
|
net-http-persistent
|
22
|
-
bcrypt (3.1.
|
25
|
+
bcrypt (3.1.17)
|
23
26
|
byebug (11.1.3)
|
24
|
-
concurrent-ruby (1.1.
|
27
|
+
concurrent-ruby (1.1.10)
|
25
28
|
connection_pool (2.2.5)
|
26
29
|
diff-lcs (1.4.4)
|
27
|
-
faraday (
|
28
|
-
faraday-
|
29
|
-
faraday-em_synchrony (~> 1.0)
|
30
|
-
faraday-excon (~> 1.1)
|
31
|
-
faraday-httpclient (~> 1.0)
|
32
|
-
faraday-multipart (~> 1.0)
|
33
|
-
faraday-net_http (~> 1.0)
|
34
|
-
faraday-net_http_persistent (~> 1.0)
|
35
|
-
faraday-patron (~> 1.0)
|
36
|
-
faraday-rack (~> 1.0)
|
37
|
-
faraday-retry (~> 1.0)
|
30
|
+
faraday (2.2.0)
|
31
|
+
faraday-net_http (~> 2.0)
|
38
32
|
ruby2_keywords (>= 0.0.4)
|
39
|
-
faraday-
|
40
|
-
faraday-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
multipart-post (>= 1.2, < 3)
|
45
|
-
faraday-net_http (1.0.1)
|
46
|
-
faraday-net_http_persistent (1.2.0)
|
47
|
-
faraday-patron (1.0.0)
|
48
|
-
faraday-rack (1.0.0)
|
49
|
-
faraday-retry (1.0.3)
|
50
|
-
i18n (1.9.1)
|
33
|
+
faraday-net_http (2.0.2)
|
34
|
+
faraday-net_http_persistent (2.0.1)
|
35
|
+
faraday-net_http
|
36
|
+
net-http-persistent (~> 4.0)
|
37
|
+
i18n (1.10.0)
|
51
38
|
concurrent-ruby (~> 1.0)
|
52
39
|
minitest (5.15.0)
|
53
|
-
multipart-post (2.1.1)
|
54
40
|
mustermann (1.1.1)
|
55
41
|
ruby2_keywords (~> 0.0.1)
|
56
42
|
net-http-persistent (4.0.1)
|
57
43
|
connection_pool (~> 2.2)
|
58
44
|
rack (2.2.3)
|
59
|
-
rack-protection (2.
|
45
|
+
rack-protection (2.2.0)
|
60
46
|
rack
|
61
47
|
rack-test (1.1.0)
|
62
48
|
rack (>= 1.0, < 3)
|
@@ -75,10 +61,10 @@ GEM
|
|
75
61
|
rspec-support (~> 3.10.0)
|
76
62
|
rspec-support (3.10.2)
|
77
63
|
ruby2_keywords (0.0.5)
|
78
|
-
sinatra (2.
|
64
|
+
sinatra (2.2.0)
|
79
65
|
mustermann (~> 1.0)
|
80
66
|
rack (~> 2.2)
|
81
|
-
rack-protection (= 2.
|
67
|
+
rack-protection (= 2.2.0)
|
82
68
|
tilt (~> 2.0)
|
83
69
|
thor (1.1.0)
|
84
70
|
tilt (2.0.10)
|
data/README.md
CHANGED
@@ -2,10 +2,6 @@
|
|
2
2
|
|
3
3
|
App Rail Airtable is a micro-framework based on Sinatra and [Airrecord](https://github.com/sirupsen/airrecord) which faciliates use of Airtable as a backend for App Rail Apps. You can deploy a template repo to Heroku for fast start.
|
4
4
|
|
5
|
-
## Quick Start
|
6
|
-
|
7
|
-
Visit [App Rail Airtable Template](https://github.com/FutureWorkshops/AppRailAirtableTemplate) and follow instructions.
|
8
|
-
|
9
5
|
## Installation
|
10
6
|
|
11
7
|
Add this line to your application's Gemfile:
|
@@ -16,7 +12,7 @@ gem 'app_rail-airtable'
|
|
16
12
|
|
17
13
|
## Usage
|
18
14
|
|
19
|
-
App Rail Airtable has two important concepts, models and servers.
|
15
|
+
App Rail Airtable has two important concepts, models and servers.
|
20
16
|
|
21
17
|
### Models
|
22
18
|
App Rail Airtable makes the following assumptions
|
@@ -32,6 +28,7 @@ To provide support for routes, models can implement
|
|
32
28
|
* ar_list_item (index)
|
33
29
|
* ar_stack (show)
|
34
30
|
* self.create_as_json (create)
|
31
|
+
* self.update_as_json (update)
|
35
32
|
|
36
33
|
In order to support authentication you should create a class (normally `User`) and inherit from `AppRail::Airtable::AuthenticationRecord`. Your table needs `Email`, `Password Hash` and `Access Token` columns.
|
37
34
|
|
@@ -45,10 +42,47 @@ Creates routes that map to a table. It delegates from the route to a model metho
|
|
45
42
|
* `index` to `ar_list_item`
|
46
43
|
* `show` to `ar_stack`
|
47
44
|
* `create` to `self.create_as_json`
|
45
|
+
* `update` to `self.update_as_json`
|
48
46
|
|
49
47
|
**authenticable_resources(name, , only:)**
|
50
48
|
Acts as `resources` but also takes a block of routes. Those nested routes will all call the `authenticate!` helper method before running. The `authenticate!` helper will call a lookup helper `find_authenticatable_resource(access_token:)` with the bearer token found in the `HTTP_AUTHORIZATION` header, which should be used to look up the correct object or return nil if none is found (resulting in a 401 response).
|
51
49
|
|
50
|
+
### Generator
|
51
|
+
|
52
|
+
```bash
|
53
|
+
bin/ara_generator <output_directory> <airtable_api_key> <airtable_base_id> [<json_schema>]
|
54
|
+
```
|
55
|
+
|
56
|
+
#### Example
|
57
|
+
|
58
|
+
```bash
|
59
|
+
bin/ara_generator ./test key1234567890 appwertyuiop '{"tables": [{"name": "DailyLogs", "fields": [{"name": "Date", "type": "date"}, {"name": "Score", "type": "integer"}], "associations": [{"name": "User", "type": "belongs_to", "model": "User"}], "ar_class_methods": [{"name": "ar_list_item", "properties": [{"type": "text", "value": "date"}, {"type": "detail_text", "value": "score"}]}], "ar_instance_methods": [{"name": "ar_stack_item", "properties": [{"type": "text", "label": "Date", "value": "date"}, {"type": "button", "label": "Score", "value": "score", "modalWorkflow": "", "style": "primary", "onSuccess": "forward", "sfSymbolName": ""}]}]}, {"name": "Users", "fields": [{"name": "Email", "type": "string"}, {"name": "Password Hash", "type": "string"}, {"name": "Access Token", "type": "string"}], "associations": [{"name": "DailyLogs", "type": "has_many", "model": "DailyLog"}], "authenticatable": "True"}]}'
|
60
|
+
```
|
61
|
+
|
62
|
+
You can find more examples of schemas in [/examples/schemas](/examples/schemas)
|
63
|
+
|
64
|
+
## Debugging
|
65
|
+
|
66
|
+
### Listing Existing Routes
|
67
|
+
To get a complete list of the available routes in your Sinatra application, run the following code snippet:
|
68
|
+
```ruby
|
69
|
+
Server.routes.map do |method, routes|
|
70
|
+
routes.map { |r| r.first.to_s }.map do |route|
|
71
|
+
"#{method.rjust(7, ' ')} #{route}"
|
72
|
+
end
|
73
|
+
end.flatten.sort.each do |route|
|
74
|
+
puts route
|
75
|
+
end
|
76
|
+
```
|
77
|
+
Output:
|
78
|
+
```
|
79
|
+
GET /mock_items
|
80
|
+
GET /mock_items/:id
|
81
|
+
PUT /mock_items/:id
|
82
|
+
HEAD /mock_items
|
83
|
+
HEAD /mock_items/:id
|
84
|
+
POST /mock_items
|
85
|
+
```
|
52
86
|
## License
|
53
87
|
|
54
88
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
data/app_rail-airtable.gemspec
CHANGED
@@ -1,35 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'lib/app_rail/airtable/version'
|
2
4
|
|
3
5
|
Gem::Specification.new do |spec|
|
4
|
-
spec.name =
|
6
|
+
spec.name = 'app_rail-airtable'
|
5
7
|
spec.version = AppRail::Airtable::VERSION
|
6
|
-
spec.authors = [
|
7
|
-
spec.email = [
|
8
|
+
spec.authors = ['Matt Brooke-Smith']
|
9
|
+
spec.email = ['matt@futureworkshops.com']
|
8
10
|
|
9
|
-
spec.summary =
|
10
|
-
spec.homepage =
|
11
|
-
spec.license =
|
12
|
-
spec.required_ruby_version = Gem::Requirement.new(
|
11
|
+
spec.summary = 'Gem to help building App Rail servers using Airtable as a backend'
|
12
|
+
spec.homepage = 'https://github.com/FutureWorkshops/app_rail-airtable'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
|
13
15
|
|
14
|
-
spec.metadata[
|
15
|
-
spec.metadata[
|
16
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
17
|
+
spec.metadata['source_code_uri'] = 'https://github.com/FutureWorkshops/app_rail-airtable'
|
16
18
|
|
17
19
|
# Specify which files should be added to the gem when it is released.
|
18
20
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
19
|
-
spec.files
|
21
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
20
22
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
21
23
|
end
|
22
|
-
|
24
|
+
|
23
25
|
spec.bindir = 'bin'
|
24
26
|
spec.executables << 'ara_generator'
|
25
|
-
|
26
|
-
spec.require_paths = [
|
27
|
+
|
28
|
+
spec.require_paths = ['lib']
|
27
29
|
spec.add_dependency 'activesupport'
|
28
|
-
spec.add_dependency 'sinatra'
|
29
30
|
spec.add_dependency 'airrecord'
|
30
|
-
spec.add_dependency 'thor'
|
31
31
|
spec.add_dependency 'bcrypt'
|
32
|
-
|
33
|
-
spec.
|
32
|
+
spec.add_dependency 'faraday', '~> 2.2'
|
33
|
+
spec.add_dependency 'faraday-net_http_persistent', '~> 2.0' # workaround to make Faraday work after upgrading to 2.2.0
|
34
|
+
spec.add_dependency 'sinatra'
|
35
|
+
spec.add_dependency 'thor'
|
36
|
+
|
34
37
|
spec.add_development_dependency 'rack-test'
|
35
|
-
|
38
|
+
spec.add_development_dependency 'rspec'
|
39
|
+
end
|
data/bin/ara_generator
CHANGED
data/bin/console
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require
|
4
|
-
require
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'app_rail/airtable'
|
5
6
|
|
6
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
8
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +11,5 @@ require "app_rail/airtable"
|
|
10
11
|
# require "pry"
|
11
12
|
# Pry.start
|
12
13
|
|
13
|
-
require
|
14
|
+
require 'irb'
|
14
15
|
IRB.start(__FILE__)
|
@@ -0,0 +1,86 @@
|
|
1
|
+
{
|
2
|
+
"tables": [
|
3
|
+
{
|
4
|
+
"name": "DailyLogs",
|
5
|
+
"fields": [
|
6
|
+
{
|
7
|
+
"name": "Date",
|
8
|
+
"type": "date"
|
9
|
+
},
|
10
|
+
{
|
11
|
+
"name": "Score",
|
12
|
+
"type": "integer"
|
13
|
+
}
|
14
|
+
],
|
15
|
+
"associations": [
|
16
|
+
{
|
17
|
+
"name": "User",
|
18
|
+
"type": "belongs_to",
|
19
|
+
"model": "User"
|
20
|
+
}
|
21
|
+
],
|
22
|
+
"ar_class_methods": [
|
23
|
+
{
|
24
|
+
"name": "ar_list_item",
|
25
|
+
"properties": [
|
26
|
+
{
|
27
|
+
"type": "text",
|
28
|
+
"value": "date"
|
29
|
+
},
|
30
|
+
{
|
31
|
+
"type": "detail_text",
|
32
|
+
"value": "score"
|
33
|
+
}
|
34
|
+
]
|
35
|
+
}
|
36
|
+
],
|
37
|
+
"ar_instance_methods": [
|
38
|
+
{
|
39
|
+
"name": "ar_stack_item",
|
40
|
+
"properties": [
|
41
|
+
{
|
42
|
+
"type": "text",
|
43
|
+
"label": "Date",
|
44
|
+
"value": "date"
|
45
|
+
},
|
46
|
+
{
|
47
|
+
"type": "button",
|
48
|
+
"label": "Score",
|
49
|
+
"value": "score",
|
50
|
+
"modalWorkflow": "",
|
51
|
+
"style": "primary",
|
52
|
+
"onSuccess": "forward",
|
53
|
+
"sfSymbolName": ""
|
54
|
+
}
|
55
|
+
]
|
56
|
+
}
|
57
|
+
],
|
58
|
+
"authenticatable": "True"
|
59
|
+
},
|
60
|
+
{
|
61
|
+
"name": "Users",
|
62
|
+
"fields": [
|
63
|
+
{
|
64
|
+
"name": "Email",
|
65
|
+
"type": "string"
|
66
|
+
},
|
67
|
+
{
|
68
|
+
"name": "Password Hash",
|
69
|
+
"type": "string"
|
70
|
+
},
|
71
|
+
{
|
72
|
+
"name": "Access Token",
|
73
|
+
"type": "string"
|
74
|
+
}
|
75
|
+
],
|
76
|
+
"associations": [
|
77
|
+
{
|
78
|
+
"name": "DailyLogs",
|
79
|
+
"type": "has_many",
|
80
|
+
"model": "DailyLog"
|
81
|
+
}
|
82
|
+
],
|
83
|
+
"authenticatable": "True"
|
84
|
+
}
|
85
|
+
]
|
86
|
+
}
|
@@ -0,0 +1,85 @@
|
|
1
|
+
{
|
2
|
+
"tables": [
|
3
|
+
{
|
4
|
+
"name": "DailyLogs",
|
5
|
+
"fields": [
|
6
|
+
{
|
7
|
+
"name": "Date",
|
8
|
+
"type": "date"
|
9
|
+
},
|
10
|
+
{
|
11
|
+
"name": "Score",
|
12
|
+
"type": "integer"
|
13
|
+
}
|
14
|
+
],
|
15
|
+
"associations": [
|
16
|
+
{
|
17
|
+
"name": "User",
|
18
|
+
"type": "belongs_to",
|
19
|
+
"model": "User"
|
20
|
+
}
|
21
|
+
],
|
22
|
+
"ar_class_methods": [
|
23
|
+
{
|
24
|
+
"name": "ar_list_item",
|
25
|
+
"properties": [
|
26
|
+
{
|
27
|
+
"type": "text",
|
28
|
+
"value": "date"
|
29
|
+
},
|
30
|
+
{
|
31
|
+
"type": "detail_text",
|
32
|
+
"value": "score"
|
33
|
+
}
|
34
|
+
]
|
35
|
+
}
|
36
|
+
],
|
37
|
+
"ar_instance_methods": [
|
38
|
+
{
|
39
|
+
"name": "ar_stack_item",
|
40
|
+
"properties": [
|
41
|
+
{
|
42
|
+
"type": "text",
|
43
|
+
"label": "Date",
|
44
|
+
"value": "date"
|
45
|
+
},
|
46
|
+
{
|
47
|
+
"type": "button",
|
48
|
+
"label": "Score",
|
49
|
+
"value": "score",
|
50
|
+
"modalWorkflow": "",
|
51
|
+
"style": "primary",
|
52
|
+
"onSuccess": "forward",
|
53
|
+
"sfSymbolName": ""
|
54
|
+
}
|
55
|
+
]
|
56
|
+
}
|
57
|
+
]
|
58
|
+
},
|
59
|
+
{
|
60
|
+
"name": "Users",
|
61
|
+
"fields": [
|
62
|
+
{
|
63
|
+
"name": "Email",
|
64
|
+
"type": "string"
|
65
|
+
},
|
66
|
+
{
|
67
|
+
"name": "Password Hash",
|
68
|
+
"type": "string"
|
69
|
+
},
|
70
|
+
{
|
71
|
+
"name": "Access Token",
|
72
|
+
"type": "string"
|
73
|
+
}
|
74
|
+
],
|
75
|
+
"associations": [
|
76
|
+
{
|
77
|
+
"name": "DailyLogs",
|
78
|
+
"type": "has_many",
|
79
|
+
"model": "DailyLog"
|
80
|
+
}
|
81
|
+
],
|
82
|
+
"authenticatable": "True"
|
83
|
+
}
|
84
|
+
]
|
85
|
+
}
|
@@ -0,0 +1,51 @@
|
|
1
|
+
{
|
2
|
+
"tables": [
|
3
|
+
{
|
4
|
+
"name": "Users",
|
5
|
+
"fields": [
|
6
|
+
{
|
7
|
+
"name": "Forename",
|
8
|
+
"type": "string"
|
9
|
+
},
|
10
|
+
{
|
11
|
+
"name": "Surname",
|
12
|
+
"type": "string"
|
13
|
+
},
|
14
|
+
{
|
15
|
+
"name": "NHS Number",
|
16
|
+
"type": "integer"
|
17
|
+
},
|
18
|
+
{
|
19
|
+
"name": "Date of Birth",
|
20
|
+
"type": "date"
|
21
|
+
},
|
22
|
+
{
|
23
|
+
"name": "Vaccine",
|
24
|
+
"type": "string"
|
25
|
+
}
|
26
|
+
],
|
27
|
+
"associations": [
|
28
|
+
{
|
29
|
+
"name": "Vaccination Appointments",
|
30
|
+
"type": "has_many",
|
31
|
+
"model": "VaccinationAppointment"
|
32
|
+
}
|
33
|
+
],
|
34
|
+
"ar_class_methods": [
|
35
|
+
{
|
36
|
+
"name": "ar_list_item",
|
37
|
+
"properties": [
|
38
|
+
{
|
39
|
+
"type": "text",
|
40
|
+
"value": "forename"
|
41
|
+
},
|
42
|
+
{
|
43
|
+
"type": "detail_text",
|
44
|
+
"value": "surname"
|
45
|
+
}
|
46
|
+
]
|
47
|
+
}
|
48
|
+
]
|
49
|
+
}
|
50
|
+
]
|
51
|
+
}
|
@@ -1,64 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'airrecord'
|
4
|
+
require 'app_rail-steps'
|
2
5
|
require 'active_support/core_ext/string/inflections'
|
3
6
|
|
4
7
|
module AppRail
|
5
8
|
module Airtable
|
6
9
|
class ApplicationRecord < Airrecord::Table
|
7
10
|
include ActiveSupport::Inflector
|
8
|
-
|
11
|
+
include AppRail::Steps::Displayable
|
12
|
+
|
9
13
|
def self.base_key
|
10
|
-
ENV.fetch(
|
14
|
+
ENV.fetch('AIRTABLE_BASE_ID')
|
11
15
|
end
|
12
|
-
|
16
|
+
|
13
17
|
def self.table_name
|
14
|
-
|
18
|
+
name.pluralize
|
15
19
|
end
|
16
|
-
|
20
|
+
|
17
21
|
def self.airtable_attr(*attributes)
|
18
22
|
attributes.each do |attribute|
|
19
23
|
define_method(attribute.to_s.snake_case) { self[attribute] }
|
20
24
|
define_method("#{attribute.to_s.snake_case}=") { |value| self[attribute] = value }
|
21
25
|
end
|
22
26
|
end
|
23
|
-
|
27
|
+
|
24
28
|
# Step utilities
|
25
29
|
def self.ar_list_item(text:, detail_text: nil, image: nil, sf_symbol: nil, material_icon: nil)
|
26
30
|
define_method(:ar_list_item_as_json) do
|
27
31
|
{
|
28
|
-
id:
|
29
|
-
text: method_value(text).to_s,
|
30
|
-
detailText: method_value(detail_text).to_s,
|
31
|
-
imageURL: method_value(image),
|
32
|
-
sfSymbolName: method_value(sf_symbol),
|
32
|
+
id: id,
|
33
|
+
text: method_value(text).to_s,
|
34
|
+
detailText: method_value(detail_text).to_s,
|
35
|
+
imageURL: method_value(image),
|
36
|
+
sfSymbolName: method_value(sf_symbol),
|
33
37
|
materialIconName: method_value(material_icon)
|
34
38
|
}.compact
|
35
39
|
end
|
36
40
|
end
|
37
|
-
|
41
|
+
|
38
42
|
def self.ar_stack(text_items:)
|
39
43
|
define_method(:ar_stack_as_json) do
|
40
|
-
text_items.map{|ti| {type: :text, label: ti, text: self[ti].to_s} }
|
44
|
+
text_items.map { |ti| { type: :text, label: ti, text: self[ti].to_s } }
|
41
45
|
end
|
42
46
|
end
|
43
|
-
|
47
|
+
|
44
48
|
# Customisable behaviour
|
45
|
-
|
49
|
+
|
46
50
|
# Override to provide custom sorting or filtering for index
|
47
51
|
def self.index(user:)
|
48
52
|
all
|
49
53
|
end
|
50
|
-
|
54
|
+
|
51
55
|
private
|
52
|
-
|
56
|
+
|
53
57
|
def method_value(name)
|
54
58
|
send(name) if name.present?
|
55
59
|
end
|
56
|
-
|
60
|
+
|
57
61
|
# size is either :small, :large or :full
|
58
62
|
def image(name, index: 0, size: :full)
|
59
|
-
self[name][index][
|
63
|
+
self[name][index]['thumbnails'][size.to_s]['url'] if self[name] && self[name].length > index
|
60
64
|
end
|
61
|
-
|
62
65
|
end
|
63
66
|
end
|
64
|
-
end
|
67
|
+
end
|