airtable2 0.2.0 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8e76de7af1e8850fd762c839dcee996e5ce71e10c800548e991f80500da1c68d
4
- data.tar.gz: 83ea6edb7e3a1473b1531f7dc3cee9273f6abdf2864e4c2bfa98051bd6667ea0
3
+ metadata.gz: 8f8968193253823a000c9f38c14185208b89a4dfd77d9ff6ab0a5b5f8b3f595e
4
+ data.tar.gz: 06c31cfbcc19f156ab7e498e7e6b6f9f0ca4b749aecb523124f85df7d8a452c2
5
5
  SHA512:
6
- metadata.gz: 651355e1f16d59ed0c96ba2088c65824bc97c707cc1cf2d34967a13a399abf6ff1e00bd424c38bc2ce5efc24bb47e0594c24b2e3ae606940cb3311a4c4da5b77
7
- data.tar.gz: b5e46f6aee0d721703feaa4f615f819ac433a64d5dd625297d8d12479032dd7bf940684ab3027ef65bcdabd9d1175399ecc4ad758dabaa68155a201cdbc05435
6
+ metadata.gz: 6bbbc8e341a9a16969df15e605d0e379138915066968abd3581b635d6bf66349526ae7f88d483384a5248f6f72c97f1f85d32e2a4a7e3e49c223f339d07cedc0
7
+ data.tar.gz: f8f0c2a7f3400519a07082a7c1640ae80947c7d4c4143d6b0d82e50068c01176d0f3aaeeeee392d9926647574f65185fc69382a147fa68b0bad4c2603dc2f59d
data/.yardopts ADDED
@@ -0,0 +1,5 @@
1
+ -o docs
2
+ -r README.md
3
+ -q
4
+ --protected
5
+ --private
data/README.md CHANGED
@@ -10,7 +10,7 @@ This is a fork of an abandoned [previous wrapper](https://github.com/nesquena/ai
10
10
 
11
11
  Add this line to your application's Gemfile:
12
12
 
13
- gem 'airtable2', github: 'https://github.com/aseroff/airtable-ruby', branch: 'main'
13
+ gem 'airtable2'
14
14
 
15
15
  And then execute:
16
16
 
@@ -41,14 +41,20 @@ and its tables
41
41
  @tables = @base.tables
42
42
  ```
43
43
 
44
- and create a new table
44
+ and a table's records, so you can navigate the has_many chain the way God intended:
45
45
 
46
46
  ```ruby
47
- @table = @base.create_table({ name: 'Names', description: 'A list of names', fields: [{ name: 'name', type: 'singleLineText' }] })
47
+ @client.bases.first.tables.first.records.first
48
48
  ```
49
49
 
50
50
  ### Manipulating Tables
51
51
 
52
+ Create a new table with:
53
+
54
+ ```ruby
55
+ @table = @base.create_table({ name: 'Names', description: 'A list of names', fields: [{ name: 'name', type: 'singleLineText' }] })
56
+ ```
57
+
52
58
  You can update at a table's metadata with the `update` method:
53
59
 
54
60
  ```ruby
@@ -86,6 +92,10 @@ Or as a convenience, you can delete all records with the `dump` method
86
92
  @table.dump
87
93
  ```
88
94
 
95
+ ## Complete documentation
96
+
97
+ YARD-generated documentation is hosted on [GitHub Pages](https://aseroff.github.io/airtable-ruby/).
98
+
89
99
  ## Contributing
90
100
 
91
101
  1. Fork it ( https://github.com/aseroff/airtable-ruby/fork )
data/airtable.gemspec CHANGED
@@ -9,19 +9,17 @@ Gem::Specification.new do |spec|
9
9
  spec.version = Airtable::VERSION
10
10
  spec.authors = ['Andrew Seroff', 'Nathan Esquenazi', 'Alexander Sorokin']
11
11
  spec.email = ['andy@seroff.co']
12
- spec.summary = 'Easily connect to airtable data using ruby'
12
+ spec.summary = 'For when Airrecord is just too much.'
13
13
  spec.description = 'Easily connect to airtable data using ruby with access to all of the airtable features.'
14
14
  spec.homepage = 'https://github.com/aseroff/airtable-ruby'
15
15
  spec.license = 'MIT'
16
16
 
17
- spec.files = `git ls-files -z`.split("\x0")
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(docs|test)/}) }
18
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ['lib']
20
20
 
21
21
  spec.required_ruby_version = '>= 3.1'
22
- spec.add_dependency 'activesupport'
23
- spec.add_dependency 'httparty'
24
- spec.add_dependency 'json'
22
+ spec.add_dependency 'httparty', '>= 0.14.0'
25
23
 
26
24
  spec.add_development_dependency 'bundler'
27
25
  spec.add_development_dependency 'minitest'
@@ -30,6 +28,7 @@ Gem::Specification.new do |spec|
30
28
  spec.add_development_dependency 'rubocop-md'
31
29
  spec.add_development_dependency 'rubocop-minitest'
32
30
  spec.add_development_dependency 'rubocop-performance'
31
+ spec.add_development_dependency 'rubocop-rake'
33
32
  spec.add_development_dependency 'webmock'
34
33
  spec.metadata['rubygems_mfa_required'] = 'true'
35
34
  end
data/lib/airtable/base.rb CHANGED
@@ -3,12 +3,13 @@
3
3
  # Object corresponding to an Airtable Base
4
4
  class Airtable::Base < Airtable::Resource
5
5
  def initialize(token, id)
6
- @token = token
6
+ super(token)
7
7
  @id = id
8
- self.class.headers({ 'Authorization': "Bearer #{@token}", 'Content-Type': 'application/json' })
9
8
  end
10
9
 
11
10
  # Expects {name:,description:,fields:[]}
11
+ # @see https://airtable.com/developers/web/api/create-table
12
+ # @return [Airtable::Table]
12
13
  def create_table(table_data)
13
14
  response = self.class.post("#{base_url}/tables",
14
15
  body: table_data.to_json).parsed_response
@@ -18,6 +19,8 @@ class Airtable::Base < Airtable::Resource
18
19
  Airtable::Table.new @token, @id, response
19
20
  end
20
21
 
22
+ # @see https://airtable.com/developers/web/api/get-base-schema
23
+ # @return [Array]<Airtable::Table>
21
24
  def tables
22
25
  response = self.class.get("#{base_url}/tables")
23
26
 
@@ -26,7 +29,14 @@ class Airtable::Base < Airtable::Resource
26
29
  response['tables'].map { Airtable::Table.new(@token, @id, _1) }
27
30
  end
28
31
 
32
+ # Instantiate table in base
33
+ # @return [Airtable::Table]
34
+ def table(table_id)
35
+ Airtable::Table.new(@token, @id, table_id)
36
+ end
37
+
29
38
  protected
30
39
 
40
+ # Endpoint for bases
31
41
  def base_url = "/v0/meta/bases/#{@id}"
32
42
  end
@@ -1,12 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Client carrying authorization token
4
- class Airtable::Client
5
- def initialize(token)
6
- @token = token
4
+ class Airtable::Client < Airtable::Resource
5
+ # @see https://airtable.com/developers/web/api/list-bases
6
+ # @return [Array]<Airtable::Base>
7
+ def bases
8
+ response = self.class.get('/v0/meta/bases').parsed_response
9
+
10
+ check_and_raise_error(response)
11
+
12
+ response['bases'].map { Airtable::Base.new(@token, _1['id']) }
7
13
  end
8
14
 
15
+ # @see https://airtable.com/developers/web/api/create-base
16
+ # def create_base(base_data)
17
+ # response = self.class.post('/v0/meta/bases'
18
+ # body: base_data.to_json).parsed_response
19
+ # check_and_raise_error(response)
20
+ # Airtable::Base.new @token, response
21
+ # end
22
+
23
+ # Instantiate base
24
+ # @return [Airtable::Base]
9
25
  def base(base_id)
10
26
  Airtable::Base.new(@token, base_id)
11
27
  end
28
+
29
+ # @see https://airtable.com/developers/web/api/get-user-id-scopes
30
+ # @return [Hash]
31
+ def whoami
32
+ response = self.class.get('/v0/meta/whoami').parsed_response
33
+
34
+ check_and_raise_error(response)
35
+
36
+ response
37
+ end
12
38
  end
@@ -5,12 +5,11 @@ class Airtable::Record < Airtable::Resource
5
5
  attr_reader :fields
6
6
 
7
7
  def initialize(token, base_id, table_id, api_response)
8
- @token = token
8
+ super(token)
9
9
  @base_id = base_id
10
10
  @table_id = table_id
11
11
  api_response.deep_symbolize_keys.each do |key, value|
12
12
  instance_variable_set(:"@#{key}", value)
13
13
  end
14
- self.class.headers({ 'Authorization': "Bearer #{@token}", 'Content-Type': 'application/json' })
15
14
  end
16
15
  end
@@ -13,6 +13,7 @@ class Airtable::Resource
13
13
  self.class.headers({ 'Authorization': "Bearer #{@token}", 'Content-Type': 'application/json' })
14
14
  end
15
15
 
16
+ # If API response is an error, raises an Airtable::Error with the error message
16
17
  def check_and_raise_error(response)
17
18
  response['error'] ? raise(Error, response['error']) : false
18
19
  end
@@ -5,14 +5,15 @@ class Airtable::Table < Airtable::Resource
5
5
  attr_reader :name
6
6
 
7
7
  def initialize(token, base_id, api_response)
8
- @token = token
8
+ super(token)
9
9
  @base_id = base_id
10
10
  api_response.deep_symbolize_keys.each do |key, value|
11
11
  instance_variable_set(:"@#{key}", value)
12
12
  end
13
- self.class.headers({ 'Authorization': "Bearer #{@token}", 'Content-Type': 'application/json' })
14
13
  end
15
14
 
15
+ # @see https://airtable.com/developers/web/api/list-records
16
+ # @return [Array<Airtable::Record>]
16
17
  def records
17
18
  response = self.class.get(table_url)
18
19
 
@@ -21,6 +22,14 @@ class Airtable::Table < Airtable::Resource
21
22
  response['records'].map { Airtable::Record.new(@token, @base_id, @table_id, _1) }
22
23
  end
23
24
 
25
+ # Instantiate record in table
26
+ # @return [Airtable::Table]
27
+ def record(record_id)
28
+ Airtable::Table.new(@token, @base_id, @id, record_id)
29
+ end
30
+
31
+ # @see https://airtable.com/developers/web/api/update-table
32
+ # @return [Airtable::Table]
24
33
  def update(table_data)
25
34
  response = self.class.patch("/v0/meta/bases/#{@base_id}/tables/#{@id}",
26
35
  body: table_data.to_json).parsed_response
@@ -30,7 +39,9 @@ class Airtable::Table < Airtable::Resource
30
39
  Airtable::Table.new @token, @base_id, response
31
40
  end
32
41
 
33
- # limit 10 records
42
+ # @note API maximum of 10 records at a time
43
+ # @see https://airtable.com/developers/web/api/create-records
44
+ # @return [Array<Airtable::Record>]
34
45
  def add_records(records)
35
46
  response = self.class.post(table_url,
36
47
  body: { records: Array(records).map { |fields| { fields: } } }.to_json).parsed_response
@@ -40,6 +51,9 @@ class Airtable::Table < Airtable::Resource
40
51
  response['records'].map { Airtable::Record.new(@token, @base_id, @id, _1) }
41
52
  end
42
53
 
54
+ # @note API maximum of 10 records at a time
55
+ # @see https://airtable.com/developers/web/api/delete-multiple-records
56
+ # @return [Array] Deleted record ids
43
57
  def delete_records(record_ids)
44
58
  params = Array(record_ids).compact.map { "records[]=#{_1}" }.join('&')
45
59
  response = self.class.delete("#{table_url}?#{params}").parsed_response
@@ -49,6 +63,7 @@ class Airtable::Table < Airtable::Resource
49
63
  record_ids
50
64
  end
51
65
 
66
+ # Deletes all table's records
52
67
  def dump
53
68
  records.map(&:id).each_slice(10) do |record_id_set|
54
69
  delete_records(record_id_set)
@@ -58,5 +73,6 @@ class Airtable::Table < Airtable::Resource
58
73
 
59
74
  protected
60
75
 
76
+ # Endpoint for tables
61
77
  def table_url = "/v0/#{@base_id}/#{@id}"
62
78
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Version
4
4
  module Airtable
5
- VERSION = '0.2.0'
5
+ VERSION = '0.2.2'
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: airtable2
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Seroff
@@ -10,44 +10,30 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2024-10-26 00:00:00.000000000 Z
13
+ date: 2024-10-28 00:00:00.000000000 Z
14
14
  dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: activesupport
17
- requirement: !ruby/object:Gem::Requirement
18
- requirements:
19
- - - ">="
20
- - !ruby/object:Gem::Version
21
- version: '0'
22
- type: :runtime
23
- prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- requirements:
26
- - - ">="
27
- - !ruby/object:Gem::Version
28
- version: '0'
29
15
  - !ruby/object:Gem::Dependency
30
16
  name: httparty
31
17
  requirement: !ruby/object:Gem::Requirement
32
18
  requirements:
33
19
  - - ">="
34
20
  - !ruby/object:Gem::Version
35
- version: '0'
21
+ version: 0.14.0
36
22
  type: :runtime
37
23
  prerelease: false
38
24
  version_requirements: !ruby/object:Gem::Requirement
39
25
  requirements:
40
26
  - - ">="
41
27
  - !ruby/object:Gem::Version
42
- version: '0'
28
+ version: 0.14.0
43
29
  - !ruby/object:Gem::Dependency
44
- name: json
30
+ name: bundler
45
31
  requirement: !ruby/object:Gem::Requirement
46
32
  requirements:
47
33
  - - ">="
48
34
  - !ruby/object:Gem::Version
49
35
  version: '0'
50
- type: :runtime
36
+ type: :development
51
37
  prerelease: false
52
38
  version_requirements: !ruby/object:Gem::Requirement
53
39
  requirements:
@@ -55,7 +41,7 @@ dependencies:
55
41
  - !ruby/object:Gem::Version
56
42
  version: '0'
57
43
  - !ruby/object:Gem::Dependency
58
- name: bundler
44
+ name: minitest
59
45
  requirement: !ruby/object:Gem::Requirement
60
46
  requirements:
61
47
  - - ">="
@@ -69,7 +55,7 @@ dependencies:
69
55
  - !ruby/object:Gem::Version
70
56
  version: '0'
71
57
  - !ruby/object:Gem::Dependency
72
- name: minitest
58
+ name: rake
73
59
  requirement: !ruby/object:Gem::Requirement
74
60
  requirements:
75
61
  - - ">="
@@ -83,7 +69,7 @@ dependencies:
83
69
  - !ruby/object:Gem::Version
84
70
  version: '0'
85
71
  - !ruby/object:Gem::Dependency
86
- name: rake
72
+ name: rubocop
87
73
  requirement: !ruby/object:Gem::Requirement
88
74
  requirements:
89
75
  - - ">="
@@ -97,7 +83,7 @@ dependencies:
97
83
  - !ruby/object:Gem::Version
98
84
  version: '0'
99
85
  - !ruby/object:Gem::Dependency
100
- name: rubocop
86
+ name: rubocop-md
101
87
  requirement: !ruby/object:Gem::Requirement
102
88
  requirements:
103
89
  - - ">="
@@ -111,7 +97,7 @@ dependencies:
111
97
  - !ruby/object:Gem::Version
112
98
  version: '0'
113
99
  - !ruby/object:Gem::Dependency
114
- name: rubocop-md
100
+ name: rubocop-minitest
115
101
  requirement: !ruby/object:Gem::Requirement
116
102
  requirements:
117
103
  - - ">="
@@ -125,7 +111,7 @@ dependencies:
125
111
  - !ruby/object:Gem::Version
126
112
  version: '0'
127
113
  - !ruby/object:Gem::Dependency
128
- name: rubocop-minitest
114
+ name: rubocop-performance
129
115
  requirement: !ruby/object:Gem::Requirement
130
116
  requirements:
131
117
  - - ">="
@@ -139,7 +125,7 @@ dependencies:
139
125
  - !ruby/object:Gem::Version
140
126
  version: '0'
141
127
  - !ruby/object:Gem::Dependency
142
- name: rubocop-performance
128
+ name: rubocop-rake
143
129
  requirement: !ruby/object:Gem::Requirement
144
130
  requirements:
145
131
  - - ">="
@@ -175,6 +161,7 @@ extensions: []
175
161
  extra_rdoc_files: []
176
162
  files:
177
163
  - ".gitignore"
164
+ - ".yardopts"
178
165
  - Gemfile
179
166
  - LICENSE.txt
180
167
  - README.md
@@ -188,9 +175,6 @@ files:
188
175
  - lib/airtable/resource.rb
189
176
  - lib/airtable/table.rb
190
177
  - lib/airtable/version.rb
191
- - test/airtable_test.rb
192
- - test/record_test.rb
193
- - test/test_helper.rb
194
178
  homepage: https://github.com/aseroff/airtable-ruby
195
179
  licenses:
196
180
  - MIT
@@ -214,5 +198,5 @@ requirements: []
214
198
  rubygems_version: 3.5.21
215
199
  signing_key:
216
200
  specification_version: 4
217
- summary: Easily connect to airtable data using ruby
201
+ summary: For when Airrecord is just too much.
218
202
  test_files: []
@@ -1,82 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
-
5
- describe Airtable do
6
- before do
7
- @client_key = '12345'
8
- @app_key = 'appXXV84Qu'
9
- @sheet_name = 'Test'
10
- end
11
-
12
- describe 'with Airtable' do
13
- it 'should allow client to be created' do
14
- @client = Airtable::Client.new(@client_key)
15
-
16
- assert_kind_of Airtable::Client, @client
17
- @table = @client.table(@app_key, @sheet_name)
18
-
19
- assert_kind_of Airtable::Table, @table
20
- end
21
-
22
- it 'should fetch record set' do
23
- stub_airtable_response!("https://api.airtable.com/v0/#{@app_key}/#{@sheet_name}",
24
- { 'records' => [], 'offset' => 'abcde' })
25
- @table = Airtable::Client.new(@client_key).table(@app_key, @sheet_name)
26
- @records = @table.records
27
-
28
- assert_equal 'abcde', @records.offset
29
- end
30
-
31
- it 'should select records based on a formula' do
32
- query_str = "OR(RECORD_ID() = 'recXYZ1', RECORD_ID() = 'recXYZ2', RECORD_ID() = 'recXYZ3', RECORD_ID() = 'recXYZ4')"
33
- escaped_query = HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER.call(filterByFormula: query_str)
34
- request_url = "https://api.airtable.com/v0/#{@app_key}/#{@sheet_name}?#{escaped_query}"
35
- stub_airtable_response!(request_url, { 'records' => [] })
36
- @table = Airtable::Client.new(@client_key).table(@app_key, @sheet_name)
37
- @select_records = @table.select(formula: query_str)
38
-
39
- assert_empty @select_records.records
40
- end
41
-
42
- it 'should raise an ArgumentError if a formula is not a string' do
43
- stub_airtable_response!("https://api.airtable.com/v0/#{@app_key}/#{@sheet_name}",
44
- { 'records' => [], 'offset' => 'abcde' })
45
- @table = Airtable::Client.new(@client_key).table(@app_key, @sheet_name)
46
- _ { proc { @table.select(formula: { foo: 'bar' }) } }.must_raise ArgumentError
47
- end
48
-
49
- it 'should allow creating records' do
50
- stub_airtable_response!("https://api.airtable.com/v0/#{@app_key}/#{@sheet_name}",
51
- { 'fields' => { 'name' => 'Sarah Jaine', 'email' => 'sarah@jaine.com', 'foo' => 'bar' }, 'id' => '12345' }, :post)
52
- table = Airtable::Client.new(@client_key).table(@app_key, @sheet_name)
53
- record = Airtable::Record.new(name: 'Sarah Jaine', email: 'sarah@jaine.com')
54
- table.create(record)
55
-
56
- assert_equal '12345', record['id']
57
- assert_equal 'bar', record['foo']
58
- end
59
-
60
- it 'should allow updating records' do
61
- record_id = '12345'
62
- stub_airtable_response!("https://api.airtable.com/v0/#{@app_key}/#{@sheet_name}/#{record_id}",
63
- { 'fields' => { 'name' => 'Sarah Jaine', 'email' => 'sarah@jaine.com', 'foo' => 'bar' }, 'id' => record_id }, :put)
64
- table = Airtable::Client.new(@client_key).table(@app_key, @sheet_name)
65
- record = Airtable::Record.new(name: 'Sarah Jaine', email: 'sarah@jaine.com', id: record_id)
66
- table.update(record)
67
-
68
- assert_equal '12345', record['id']
69
- assert_equal 'bar', record['foo']
70
- end
71
-
72
- it 'should raise an error when the API returns an error' do
73
- stub_airtable_response!("https://api.airtable.com/v0/#{@app_key}/#{@sheet_name}",
74
- { 'error' => { 'type' => 'UNKNOWN_COLUMN_NAME', 'message' => 'Could not find fields foo' } }, :post, 422)
75
- table = Airtable::Client.new(@client_key).table(@app_key, @sheet_name)
76
- record = Airtable::Record.new(foo: 'bar')
77
- assert_raises Airtable::Error do
78
- table.create(record)
79
- end
80
- end
81
- end
82
- end
data/test/record_test.rb DELETED
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'test_helper'
4
-
5
- describe Airtable do
6
- describe Airtable::Record do
7
- it 'should not return id in fields_for_update' do
8
- record = Airtable::Record.new(name: 'Sarah Jaine', email: 'sarah@jaine.com', id: 12_345)
9
- _(record.fields_for_update).wont_include(:id)
10
- end
11
-
12
- it 'returns new columns in fields_for_update' do
13
- record = Airtable::Record.new(name: 'Sarah Jaine', email: 'sarah@jaine.com', id: 12_345)
14
- record[:website] = 'http://sarahjaine.com'
15
- _(record.fields_for_update).must_include(:website)
16
- end
17
-
18
- it 'returns fields_for_update in original capitalization' do
19
- record = Airtable::Record.new('Name' => 'Sarah Jaine')
20
- _(record.fields_for_update).must_include('Name')
21
- end
22
- end
23
- end
data/test/test_helper.rb DELETED
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'airtable'
4
- require 'webmock/minitest'
5
- require 'minitest/pride'
6
- require 'minitest/autorun'
7
-
8
- def stub_airtable_response!(url, response, method = :get, status = 200)
9
- stub_request(method, url)
10
- .to_return(
11
- body: response.to_json,
12
- status: status,
13
- headers: { 'Content-Type' => 'application/json' }
14
- )
15
- end