provider_kit 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 +7 -0
- data/LICENSE +21 -0
- data/README.md +129 -0
- data/Rakefile +11 -0
- data/lib/generators/provider/USAGE +10 -0
- data/lib/generators/provider/provider_generator.rb +55 -0
- data/lib/generators/provider/templates/capability.rb.tt +11 -0
- data/lib/generators/provider/templates/context.rb.tt +12 -0
- data/lib/generators/provider/templates/provider.rb.tt +13 -0
- data/lib/generators/provider/templates/spec.rb.tt +34 -0
- data/lib/provider_kit/buildable.rb +16 -0
- data/lib/provider_kit/callbacks.rb +33 -0
- data/lib/provider_kit/capability.rb +121 -0
- data/lib/provider_kit/capability_extension.rb +22 -0
- data/lib/provider_kit/capable.rb +101 -0
- data/lib/provider_kit/context.rb +58 -0
- data/lib/provider_kit/encrypted_settings.rb +47 -0
- data/lib/provider_kit/encryptor.rb +32 -0
- data/lib/provider_kit/engine.rb +20 -0
- data/lib/provider_kit/exceptions.rb +12 -0
- data/lib/provider_kit/execution.rb +40 -0
- data/lib/provider_kit/json_client.rb +81 -0
- data/lib/provider_kit/json_request.rb +160 -0
- data/lib/provider_kit/logging.rb +73 -0
- data/lib/provider_kit/provideable.rb +40 -0
- data/lib/provider_kit/provider.rb +62 -0
- data/lib/provider_kit/provider_attribute.rb +99 -0
- data/lib/provider_kit/registerable.rb +52 -0
- data/lib/provider_kit/registration.rb +88 -0
- data/lib/provider_kit/settings.rb +169 -0
- data/lib/provider_kit/version.rb +15 -0
- data/lib/provider_kit.rb +61 -0
- metadata +205 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ProviderKit
|
4
|
+
# Designed to add registration methods to the core namespace
|
5
|
+
module Registerable
|
6
|
+
|
7
|
+
def deregister(key)
|
8
|
+
if registration = registrations[key]
|
9
|
+
config.registered_providers.delete(key)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def for(type)
|
14
|
+
if env_key = ENV["PROVIDER_FOR_#{ type.upcase }"].presence
|
15
|
+
registrations[env_key.to_clean_sym].klass
|
16
|
+
elsif default_key = config.type_defaults[type].presence
|
17
|
+
registrations[default_key].klass
|
18
|
+
else
|
19
|
+
providers(type:).last
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def providers(type: nil)
|
24
|
+
registrations(type:).values.map(&:klass)
|
25
|
+
end
|
26
|
+
|
27
|
+
# ProviderKit.register :stripe
|
28
|
+
def register(key, **options)
|
29
|
+
registration = Registration.new(key, **options)
|
30
|
+
return false unless registration.valid?
|
31
|
+
|
32
|
+
deregister(key)
|
33
|
+
|
34
|
+
config.registered_providers[key] = registration
|
35
|
+
end
|
36
|
+
|
37
|
+
def registrations(type: nil)
|
38
|
+
if type.present?
|
39
|
+
config.registered_providers.select { |_, reg| reg.types.include?(type) }
|
40
|
+
else
|
41
|
+
config.registered_providers
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def use(key, **options)
|
46
|
+
type = options[:for] || options[:type]
|
47
|
+
|
48
|
+
config.type_defaults[type] = key
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ProviderKit
|
4
|
+
class Registration
|
5
|
+
|
6
|
+
attr_reader :key
|
7
|
+
attr_reader :options
|
8
|
+
|
9
|
+
def initialize(key, **options)
|
10
|
+
@key = key
|
11
|
+
|
12
|
+
if options[:type]
|
13
|
+
@types = Array(options.delete(:type)).flatten.compact.presence
|
14
|
+
end
|
15
|
+
|
16
|
+
if options[:types]
|
17
|
+
@types = Array(options.delete(:types)).flatten.compact.presence
|
18
|
+
end
|
19
|
+
|
20
|
+
@options = options
|
21
|
+
end
|
22
|
+
|
23
|
+
def class_name
|
24
|
+
options[:class_name].presence || "#{ key.to_s.classify }::Provider"
|
25
|
+
end
|
26
|
+
|
27
|
+
def config
|
28
|
+
@config ||= Settings.load(options)
|
29
|
+
end
|
30
|
+
|
31
|
+
def credentials
|
32
|
+
@credentials ||= Settings.load(Rails.application.credentials.dig(:providers, key)&.to_h)
|
33
|
+
end
|
34
|
+
|
35
|
+
def display_key
|
36
|
+
config.short_key.presence || key
|
37
|
+
end
|
38
|
+
|
39
|
+
def disallow_in_test_env?
|
40
|
+
options[:test] == false
|
41
|
+
end
|
42
|
+
|
43
|
+
def klass
|
44
|
+
if Rails.application.config.cache_classes
|
45
|
+
@klass ||= build_klass_from_name
|
46
|
+
else
|
47
|
+
build_klass_from_name
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# generic? => (types.include?(:generic))
|
52
|
+
def method_missing(method_name)
|
53
|
+
if match = method_name.to_s.match(/^(?<attribute>[a-z0-9_]+)\?$/)
|
54
|
+
types.include?(match[:attribute].to_sym)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def name
|
59
|
+
options[:name].presence || key.to_s.titleize
|
60
|
+
end
|
61
|
+
|
62
|
+
def provider
|
63
|
+
ProviderKit::Provider.new(key).provider_instance
|
64
|
+
end
|
65
|
+
|
66
|
+
def types
|
67
|
+
@types ||= [ :generic ]
|
68
|
+
end
|
69
|
+
|
70
|
+
def valid?
|
71
|
+
if disallow_in_test_env? && Rails.env.test?
|
72
|
+
return false
|
73
|
+
end
|
74
|
+
|
75
|
+
true
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def build_klass_from_name
|
81
|
+
if klass = class_name.to_s.safe_constantize
|
82
|
+
klass.key = key if klass.respond_to?(:key=)
|
83
|
+
klass
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ProviderKit
|
4
|
+
# Serializable object to read/write from account settings fields
|
5
|
+
class Settings
|
6
|
+
|
7
|
+
attr_reader :data
|
8
|
+
|
9
|
+
def initialize(raw_data)
|
10
|
+
@data = build_data(raw_data)&.symbolize_keys || {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](key)
|
14
|
+
data[key]
|
15
|
+
end
|
16
|
+
|
17
|
+
def []=(key, value)
|
18
|
+
data[key] = value
|
19
|
+
end
|
20
|
+
|
21
|
+
def include?(key)
|
22
|
+
data.include?(key)
|
23
|
+
end
|
24
|
+
|
25
|
+
def inspect
|
26
|
+
@data.inspect
|
27
|
+
end
|
28
|
+
|
29
|
+
def method_missing(method_name, *args)
|
30
|
+
super if data.nil?
|
31
|
+
|
32
|
+
# some_key => :value
|
33
|
+
if match = method_name.to_s.match(/^(?<attribute>[a-z0-9_]+)$/)
|
34
|
+
key = match[:attribute].to_sym
|
35
|
+
|
36
|
+
if Hash === data[key]
|
37
|
+
self.class.new(data[key])
|
38
|
+
else
|
39
|
+
data[key]
|
40
|
+
end
|
41
|
+
|
42
|
+
# some_key = "value"
|
43
|
+
elsif match = method_name.to_s.match(/^(?<attribute>[a-z0-9_]+)=$/)
|
44
|
+
key = match[:attribute].to_sym
|
45
|
+
|
46
|
+
data[key] = args.first
|
47
|
+
|
48
|
+
# some_key? => true/false
|
49
|
+
elsif match = method_name.to_s.match(/^(?<attribute>[a-z0-9_]+)\?$/)
|
50
|
+
key = match[:attribute].to_sym
|
51
|
+
|
52
|
+
data.has_key?(key) && (data[key] == true || data[key] == "1")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# always yes so this gets picked up by `method_missing`
|
57
|
+
def respond_to?(_)
|
58
|
+
true
|
59
|
+
end
|
60
|
+
|
61
|
+
def update(value = {})
|
62
|
+
existing = (data.presence || {}).symbolize_keys
|
63
|
+
value = value.try(:to_h) if value.respond_to?(:to_h)
|
64
|
+
value = nil unless value.is_a?(Hash)
|
65
|
+
value = {} unless value.present?
|
66
|
+
value = value.symbolize_keys
|
67
|
+
|
68
|
+
updated = existing.merge(value)
|
69
|
+
|
70
|
+
@data = build_data(updated)
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_h
|
74
|
+
data.presence || {}
|
75
|
+
end
|
76
|
+
|
77
|
+
def to_json
|
78
|
+
if data.present?
|
79
|
+
data.to_json
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.dump(data)
|
84
|
+
case data
|
85
|
+
when self
|
86
|
+
data.to_json
|
87
|
+
else
|
88
|
+
new(data).to_json
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.load(data = nil)
|
93
|
+
new(data.presence || {})
|
94
|
+
end
|
95
|
+
|
96
|
+
class Jsonb < self
|
97
|
+
|
98
|
+
def to_json
|
99
|
+
if data.present?
|
100
|
+
data
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def build_data(data)
|
109
|
+
hash = if Settings === data
|
110
|
+
data.data
|
111
|
+
elsif Hash === data
|
112
|
+
data
|
113
|
+
else
|
114
|
+
JSON.parse(data)
|
115
|
+
end
|
116
|
+
|
117
|
+
clean_values(hash)
|
118
|
+
rescue StandardError => e
|
119
|
+
Rails.logger.error "Error loading Settings #{ e.message }"
|
120
|
+
|
121
|
+
{}
|
122
|
+
end
|
123
|
+
|
124
|
+
def clean_array_value(value)
|
125
|
+
value.map do |item|
|
126
|
+
if String === item && item =~ /^{(.*?)}$/
|
127
|
+
JSON.parse(item).symbolize_keys rescue item
|
128
|
+
else
|
129
|
+
item
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def clean_hash_value(key, value)
|
135
|
+
if value.nil?
|
136
|
+
nil
|
137
|
+
elsif key.to_s.ends_with?("_id") && value.to_s.strip =~ /^\d*$/
|
138
|
+
value.to_i
|
139
|
+
elsif value.to_s.strip =~ /^\d+$/ && value.to_s.strip.size < 8
|
140
|
+
value.to_i
|
141
|
+
elsif value.is_a?(String)
|
142
|
+
value.to_s.strip.presence
|
143
|
+
|
144
|
+
# Money duck type without requiring Money gem here
|
145
|
+
elsif value.respond_to?(:cents) && value.respond_to?(:currency)
|
146
|
+
value
|
147
|
+
elsif value.to_s.strip =~ /^\d+\.\d+$/
|
148
|
+
BigDecimal(value)
|
149
|
+
elsif value == "1" || value == "true"
|
150
|
+
true
|
151
|
+
elsif value == "0" || value == "false"
|
152
|
+
false
|
153
|
+
elsif Array === value
|
154
|
+
clean_array_value(value)
|
155
|
+
else
|
156
|
+
value
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# make true/false values consistent from forms
|
161
|
+
def clean_values(hash)
|
162
|
+
hash.reduce({}) do |result, (key, value)|
|
163
|
+
result[key] = clean_hash_value(key, value)
|
164
|
+
result
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
data/lib/provider_kit.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "provider_kit/engine"
|
4
|
+
require "provider_kit/exceptions"
|
5
|
+
require "provider_kit/version"
|
6
|
+
require "active_support/dependencies/autoload"
|
7
|
+
require "zeitwerk"
|
8
|
+
|
9
|
+
loader = Zeitwerk::Loader.new
|
10
|
+
path = File.expand_path(File.join(File.dirname(__FILE__), "provider_kit"))
|
11
|
+
|
12
|
+
loader.push_dir(path)
|
13
|
+
loader.ignore(File.expand_path(path, "generators"))
|
14
|
+
loader.setup
|
15
|
+
|
16
|
+
module ProviderKit
|
17
|
+
|
18
|
+
extend ActiveSupport::Autoload
|
19
|
+
include ActiveSupport::Configurable
|
20
|
+
|
21
|
+
## Config
|
22
|
+
configure do |config|
|
23
|
+
config.registered_providers = {}
|
24
|
+
|
25
|
+
# Set to :hash to just return a plain hash
|
26
|
+
config.capability_response_format = :object
|
27
|
+
|
28
|
+
# Default provider for given type
|
29
|
+
config.type_defaults = {}
|
30
|
+
end
|
31
|
+
|
32
|
+
eager_autoload do
|
33
|
+
autoload :Buildable
|
34
|
+
autoload :Registerable
|
35
|
+
end
|
36
|
+
|
37
|
+
## Core
|
38
|
+
autoload :Callbacks
|
39
|
+
autoload :EncryptedSettings
|
40
|
+
autoload :Encryptor
|
41
|
+
autoload :Execution
|
42
|
+
autoload :JsonClient
|
43
|
+
autoload :JsonRequest
|
44
|
+
autoload :Logging
|
45
|
+
autoload :Provideable
|
46
|
+
autoload :Provider
|
47
|
+
autoload :ProviderAttribute
|
48
|
+
autoload :Settings
|
49
|
+
|
50
|
+
## Third-party API setup
|
51
|
+
autoload :Capability
|
52
|
+
autoload :CapabilityExtension
|
53
|
+
autoload :Capable
|
54
|
+
autoload :Context
|
55
|
+
autoload :Registration
|
56
|
+
|
57
|
+
## Set up extensions for this module
|
58
|
+
extend Registerable
|
59
|
+
extend Buildable
|
60
|
+
|
61
|
+
end
|
metadata
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: provider_kit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- John D. Tornow
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-09-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '8.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '8.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: zeitwerk
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: annotaterb
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.15'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4.15'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: factory_bot_rails
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '6.5'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '6.5'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: faker
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.5'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.5'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec-rails
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '8.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '8.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.76'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.76'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: shoulda-matchers
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '6.5'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '6.5'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: timecop
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0.9'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0.9'
|
139
|
+
description: A generic interface and abstraction layer for dealing with third-party
|
140
|
+
data providers and APIs in Rails. ProviderKit helps you to add a layer between your
|
141
|
+
third-party APIs and internal code.
|
142
|
+
email:
|
143
|
+
- john@johntornow.com
|
144
|
+
executables: []
|
145
|
+
extensions: []
|
146
|
+
extra_rdoc_files: []
|
147
|
+
files:
|
148
|
+
- LICENSE
|
149
|
+
- README.md
|
150
|
+
- Rakefile
|
151
|
+
- lib/generators/provider/USAGE
|
152
|
+
- lib/generators/provider/provider_generator.rb
|
153
|
+
- lib/generators/provider/templates/capability.rb.tt
|
154
|
+
- lib/generators/provider/templates/context.rb.tt
|
155
|
+
- lib/generators/provider/templates/provider.rb.tt
|
156
|
+
- lib/generators/provider/templates/spec.rb.tt
|
157
|
+
- lib/provider_kit.rb
|
158
|
+
- lib/provider_kit/buildable.rb
|
159
|
+
- lib/provider_kit/callbacks.rb
|
160
|
+
- lib/provider_kit/capability.rb
|
161
|
+
- lib/provider_kit/capability_extension.rb
|
162
|
+
- lib/provider_kit/capable.rb
|
163
|
+
- lib/provider_kit/context.rb
|
164
|
+
- lib/provider_kit/encrypted_settings.rb
|
165
|
+
- lib/provider_kit/encryptor.rb
|
166
|
+
- lib/provider_kit/engine.rb
|
167
|
+
- lib/provider_kit/exceptions.rb
|
168
|
+
- lib/provider_kit/execution.rb
|
169
|
+
- lib/provider_kit/json_client.rb
|
170
|
+
- lib/provider_kit/json_request.rb
|
171
|
+
- lib/provider_kit/logging.rb
|
172
|
+
- lib/provider_kit/provideable.rb
|
173
|
+
- lib/provider_kit/provider.rb
|
174
|
+
- lib/provider_kit/provider_attribute.rb
|
175
|
+
- lib/provider_kit/registerable.rb
|
176
|
+
- lib/provider_kit/registration.rb
|
177
|
+
- lib/provider_kit/settings.rb
|
178
|
+
- lib/provider_kit/version.rb
|
179
|
+
homepage: https://github.com/jdtornow/provider_kit
|
180
|
+
licenses:
|
181
|
+
- MIT
|
182
|
+
metadata:
|
183
|
+
bug_tracker_uri: https://github.com/jdtornow/provider_kit/issues
|
184
|
+
changelog_uri: https://github.com/jdtornow/provider_kit/releases
|
185
|
+
homepage_uri: https://github.com/jdtornow/provider_kit
|
186
|
+
post_install_message:
|
187
|
+
rdoc_options: []
|
188
|
+
require_paths:
|
189
|
+
- lib
|
190
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '3.2'
|
195
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
196
|
+
requirements:
|
197
|
+
- - ">="
|
198
|
+
- !ruby/object:Gem::Version
|
199
|
+
version: 1.8.11
|
200
|
+
requirements: []
|
201
|
+
rubygems_version: 3.5.5
|
202
|
+
signing_key:
|
203
|
+
specification_version: 4
|
204
|
+
summary: A generic interface for dealing with third-party data providers and APIs.
|
205
|
+
test_files: []
|