marketo_chef 1.0.2

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.
@@ -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: []