app_rail-airtable 0.1.0 → 0.5.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f9ec1d3dd50d2b81b46a1f6804319b97424192accf1e825830ade564c4901aa5
4
- data.tar.gz: '0220939ea9dfd1ca57806f60328ca0bd4ea09417fdc2aeb1b61e70687024aeef'
3
+ metadata.gz: 208213f6a53029eb29b4d98ed9b6d4f9bb7bb0375d3a5de3866615c8701ec3d9
4
+ data.tar.gz: 5aea3eda06422225f87170aa3b1d8a8a3310123fb151a554b29ad8059db72847
5
5
  SHA512:
6
- metadata.gz: d13146f0116ea27771ac901dcc514938fa5e3b23e1b1342f6123a76a7e39bdb3b1dd1c7a2ce1bd477cc2a579a7c313ed44e521652f99ca9e8dcb78dba6b7a364
7
- data.tar.gz: e6cfc76d2a99c122d3643dd587bdef60a5e7131f04c4f7d0de0ed60ed76c2c130f6ded6d74123864fa6cb45c15cb904e0c7fcd2e4500720ce2479f4de6ae3132
6
+ metadata.gz: 975099138a2b7aef7ae085a09d7ace624f6b7eba9c892691807505ec44f8322dbf8b3a474113ecb3053b6d8243264db97531607ec2cfc3b4ad8907c253242dc0
7
+ data.tar.gz: 2b07588a9c2c375a0b4574a4500921c40f306c45bf153fd530520b7874716f4fbdb3b4cd56ade60c5dba9827b097052833e8827b4b564a83303af47bb4373af7
data/.gitignore CHANGED
@@ -1,3 +1,7 @@
1
+ .DS_STORE
2
+ *.swp
3
+ *.rbc
4
+ *.sass-cache
1
5
  /.bundle/
2
6
  /.yardoc
3
7
  /_yardoc/
@@ -9,3 +13,7 @@
9
13
 
10
14
  # rspec failure tracking
11
15
  .rspec_status
16
+ .byebug_history
17
+
18
+ # dotenv
19
+ .env
data/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # 0.4.6
2
+ * Add authenticatable_resource POST / DELETE `/<resource>/session` routes for sign in and sign out
3
+ * Simplify sign in methods & remove Authenticatable#create_session_as_json
4
+
5
+ # 0.4.0
6
+
7
+ ### Breaking changes
8
+ * `AppRail::Airtable::Sinatra` `.resources` and `.authenticable_resources` `only` argument defaults to `[:index, :show, :create, :update]` instead of `[:index, :show, :create]`
9
+
10
+ ### New features
11
+ * Add `Authenticatable#initialize` method to initialize an Airrecord record without persisting it to airtable
12
+ * Add `update` route (`PUT`), which points to the corresponding model method `self.update_as_json`
13
+
data/Gemfile CHANGED
@@ -1,7 +1,11 @@
1
- source "https://rubygems.org"
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 "rake", "~> 12.0"
7
- gem "rspec", "~> 3.0"
8
+ gem 'byebug'
9
+ gem 'rake', '~> 12.0'
10
+ gem 'rspec', '~> 3.0'
11
+ gem 'thor'
data/Gemfile.lock CHANGED
@@ -1,55 +1,50 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- app_rail-airtable (0.1.0)
4
+ app_rail-airtable (0.5.0)
5
5
  activesupport
6
6
  airrecord
7
+ app_rail-steps
8
+ bcrypt
9
+ faraday (~> 2.2)
10
+ faraday-net_http_persistent (~> 2.0)
7
11
  sinatra
12
+ thor
8
13
 
9
14
  GEM
10
15
  remote: https://rubygems.org/
11
16
  specs:
12
- activesupport (6.1.4.1)
17
+ activesupport (7.0.4)
13
18
  concurrent-ruby (~> 1.0, >= 1.0.2)
14
19
  i18n (>= 1.6, < 2)
15
20
  minitest (>= 5.1)
16
21
  tzinfo (~> 2.0)
17
- zeitwerk (~> 2.3)
18
- airrecord (1.0.7)
19
- faraday (>= 0.10, < 2.0)
22
+ airrecord (1.0.10)
23
+ faraday (>= 1.0, < 3.0)
24
+ faraday-net_http_persistent
20
25
  net-http-persistent
