airtable2 0.2.0

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
+ 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: []