marketo_chef 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4c36b0fb24e96ea385f40fa34880052e0d01908f
4
+ data.tar.gz: 9dcf3329d2f194cdf092713d783475c0d543adcc
5
+ SHA512:
6
+ metadata.gz: bb2b0184f2497951fb2ab25ee12f619eaca6b120bf31fb61af55c428de5f15d85a78cf557e6d1a3bcb7c9993d8f4272642daa6a053f2b20ce7abbd08ebca5d1a
7
+ data.tar.gz: 67a90747de4831b81b40f11f4d1da75fbd514ba2998d90799a130196fcbd90e648f51e006a1f3906d1065a9251a9a07a8dd061f882798c4b1eaf7a2077cbcc3d
@@ -0,0 +1,4 @@
1
+ MARKETO_HOST="123-ABC-456.mktorest.com"
2
+ MARKETO_CLIENT_ID="c4206ec5-be87-465c-a0cc-99486a4c4e1b"
3
+ MARKETO_CLIENT_SECRET="Z5bzTySMrFdgcFZkSNvBywMuUen9ah7Q"
4
+ MARKETO_CAMPAIGN_ID="1234"
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1 @@
1
+ marketo_chef
@@ -0,0 +1 @@
1
+ 2.4.1
@@ -0,0 +1,3 @@
1
+ sudo: false
2
+ language: ruby
3
+ before_install: gem install bundler -v 1.14.6
@@ -0,0 +1,25 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](http://keepachangelog.com/)
5
+ and this project adheres to [Semantic Versioning](http://semver.org/).
6
+
7
+ ## [1.0.2] - 2017.07.06
8
+ ### Changed
9
+ - Marketo error codes are declared as Strings
10
+ - Pass the `reasons` array from the response to `handle_skipped`
11
+
12
+ ## [1.0.1] - 2017.06.27
13
+ ### Added
14
+ - Release history via CHANGELOG.md
15
+ - Documentation via README.md
16
+ - Checking response for communication failure (2d9f5c822a95187968944420998dc741fb8f2024)
17
+
18
+ ### Changed
19
+ - Error handling around response-level errors
20
+ - Lower required Faraday version due to transitive dependencies in client
21
+ applications (ce4109847c3569c1f2b442d32a530e32b8ae74bd)
22
+
23
+ ## [0.1.0] - 2017.05.05
24
+ ### Added
25
+ - Method to add lead, assign to campaign
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 marketo_chef.gemspec
6
+ gemspec
@@ -0,0 +1,85 @@
1
+ # MarketoChef
2
+
3
+ This gem defines a Marketo API client providing a simple interface to our
4
+ single common use case: adding a lead to the Marketo database and assigning it
5
+ to a specific campaign.
6
+
7
+ [![Build Status](https://travis-ci.com/chef/marketo_chef.svg?token=QMKWuXEPx23uB8JGd4tb&branch=master)](https://travis-ci.com/chef/marketo_chef)
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby gem 'marketo_chef' ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install marketo_chef
22
+
23
+ ## Usage
24
+
25
+ The gem expects client code to configure it before use. Like so:
26
+ ```ruby
27
+ MarketoChef.configure do |c|
28
+ c.host = ENV['MARKETO_HOST']
29
+ c.client_id = ENV['MARKETO_CLIENT_ID']
30
+ c.client_secret = ENV['MARKETO_CLIENT_SECRET']
31
+ c.campaign_id = ENV['MARKETO_CAMPAIGN_ID']
32
+ end
33
+ ```
34
+ Conventionally this would be added to an initializer in Rails or Hanami, for
35
+ example in a file `config/initializers/01_marketo_chef.rb`, with Rails. This
36
+ ensures the application will attempt to configure the gem at start time rather
37
+ than later, when discovering that configuration values aren't available
38
+ (perhaps an environment variable name is incorrect) will generate unexpected
39
+ errors.
40
+
41
+ Required environment variables, with bogus example values:
42
+ ```shell
43
+ MARKETO_HOST="123-ABC-456.mktorest.com"
44
+ MARKETO_CLIENT_ID="c4206ec5-be87-465c-a0cc-99486a4c4e1b"
45
+ MARKETO_CLIENT_SECRET="Z5bzTySMrFdgcFZkSNvBywMuUen9ah7Q"
46
+ MARKETO_CAMPAIGN_ID="1234"
47
+ ```
48
+
49
+ The gem exposes a single method for lead tracking, which accepts a hash of lead
50
+ data, creates or updates the lead in Marketo, and adds it to the specified
51
+ campaign.
52
+
53
+ ```ruby
54
+ MarketoChef.add_lead(
55
+ 'formname': 'Some Form',
56
+ 'product-interest': 'some-interest',
57
+ 'firstName': 'Jane',
58
+ 'lastName': 'Doe',
59
+ 'email': 'jdoe@example.com',
60
+ 'company': 'ExampleCo'
61
+ )
62
+ ```
63
+
64
+ ## Development
65
+
66
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
67
+ `rake test` to run the tests. You can also run `bin/console` for an interactive
68
+ prompt that will allow you to experiment.
69
+
70
+ You can also run `rubocop` to verify style guide adherence. It happens along
71
+ with the tests in CI, but better to catch those problems before committing and
72
+ pushing and so on.
73
+
74
+ To install this gem onto your local machine, run `bundle exec rake install`.
75
+
76
+ To release a new version, update the version number in `version.rb`, then
77
+ create a git tag for the version, and update CHANGELOG.md to document the
78
+ release. Finally, push git commits and tags to GitHub.
79
+
80
+ This gem is not public and should not be published to rubygems.org.
81
+
82
+ ## Contributing
83
+
84
+ Bug reports and pull requests are welcome on GitHub at
85
+ https://github.com/chef/marketo_chef.
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << 'test'
8
+ t.libs << 'lib'
9
+ t.test_files = FileList['test/**/*_test.rb']
10
+ end
11
+
12
+ desc 'continuous integration tasks'
13
+ task ci: %i[test rubocop] do
14
+ end
15
+
16
+ task :rubocop do
17
+ sh 'rubocop'
18
+ end
19
+
20
+ task default: :ci
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require 'bundler/setup'
6
+ require 'marketo_chef'
7
+
8
+ # You can add fixtures and/or initialization code here to make experimenting
9
+ # with your gem easier. You can also use a different console, if you like.
10
+
11
+ # (If you use this, don't forget to add pry to your Gemfile!)
12
+ # require "pry"
13
+ # Pry.start
14
+
15
+ require 'irb'
16
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ require 'marketo_chef/client'
6
+ require 'marketo_chef/configuration'
7
+ require 'marketo_chef/version'
8
+
9
+ # Common usage of the Marketo API for our web properties
10
+ module MarketoChef
11
+ # http://developers.marketo.com/rest-api/error-codes/#response_level_errors
12
+ # 607: Daily API Request Limit Reached
13
+ # 611: System Error (generic unhandled exception)
14
+ RESPONSE_ERROR_CODES = %w[607 611].freeze
15
+
16
+ # http://developers.marketo.com/rest-api/error-codes/#record_level_errors
17
+ # 1001: Invalid value '%s'. Required of type '%s'
18
+ # 1002: Missing value for required parameter '%s'
19
+ # 1003: Invalid data
20
+ # 1006: Field '%s' not found
21
+ # 1007: Multiple leads match the lookup criteria
22
+ # ...these are the only ones we might need to care about currently
23
+ MAYBE_OUR_FAULT_CODES = %w[1003 1006].freeze
24
+ MAYBE_THEIR_FAULT_CODES = %w[1001 1002].freeze
25
+
26
+ class << self
27
+ extend Forwardable
28
+
29
+ def_delegators :configuration,
30
+ :campaign_id, :client_id, :client_secret, :host
31
+
32
+ def configuration
33
+ @configuration ||= Configuration.new
34
+ end
35
+
36
+ def configure
37
+ yield(configuration)
38
+
39
+ configuration.validate
40
+ end
41
+
42
+ def add_lead(lead)
43
+ result = sync(lead)
44
+
45
+ return unless result
46
+
47
+ handle_skipped(lead, result['reasons']) if result['status'] == 'skipped'
48
+
49
+ trigger_campaign(campaign_id, result['id']) if result.key?('id')
50
+ end
51
+
52
+ private
53
+
54
+ def sync(lead)
55
+ response = Client.instance.sync_lead(lead)
56
+
57
+ if response.key?('errors')
58
+ handle_response_err(response)
59
+ else
60
+ response['result']&.first
61
+ end
62
+ end
63
+
64
+ def trigger_campaign(campaign_id, lead_id)
65
+ response = Client.instance.trigger_campaign(campaign_id, lead_id)
66
+
67
+ campaign_error(campaign_id, lead_id, response) if response.key?('errors')
68
+
69
+ response
70
+ end
71
+
72
+ def capture_error(message)
73
+ puts message
74
+ Raven.capture_exception(Exception.new(message))
75
+ end
76
+
77
+ def handle_response_err(response)
78
+ codes = ->(c) { c['code'] }
79
+ known = ->(c) { RESPONSE_ERROR_CODES.include?(c) }
80
+ feedback = ->(r) { "#{r['code']}: #{r['message']}" }
81
+
82
+ return if reasons.collect(&codes).any?(&known)
83
+
84
+ response_error(response.reject(&known).collect(&feedback))
85
+ end
86
+
87
+ def handle_skipped(lead, reasons)
88
+ codes = ->(c) { c['code'] }
89
+ reportable = ->(c) { MAYBE_OUR_FAULT_CODES.include?(c) }
90
+ feedback = ->(r) { "#{r['code']}: #{r['message']}" }
91
+
92
+ return unless reasons.collect(&codes).any?(&reportable)
93
+
94
+ skipped_lead_error(lead, reasons.collect(&feedback))
95
+ rescue TypeError => error
96
+ skipped_lead_type_error(lead, reasons, error)
97
+ end
98
+
99
+ def response_error(reasons)
100
+ capture_error <<~ERR
101
+ Response Error:\n
102
+ \t#{reasons.join("\n\t")}
103
+ ERR
104
+ end
105
+
106
+ def campaign_error(campaign_id, lead, errors)
107
+ capture_error <<~ERR
108
+ Campaign Triggering Error: #{errors}\n
109
+ \tcampaign id: #{campaign_id}\n
110
+ \tlead: #{lead}
111
+ ERR
112
+ end
113
+
114
+ def skipped_lead_error(lead, reasons)
115
+ capture_error <<~ERR
116
+ Lead Submission Skipped:\n
117
+ \tinput: #{lead}\n
118
+ \t#{reasons.join("\n\t")}
119
+ ERR
120
+ end
121
+
122
+ def skipped_lead_type_error(lead, reasons, error)
123
+ capture_error <<~ERR
124
+ Something went wrong trying to parse this skipped lead response:\n
125
+ \tLead: #{lead}\n
126
+ \tReasons: #{reasons}\n
127
+ \tError: #{error}
128
+ ERR
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'singleton'
5
+
6
+ require 'faraday'
7
+ require 'faraday_middleware'
8
+
9
+ module MarketoChef
10
+ # Faraday wrapper to handle communication with Marketo API
11
+ class Client
12
+ include Singleton
13
+ extend Forwardable
14
+
15
+ def_delegators :MarketoChef, :client_id, :client_secret, :host
16
+
17
+ def sync_lead(lead)
18
+ authenticate!
19
+
20
+ connection.post do |req|
21
+ req.url '/rest/v1/leads.json'
22
+ req.headers[:authorization] = "Bearer #{@token}"
23
+ req.body = { action: 'createOrUpdate', input: [lead] }
24
+ end.body
25
+ end
26
+
27
+ def trigger_campaign(campaign_id, lead_id)
28
+ authenticate!
29
+
30
+ connection.post do |req|
31
+ req.url "/rest/v1/campaigns/#{campaign_id}/trigger.json"
32
+ req.headers[:authorization] = "Bearer #{@token}"
33
+ req.body = { input: { leads: [{ id: lead_id }] } }
34
+ end.body
35
+ end
36
+
37
+ private
38
+
39
+ def connection
40
+ @connection ||= Faraday.new(url: "https://#{host}") do |conn|
41
+ conn.request :multipart
42
+ conn.request :json
43
+ conn.response :json, content_type: /\bjson$/
44
+ conn.adapter Faraday.default_adapter
45
+ end
46
+ end
47
+
48
+ def authenticate!
49
+ authenticate unless authenticated?
50
+ end
51
+
52
+ def authenticated?
53
+ @token && valid_token?
54
+ end
55
+
56
+ def valid_token?
57
+ return false unless defined?(@valid_until)
58
+
59
+ Time.now < @valid_until
60
+ end
61
+
62
+ def authenticate
63
+ connection.get('/identity/oauth/token', auth_params).tap do |res|
64
+ inspect_auth_response(res.body)
65
+
66
+ save_authentication(res.body)
67
+ end
68
+ end
69
+
70
+ def inspect_auth_response(body)
71
+ # res.body may be an HTML string, "The document has moved" message,
72
+ # which occurs if the host is mktoapi.com instead of mktorest.com.
73
+ raise body if body.is_a?(String)
74
+ raise body['errors'][0]['message'] if body.key?('errors')
75
+ end
76
+
77
+ def auth_params
78
+ {
79
+ grant_type: 'client_credentials',
80
+ client_id: client_id,
81
+ client_secret: client_secret
82
+ }
83
+ end
84
+
85
+ def save_authentication(data)
86
+ @token = data.fetch('access_token')
87
+ @token_type = data.fetch('token_type')
88
+ @valid_until = Time.now + data.fetch('expires_in')
89
+ @scope = data.fetch('scope')
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MarketoChef
4
+ # Store and validate Marketo client configuration data
5
+ class Configuration
6
+ FIELDS = %i[host client_id client_secret campaign_id].freeze
7
+
8
+ attr_accessor :client_id, :client_secret, :campaign_id
9
+ attr_reader :host
10
+
11
+ def initialize(config = {})
12
+ return unless config.any?
13
+
14
+ config.map { |k, v| send("#{k}=", v) if FIELDS.include?(k) }
15
+ end
16
+
17
+ def host=(value)
18
+ value = value.sub('mktoapi', 'mktorest')
19
+
20
+ match = %r{https?:\/\/(.+)}.match(value)
21
+
22
+ @host = match ? match[1] : value
23
+ end
24
+
25
+ def validate
26
+ missing = ->(f) { !instance_variable_defined?(:"@#{f}") }
27
+ bomb = ->(f) { raise "Required Marketo configuration undefined: #{f}" }
28
+
29
+ FIELDS.select(&missing).map(&bomb)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MarketoChef
4
+ VERSION = '1.0.2'
5
+ end
@@ -0,0 +1,51 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ lib = File.expand_path('../lib', __FILE__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+ require 'marketo_chef/version'
7
+
8
+ # rubocop:disable Style/GuardClause
9
+ # rubocop:disable Metrics/BlockLength
10
+ Gem::Specification.new do |spec|
11
+ spec.name = 'marketo_chef'
12
+ spec.version = MarketoChef::VERSION
13
+ spec.authors = ['Trevor Bramble']
14
+ spec.email = ['tbramble@chef.io']
15
+
16
+ spec.summary = 'Marketo API client for our common uses and error handling'
17
+ spec.homepage = 'https://github.com/chef/marketo_chef'
18
+
19
+ spec.licenses = [] # closed-source
20
+
21
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the
22
+ # 'allowed_push_host' to allow pushing to a single host or delete this
23
+ # section to allow pushing to any host.
24
+ # if spec.respond_to?(:metadata)
25
+ # spec.metadata['allowed_push_host'] = 'https://ixnay.ushpay'
26
+ # else
27
+ # raise 'RubyGems >= 2.0 required to protect against public gem pushes.'
28
+ # end
29
+
30
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
31
+ f.match(%r{^(test|spec|features)/})
32
+ end
33
+ spec.bindir = 'exe'
34
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
35
+ spec.require_paths = ['lib']
36
+
37
+ # This gem needs to be compatible with OmniAuth (used on the Learn Chef site)
38
+ # which depends on the OAuth2 gem, which requires faraday ['>= 0.8', '< 0.12']
39
+ # https://github.com/intridea/oauth2/blob/v1.3.1/oauth2.gemspec
40
+ spec.add_runtime_dependency 'faraday', '~> 0.11.0'
41
+ spec.add_runtime_dependency 'faraday_middleware', '~> 0.11'
42
+
43
+ spec.add_development_dependency 'bundler', '~> 1.14'
44
+ spec.add_development_dependency 'minitest', '~> 5.10'
45
+ spec.add_development_dependency 'rake', '~> 12.0'
46
+ spec.add_development_dependency 'rspec-expectations', '~> 3.6'
47
+ spec.add_development_dependency 'rubocop', '~> 0.48'
48
+ spec.add_development_dependency 'travis', '~> 1.8'
49
+ end
50
+ # rubocop:enable Metrics/BlockLength
51
+ # rubocop:enable Style/GuardClause
metadata ADDED
@@ -0,0 +1,171 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: marketo_chef
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Trevor Bramble
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-03-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.11.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.11.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday_middleware
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.11'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.11'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.14'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.14'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.10'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.10'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '12.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '12.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec-expectations
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.6'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.6'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.48'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.48'
111
+ - !ruby/object:Gem::Dependency
112
+ name: travis
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.8'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.8'
125
+ description:
126
+ email:
127
+ - tbramble@chef.io
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".env.example"
133
+ - ".gitignore"
134
+ - ".ruby-gemset"
135
+ - ".ruby-version"
136
+ - ".travis.yml"
137
+ - CHANGELOG.md
138
+ - Gemfile
139
+ - README.md
140
+ - Rakefile
141
+ - bin/console
142
+ - bin/setup
143
+ - lib/marketo_chef.rb
144
+ - lib/marketo_chef/client.rb
145
+ - lib/marketo_chef/configuration.rb
146
+ - lib/marketo_chef/version.rb
147
+ - marketo_chef.gemspec
148
+ homepage: https://github.com/chef/marketo_chef
149
+ licenses: []
150
+ metadata: {}
151
+ post_install_message:
152
+ rdoc_options: []
153
+ require_paths:
154
+ - lib
155
+ required_ruby_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ required_rubygems_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ requirements: []
166
+ rubyforge_project:
167
+ rubygems_version: 2.6.14
168
+ signing_key:
169
+ specification_version: 4
170
+ summary: Marketo API client for our common uses and error handling
171
+ test_files: []