21
- concurrent-ruby (1.1.9)
22
- connection_pool (2.2.5)
26
+ app_rail-steps (0.2.17)
27
+ bcrypt (3.1.18)
28
+ byebug (11.1.3)
29
+ concurrent-ruby (1.1.10)
30
+ connection_pool (2.3.0)
23
31
  diff-lcs (1.4.4)
24
- faraday (1.7.2)
25
- faraday-em_http (~> 1.0)
26
- faraday-em_synchrony (~> 1.0)
27
- faraday-excon (~> 1.1)
28
- faraday-httpclient (~> 1.0.1)
29
- faraday-net_http (~> 1.0)
30
- faraday-net_http_persistent (~> 1.1)
31
- faraday-patron (~> 1.0)
32
- faraday-rack (~> 1.0)
33
- multipart-post (>= 1.2, < 3)
32
+ faraday (2.6.0)
33
+ faraday-net_http (>= 2.0, < 3.1)
34
34
  ruby2_keywords (>= 0.0.4)
35
- faraday-em_http (1.0.0)
36
- faraday-em_synchrony (1.0.0)
37
- faraday-excon (1.1.0)
38
- faraday-httpclient (1.0.1)
39
- faraday-net_http (1.0.1)
40
- faraday-net_http_persistent (1.2.0)
41
- faraday-patron (1.0.0)
42
- faraday-rack (1.0.0)
43
- i18n (1.8.10)
35
+ faraday-net_http (3.0.1)
36
+ faraday-net_http_persistent (2.1.0)
37
+ faraday (~> 2.5)
38
+ net-http-persistent (~> 4.0)
39
+ i18n (1.12.0)
44
40
  concurrent-ruby (~> 1.0)
45
- minitest (5.14.4)
46
- multipart-post (2.1.1)
47
- mustermann (1.1.1)
41
+ minitest (5.16.3)
42
+ mustermann (2.0.2)
48
43
  ruby2_keywords (~> 0.0.1)
49
44
  net-http-persistent (4.0.1)
50
45
  connection_pool (~> 2.2)
51
46
  rack (2.2.3)
52
- rack-protection (2.1.0)
47
+ rack-protection (2.2.2)
53
48
  rack
54
49
  rack-test (1.1.0)
55
50
  rack (>= 1.0, < 3)
@@ -68,24 +63,26 @@ GEM
68
63
  rspec-support (~> 3.10.0)
69
64
  rspec-support (3.10.2)
70
65
  ruby2_keywords (0.0.5)
71
- sinatra (2.1.0)
72
- mustermann (~> 1.0)
66
+ sinatra (2.2.2)
67
+ mustermann (~> 2.0)
73
68
  rack (~> 2.2)
74
- rack-protection (= 2.1.0)
69
+ rack-protection (= 2.2.2)
75
70
  tilt (~> 2.0)
76
- tilt (2.0.10)
77
- tzinfo (2.0.4)
71
+ thor (1.1.0)
72
+ tilt (2.0.11)
73
+ tzinfo (2.0.5)
78
74
  concurrent-ruby (~> 1.0)
79
- zeitwerk (2.4.2)
80
75
 
81
76
  PLATFORMS
82
77
  ruby
83
78
 
84
79
  DEPENDENCIES
85
80
  app_rail-airtable!
81
+ byebug
86
82
  rack-test
87
83
  rake (~> 12.0)
88
84
  rspec (~> 3.0)
85
+ thor
89
86
 
90
87
  BUNDLED WITH
91
88
  2.1.4
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
- # AppRail::Airtable
1
+ # App Rail Airtable
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/app_rail/airtable`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
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.
6
4
 
7
5
  ## Installation
8
6
 
@@ -12,29 +10,79 @@ Add this line to your application's Gemfile:
12
10
  gem 'app_rail-airtable'
13
11
  ```
14
12
 
15
- And then execute:
13
+ ## Usage
16
14
 
17
- $ bundle install
15
+ App Rail Airtable has two important concepts, models and servers.
18
16
 
19
- Or install it yourself as:
17
+ ### Models
18
+ App Rail Airtable makes the following assumptions
19
+ 1. Your App will connect to a single Airtable Base
20
+ 2. Your Airtable Base implements a normalised datamodel
21
+ 3. Your tables in Airtable are plural versions of the models you create in Ruby
20
22
 
