airtable2 0.2.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8e76de7af1e8850fd762c839dcee996e5ce71e10c800548e991f80500da1c68d
4
+ data.tar.gz: 83ea6edb7e3a1473b1531f7dc3cee9273f6abdf2864e4c2bfa98051bd6667ea0
5
+ SHA512:
6
+ metadata.gz: 651355e1f16d59ed0c96ba2088c65824bc97c707cc1cf2d34967a13a399abf6ff1e00bd424c38bc2ce5efc24bb47e0594c24b2e3ae606940cb3311a4c4da5b77
7
+ data.tar.gz: b5e46f6aee0d721703feaa4f615f819ac433a64d5dd625297d8d12479032dd7bf940684ab3027ef65bcdabd9d1175399ecc4ad758dabaa68155a201cdbc05435
data/.gitignore ADDED
@@ -0,0 +1,24 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .vscode
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+ *.bundle
20
+ *.so
21
+ *.o
22
+ *.a
23
+ mkmf.log
24
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in airtable.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2015 Nathan Esquenazi
2
+ Copyright (c) 2016 Airtable
3
+
4
+ MIT License
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # Airtable API Wrapper for Ruby
2
+
3
+ For when Airrecord is just too much.
4
+
5
+ # Note on library status
6
+
7
+ This is a fork of an abandoned [previous wrapper](https://github.com/nesquena/airtable-ruby). There's still plenty to do to get it up to speed with the current Airtable API.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'airtable2', github: 'https://github.com/aseroff/airtable-ruby', branch: 'main'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ ## Usage
20
+
21
+ ### Creating a Client
22
+
23
+ First, be sure to register for an [airtable](https://airtable.com) account, setup a base, and create a token with the desired permissions for your base. Now, setup your Airtable client:
24
+
25
+ ```ruby
26
+ # Pass in api key to client
27
+ @client = Airtable::Client.new('your.token.goes.here')
28
+ ```
29
+
30
+ ### Accessing Data
31
+
32
+ Now we can access our base
33
+
34
+ ```ruby
35
+ @base = @client.base('appExAmPlE')
36
+ ```
37
+
38
+ and its tables
39
+
40
+ ```ruby
41
+ @tables = @base.tables
42
+ ```
43
+
44
+ and create a new table
45
+
46
+ ```ruby
47
+ @table = @base.create_table({ name: 'Names', description: 'A list of names', fields: [{ name: 'name', type: 'singleLineText' }] })
48
+ ```
49
+
50
+ ### Manipulating Tables
51
+
52
+ You can update at a table's metadata with the `update` method:
53
+
54
+ ```ruby
55
+ @table.update({ description: 'Updated description' })
56
+ ```
57
+
58
+ ### Querying Records
59
+
60
+ Once you have access to a table from above, we can query a set of records in the table with:
61
+
62
+ ```ruby
63
+ @records = @table.records
64
+ ```
65
+
66
+ ### Inserting Records
67
+
68
+ A single record or an array of records can be inserted using the `add_records` method on a table (max 10 at a time):
69
+
70
+ ```ruby
71
+ @table.add_records({ 'Name': 'name value', 'Age': 35 })
72
+ ```
73
+
74
+ ### Deleting Records
75
+
76
+ A single record or an array of records can be destroyed by passing their ids to the `delete_records` method on a table:
77
+
78
+ ```ruby
79
+ @record = @table.records[0]
80
+ @table.delete_records(@record.id)
81
+ ```
82
+
83
+ Or as a convenience, you can delete all records with the `dump` method
84
+
85
+ ```ruby
86
+ @table.dump
87
+ ```
88
+
89
+ ## Contributing
90
+
91
+ 1. Fork it ( https://github.com/aseroff/airtable-ruby/fork )
92
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
93
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
94
+ 4. Push to the branch (`git push origin my-new-feature`)
95
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs << 'test'
8
+ t.pattern = 'test/*_test.rb'
9
+ end
data/airtable.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'airtable/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'airtable2'
9
+ spec.version = Airtable::VERSION
10
+ spec.authors = ['Andrew Seroff', 'Nathan Esquenazi', 'Alexander Sorokin']
11
+ spec.email = ['andy@seroff.co']
12
+ spec.summary = 'Easily connect to airtable data using ruby'
13
+ spec.description = 'Easily connect to airtable data using ruby with access to all of the airtable features.'
14
+ spec.homepage = 'https://github.com/aseroff/airtable-ruby'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.required_ruby_version = '>= 3.1'
22
+ spec.add_dependency 'activesupport'
23
+ spec.add_dependency 'httparty'
24
+ spec.add_dependency 'json'
25
+
26
+ spec.add_development_dependency 'bundler'
27
+ spec.add_development_dependency 'minitest'
28
+ spec.add_development_dependency 'rake'
29
+ spec.add_development_dependency 'rubocop'
30
+ spec.add_development_dependency 'rubocop-md'
31
+ spec.add_development_dependency 'rubocop-minitest'
32
+ spec.add_development_dependency 'rubocop-performance'
33
+ spec.add_development_dependency 'webmock'
34
+ spec.metadata['rubygems_mfa_required'] = 'true'
35
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Object corresponding to an Airtable Base
4
+ class Airtable::Base < Airtable::Resource
5
+ def initialize(token, id)
6
+ @token = token
7
+ @id = id
8
+ self.class.headers({ 'Authorization': "Bearer #{@token}", 'Content-Type': 'application/json' })
9
+ end
10
+
11
+ # Expects {name:,description:,fields:[]}
12
+ def create_table(table_data)
13
+ response = self.class.post("#{base_url}/tables",
14
+ body: table_data.to_json).parsed_response
15
+
16
+ check_and_raise_error(response)
17
+
18
+ Airtable::Table.new @token, @id, response
19
+ end
20
+
21
+ def tables
22
+ response = self.class.get("#{base_url}/tables")
23
+
24
+ check_and_raise_error(response)
25
+
26
+ response['tables'].map { Airtable::Table.new(@token, @id, _1) }
27
+ end
28
+
29
+ protected
30
+
31
+ def base_url = "/v0/meta/bases/#{@id}"
32
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Client carrying authorization token
4
+ class Airtable::Client
5
+ def initialize(token)
6
+ @token = token
7
+ end
8
+
9
+ def base(base_id)
10
+ Airtable::Base.new(@token, base_id)
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Error class
4
+ class Airtable::Error < StandardError
5
+ attr_reader :message, :type
6
+
7
+ # {"error"=>{"type"=>"UNKNOWN_COLUMN_NAME", "message"=>"Could not find fields foo"}}
8
+
9
+ def initialize(error_hash)
10
+ @message = error_hash['message']
11
+ @type = error_hash['type']
12
+ super(@message)
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Object corresponding to an Airtable Record
4
+ class Airtable::Record < Airtable::Resource
5
+ attr_reader :fields
6
+
7
+ def initialize(token, base_id, table_id, api_response)
8
+ @token = token
9
+ @base_id = base_id
10
+ @table_id = table_id
11
+ api_response.deep_symbolize_keys.each do |key, value|
12
+ instance_variable_set(:"@#{key}", value)
13
+ end
14
+ self.class.headers({ 'Authorization': "Bearer #{@token}", 'Content-Type': 'application/json' })
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Base class for authorized resources sending network requests
4
+ class Airtable::Resource
5
+ include HTTParty
6
+ base_uri 'https://api.airtable.com'
7
+ # debug_output $stdout
8
+
9
+ attr_reader :id, :token
10
+
11
+ def initialize(token)
12
+ @token = token
13
+ self.class.headers({ 'Authorization': "Bearer #{@token}", 'Content-Type': 'application/json' })
14
+ end
15
+
16
+ def check_and_raise_error(response)
17
+ response['error'] ? raise(Error, response['error']) : false
18
+ end
19
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Object corresponding to an Airtable Table
4
+ class Airtable::Table < Airtable::Resource
5
+ attr_reader :name
6
+
7
+ def initialize(token, base_id, api_response)
8
+ @token = token
9
+ @base_id = base_id
10
+ api_response.deep_symbolize_keys.each do |key, value|
11
+ instance_variable_set(:"@#{key}", value)
12
+ end
13
+ self.class.headers({ 'Authorization': "Bearer #{@token}", 'Content-Type': 'application/json' })
14
+ end
15
+
16
+ def records
17
+ response = self.class.get(table_url)
18
+
19
+ check_and_raise_error(response)
20
+
21
+ response['records'].map { Airtable::Record.new(@token, @base_id, @table_id, _1) }
22
+ end
23
+
24
+ def update(table_data)
25
+ response = self.class.patch("/v0/meta/bases/#{@base_id}/tables/#{@id}",
26
+ body: table_data.to_json).parsed_response
27
+
28
+ check_and_raise_error(response)
29
+
30
+ Airtable::Table.new @token, @base_id, response
31
+ end
32
+
33
+ # limit 10 records
34
+ def add_records(records)
35
+ response = self.class.post(table_url,
36
+ body: { records: Array(records).map { |fields| { fields: } } }.to_json).parsed_response
37
+
38
+ check_and_raise_error(response)
39
+
40
+ response['records'].map { Airtable::Record.new(@token, @base_id, @id, _1) }
41
+ end
42
+
43
+ def delete_records(record_ids)
44
+ params = Array(record_ids).compact.map { "records[]=#{_1}" }.join('&')
45
+ response = self.class.delete("#{table_url}?#{params}").parsed_response
46
+
47
+ check_and_raise_error(response)
48
+
49
+ record_ids
50
+ end
51
+
52
+ def dump
53
+ records.map(&:id).each_slice(10) do |record_id_set|
54
+ delete_records(record_id_set)
55
+ sleep(0.2)
56
+ end
57
+ end
58
+
59
+ protected
60
+
61
+ def table_url = "/v0/#{@base_id}/#{@id}"
62
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Version
4
+ module Airtable
5
+ VERSION = '0.2.0'
6
+ end
data/lib/airtable.rb ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'httparty'
4
+ require 'delegate'
5
+ require 'active_support/core_ext/hash'
6
+ require 'json'
7
+
8
+ require 'airtable/version'
9
+ require 'airtable/resource'
10
+ require 'airtable/record'
11
+ require 'airtable/table'
12
+ require 'airtable/base'
13
+ require 'airtable/client'
14
+ require 'airtable/error'
@@ -0,0 +1,82 @@
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
@@ -0,0 +1,23 @@
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
@@ -0,0 +1,15 @@
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
metadata ADDED
@@ -0,0 +1,218 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: airtable2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Seroff
8
+ - Nathan Esquenazi
9
+ - Alexander Sorokin
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2024-10-26 00:00:00.000000000 Z
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
+ - !ruby/object:Gem::Dependency
30
+ name: httparty
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ - !ruby/object:Gem::Dependency
44
+ name: json
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ - !ruby/object:Gem::Dependency
58
+ name: bundler
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ - !ruby/object:Gem::Dependency
72
+ name: minitest
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ - !ruby/object:Gem::Dependency
86
+ name: rake
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ - !ruby/object:Gem::Dependency
100
+ name: rubocop
101
+ requirement: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ - !ruby/object:Gem::Dependency
114
+ name: rubocop-md
115
+ requirement: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ type: :development
121
+ prerelease: false
122
+ version_requirements: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ - !ruby/object:Gem::Dependency
128
+ name: rubocop-minitest
129
+ requirement: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ - !ruby/object:Gem::Dependency
142
+ name: rubocop-performance
143
+ requirement: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ type: :development
149
+ prerelease: false
150
+ version_requirements: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ - !ruby/object:Gem::Dependency
156
+ name: webmock
157
+ requirement: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ type: :development
163
+ prerelease: false
164
+ version_requirements: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - ">="
167
+ - !ruby/object:Gem::Version
168
+ version: '0'
169
+ description: Easily connect to airtable data using ruby with access to all of the
170
+ airtable features.
171
+ email:
172
+ - andy@seroff.co
173
+ executables: []
174
+ extensions: []
175
+ extra_rdoc_files: []
176
+ files:
177
+ - ".gitignore"
178
+ - Gemfile
179
+ - LICENSE.txt
180
+ - README.md
181
+ - Rakefile
182
+ - airtable.gemspec
183
+ - lib/airtable.rb
184
+ - lib/airtable/base.rb
185
+ - lib/airtable/client.rb
186
+ - lib/airtable/error.rb
187
+ - lib/airtable/record.rb
188
+ - lib/airtable/resource.rb
189
+ - lib/airtable/table.rb
190
+ - lib/airtable/version.rb
191
+ - test/airtable_test.rb
192
+ - test/record_test.rb
193
+ - test/test_helper.rb
194
+ homepage: https://github.com/aseroff/airtable-ruby
195
+ licenses:
196
+ - MIT
197
+ metadata:
198
+ rubygems_mfa_required: 'true'
199
+ post_install_message:
200
+ rdoc_options: []
201
+ require_paths:
202
+ - lib
203
+ required_ruby_version: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - ">="
206
+ - !ruby/object:Gem::Version
207
+ version: '3.1'
208
+ required_rubygems_version: !ruby/object:Gem::Requirement
209
+ requirements:
210
+ - - ">="
211
+ - !ruby/object:Gem::Version
212
+ version: '0'
213
+ requirements: []
214
+ rubygems_version: 3.5.21
215
+ signing_key:
216
+ specification_version: 4
217
+ summary: Easily connect to airtable data using ruby
218
+ test_files: []