21
- $ gem install app_rail-airtable
23
+ **API keys have account scope in Airtable so it is recommended to create a new account for each project so that API keys do not leak data, e.g. bob+<project name>@gmail.com. You can share the project with your main account so you don't need to login to separate accounts for each project.**
22
24
 
23
- ## Usage
25
+ You should create a model class for each table you want to use in your App. Model classes should extend `AppRail::Airtable::ApplicationRecord` and define [table name](https://github.com/sirupsen/airrecord#table) and [associations](https://github.com/sirupsen/airrecord#associations)
26
+
27
+ To provide support for routes, models can implement
28
+ * ar_list_item (index)
29
+ * ar_stack (show)
30
+ * self.create_as_json (create)
31
+ * self.update_as_json (update)
32
+
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.
34
+
35
+ ### Servers
36
+ You should create a single server which extends `AppRail::Airtable::Sinatra`.
37
+
38
+ App Rail Airtable provides two helpers to generate routes
39
+
40
+ **resources(name, only:)**
41
+ Creates routes that map to a table. It delegates from the route to a model method
42
+ * `index` to `ar_list_item`
43
+ * `show` to `ar_stack`
44
+ * `create` to `self.create_as_json`
45
+ * `update` to `self.update_as_json`
24
46
 
25
- TODO: Write usage instructions here
47
+ **authenticable_resources(name, , only:)**
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).
26
49
 
27
- ## Development
50
+ ### Generator
28
51
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
52
+ ```bash
53
+ bin/ara_generator <output_directory> <airtable_api_key> <airtable_base_id> [<json_schema>]
54
+ ```
30
55
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
56
+ #### Example
32
57
 
33
- ## Contributing
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
+ ```
34
61
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/app_rail-airtable.
62
+ You can find more examples of schemas in [/examples/schemas](/examples/schemas)
36
63
 
64
+ ## Debugging
37
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
+ ```
38
86
  ## License
39
87
 
40
88
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec)
5
7
 
6
- task :default => :spec
8
+ task default: :spec
@@ -1,29 +1,40 @@
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 = "app_rail-airtable"
6
+ spec.name = 'app_rail-airtable'
5
7
  spec.version = AppRail::Airtable::VERSION
6
- spec.authors = ["Matt Brooke-Smith"]
7
- spec.email = ["matt@futureworkshops.com"]
8
+ spec.authors = ['Matt Brooke-Smith']
9
+ spec.email = ['matt@futureworkshops.com']
8
10
 
9
- spec.summary = %q{Gem to help building App Rail servers using Airtable as a backend}
10
- spec.homepage = "https://github.com/FutureWorkshops/app_rail-airtable"
11
- spec.license = "MIT"
12
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
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["homepage_uri"] = spec.homepage
15
- spec.metadata["source_code_uri"] = "https://github.com/FutureWorkshops/app_rail-airtable"
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 = Dir.chdir(File.expand_path('..', __FILE__)) do
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
- spec.require_paths = ["lib"]
24
+
25
+ spec.bindir = 'bin'
26
+ spec.executables << 'ara_generator'
27
+
28
+ spec.require_paths = ['lib']
23
29
  spec.add_dependency 'activesupport'
24
- spec.add_dependency 'sinatra'
25
30
  spec.add_dependency 'airrecord'
26
-
27
- spec.add_development_dependency 'rspec'
31
+ spec.add_dependency 'app_rail-steps'
32
+ spec.add_dependency 'bcrypt'
33
+ spec.add_dependency 'faraday', '~> 2.2'
34
+ spec.add_dependency 'faraday-net_http_persistent', '~> 2.0' # workaround to make Faraday work after upgrading to 2.2.0
35
+ spec.add_dependency 'sinatra'
36
+ spec.add_dependency 'thor'
37
+
28
38
  spec.add_development_dependency 'rack-test'
29
- end
39
+ spec.add_development_dependency 'rspec'
40
+ end
data/bin/ara_generator ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'app_rail/airtable/generator'
6
+ require 'byebug'
7
+
8
+ AppRail::Airtable::Generator.start(ARGV)
data/bin/console CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require "bundler/setup"
4
- require "app_rail/airtable"
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 "irb"
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
+ }