mailchimp_api_v3 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +38 -0
- data/.hound.yml +4 -0
- data/.rspec +2 -0
- data/.rubocop.yml +26 -0
- data/CODE_OF_CONDUCT.md +15 -0
- data/Gemfile +2 -0
- data/Guardfile +16 -0
- data/LICENSE +29 -0
- data/README.md +44 -0
- data/lib/mailchimp.rb +8 -0
- data/lib/mailchimp/account.rb +14 -0
- data/lib/mailchimp/client.rb +44 -0
- data/lib/mailchimp/client/remote.rb +79 -0
- data/lib/mailchimp/collection.rb +45 -0
- data/lib/mailchimp/collection/paging.rb +70 -0
- data/lib/mailchimp/exception.rb +40 -0
- data/lib/mailchimp/instance.rb +31 -0
- data/lib/mailchimp/interest.rb +9 -0
- data/lib/mailchimp/interest_categories.rb +23 -0
- data/lib/mailchimp/interest_category.rb +19 -0
- data/lib/mailchimp/interests.rb +25 -0
- data/lib/mailchimp/list.rb +20 -0
- data/lib/mailchimp/lists.rb +21 -0
- data/lib/mailchimp/member.rb +15 -0
- data/lib/mailchimp/members.rb +23 -0
- data/lib/mailchimp/version.rb +3 -0
- data/mailchimp_api_v3.gemspec +34 -0
- data/spec/fixtures/cassettes/mailchimp.yml +710 -0
- data/spec/fixtures/cassettes/members.yml +266 -0
- data/spec/mailchimp/account_spec.rb +17 -0
- data/spec/mailchimp/client_spec.rb +56 -0
- data/spec/mailchimp/exception_spec.rb +45 -0
- data/spec/mailchimp/interest_categories_spec.rb +55 -0
- data/spec/mailchimp/interest_category_spec.rb +27 -0
- data/spec/mailchimp/interest_spec.rb +22 -0
- data/spec/mailchimp/interests_spec.rb +49 -0
- data/spec/mailchimp/list_spec.rb +36 -0
- data/spec/mailchimp/lists_spec.rb +15 -0
- data/spec/mailchimp/member_spec.rb +21 -0
- data/spec/mailchimp/members_spec.rb +34 -0
- data/spec/mailchimp_spec.rb +20 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/api_key.rb +7 -0
- data/spec/support/vcr_setup.rb +14 -0
- metadata +288 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cea605da8196d54280fa8b3b52f804643720a4cc
|
4
|
+
data.tar.gz: 9adf62426b975cf8f73e98a52f571a04fe335b57
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5aa0078062716c9af0a986c84669a29eebc2cf982f5de986016b23a70a19e3aa6e19bcb3b8608fd254109f48614b736f86602183c8f24eab4086d77db5e9440b
|
7
|
+
data.tar.gz: 5fc82ff959426f5d6ad832747301c6bdf536feeaf95d080645f2ef0b5c6c565a71268e7b5c3bed8f2764f2448f2e7bcc030954b3d7f88677380dc18b16811632
|
data/.gitignore
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# Put your test API key in this file:
|
2
|
+
api_key.yml
|
3
|
+
|
4
|
+
*.gem
|
5
|
+
*.rbc
|
6
|
+
/.config
|
7
|
+
/coverage/
|
8
|
+
/InstalledFiles
|
9
|
+
/pkg/
|
10
|
+
/spec/reports/
|
11
|
+
/test/tmp/
|
12
|
+
/test/version_tmp/
|
13
|
+
/tmp/
|
14
|
+
|
15
|
+
## Specific to RubyMotion:
|
16
|
+
.dat*
|
17
|
+
.repl_history
|
18
|
+
build/
|
19
|
+
|
20
|
+
## Documentation cache and generated files:
|
21
|
+
/.yardoc/
|
22
|
+
/_yardoc/
|
23
|
+
/doc/
|
24
|
+
/rdoc/
|
25
|
+
|
26
|
+
## Environment normalisation:
|
27
|
+
/.bundle/
|
28
|
+
/vendor/bundle
|
29
|
+
/lib/bundler/man/
|
30
|
+
|
31
|
+
# for a library or gem, you might want to ignore these files since the code is
|
32
|
+
# intended to run in multiple environments; otherwise, check them in:
|
33
|
+
Gemfile.lock
|
34
|
+
.ruby-version
|
35
|
+
.ruby-gemset
|
36
|
+
|
37
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
38
|
+
.rvmrc
|
data/.hound.yml
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
---
|
2
|
+
StringLiterals:
|
3
|
+
EnforcedStyle: single_quotes
|
4
|
+
Enabled: true
|
5
|
+
|
6
|
+
DotPosition:
|
7
|
+
Description: 'Checks the position of the dot in multi-line method calls.'
|
8
|
+
EnforcedStyle: leading
|
9
|
+
Enabled: true
|
10
|
+
|
11
|
+
ClassAndModuleChildren:
|
12
|
+
Description: 'Checks style of children classes and modules.'
|
13
|
+
EnforcedStyle: nested
|
14
|
+
Enabled: true
|
15
|
+
|
16
|
+
Documentation:
|
17
|
+
Description: 'Document classes and non-namespace modules.'
|
18
|
+
Enabled: false
|
19
|
+
|
20
|
+
FileName:
|
21
|
+
Description: 'Use snake_case for source file names.'
|
22
|
+
Enabled: true
|
23
|
+
|
24
|
+
LineLength:
|
25
|
+
Max: 120
|
26
|
+
Enabled: true
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
### Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
|
12
|
+
|
13
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
14
|
+
|
15
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/)
|
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
guard :rubocop do
|
2
|
+
watch(/.+\.rb$/)
|
3
|
+
watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
|
4
|
+
end
|
5
|
+
|
6
|
+
guard(
|
7
|
+
:rspec,
|
8
|
+
all_after_pass: true,
|
9
|
+
all_on_start: true,
|
10
|
+
cmd: 'NO_SIMPLECOV=true bundle exec rspec --fail-fast --format documentation'
|
11
|
+
) do
|
12
|
+
watch(%r{spec/.+_spec\.rb$})
|
13
|
+
watch(%r{lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
14
|
+
watch('spec/spec_helper.rb') { 'spec' }
|
15
|
+
watch(%r{^spec/support/.+\.rb$}) { 'spec' }
|
16
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
Copyright (c) 2015, Xenapto
|
2
|
+
Some code copyright (c) 2015, The Rocket Science Group, LLC
|
3
|
+
All rights reserved.
|
4
|
+
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
7
|
+
|
8
|
+
* Redistributions of source code must retain the above copyright notice, this
|
9
|
+
list of conditions and the following disclaimer.
|
10
|
+
|
11
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
13
|
+
and/or other materials provided with the distribution.
|
14
|
+
|
15
|
+
* Neither the name of mailchimp_api_v3 nor the names of its
|
16
|
+
contributors may be used to endorse or promote products derived from
|
17
|
+
this software without specific prior written permission.
|
18
|
+
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
|
data/README.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
## Mailchimp API v3
|
2
|
+
|
3
|
+
![Gem Version(https://rubygems.org/gems/mailchimp_api_v3)](http://img.shields.io/gem/v/mailchimp_api_v3.svg?style=flat)
|
4
|
+
![build status(https://circleci.com/gh/Xenapto/mailchimp_api_v3)](https://img.shields.io/circleci/project/Xenapto/mailchimp_api_v3/develop.svg)
|
5
|
+
[![Code Climate](http://img.shields.io/codeclimate/github/Xenapto/mailchimp_api_v3.svg?style=flat)](https://codeclimate.com/github/Xenapto/mailchimp_api_v3)
|
6
|
+
[![Coverage Status](https://img.shields.io/coveralls/Xenapto/mailchimp_api_v3/develop.svg?style=flat)](https://coveralls.io/r/Xenapto/mailchimp_api_v3?branch=develop)
|
7
|
+
[![Developer status](http://img.shields.io/badge/developer-awesome-brightgreen.svg?style=flat)](http://xenapto.com)
|
8
|
+
|
9
|
+
A simple gem to interact with Mailchimp through their API v3
|
10
|
+
|
11
|
+
### Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
gem 'mailchimp_api_v3'
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install mailchimp_api_v3
|
24
|
+
|
25
|
+
### Usage
|
26
|
+
|
27
|
+
Examples:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
Mailchimp.connect(mc_key).lists
|
31
|
+
```
|
32
|
+
|
33
|
+
### Contributing
|
34
|
+
|
35
|
+
1. Fork it
|
36
|
+
1. Create your feature branch (`git checkout -b my-new-feature`)
|
37
|
+
1. Commit your changes (`git commit -am 'Add some feature'`)
|
38
|
+
1. Push to the branch (`git push origin my-new-feature`)
|
39
|
+
1. Create new Pull Request
|
40
|
+
|
41
|
+
### Acknowledgements
|
42
|
+
|
43
|
+
I used the sample code in https://github.com/mailchimp/APIv3-examples as my starting point for this gem. Thanks to
|
44
|
+
the Mailchimp developers for the head start.
|
data/lib/mailchimp.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'mailchimp/exception'
|
2
|
+
require 'mailchimp/collection'
|
3
|
+
require 'mailchimp/instance'
|
4
|
+
require 'mailchimp/account'
|
5
|
+
require 'mailchimp/lists'
|
6
|
+
require 'mailchimp/client/remote'
|
7
|
+
|
8
|
+
module Mailchimp
|
9
|
+
class Client
|
10
|
+
include Remote
|
11
|
+
|
12
|
+
def account
|
13
|
+
Account.new self, get
|
14
|
+
end
|
15
|
+
|
16
|
+
def lists
|
17
|
+
Lists.new self
|
18
|
+
end
|
19
|
+
|
20
|
+
def connected?
|
21
|
+
account
|
22
|
+
rescue Mailchimp::Exception::APIKeyError
|
23
|
+
false
|
24
|
+
else
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def initialize(api_key = nil, extra_headers = {})
|
31
|
+
@api_key = api_key || ENV['MAILCHIMP_API_KEY']
|
32
|
+
fail Mailchimp::Exception::APIKeyError, 'title' => 'Invalid API key format' unless api_key_valid?
|
33
|
+
@extra_headers = extra_headers
|
34
|
+
end
|
35
|
+
|
36
|
+
def api_key_valid?
|
37
|
+
@api_key =~ /\w+-\w{3}/
|
38
|
+
end
|
39
|
+
|
40
|
+
def dc
|
41
|
+
@dc ||= @api_key.split('-')[1]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'restclient'
|
3
|
+
|
4
|
+
module Mailchimp
|
5
|
+
class Client
|
6
|
+
module Remote
|
7
|
+
def get(path = '', options = {})
|
8
|
+
managed_remote path, options, :get
|
9
|
+
end
|
10
|
+
|
11
|
+
def post(data, path = '', options = {})
|
12
|
+
managed_remote path, options, :post, data
|
13
|
+
end
|
14
|
+
|
15
|
+
def patch(data, path = '', options = {})
|
16
|
+
managed_remote path, options, :patch, data
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
RETRY_EXCEPTIONS = [SocketError]
|
22
|
+
|
23
|
+
def managed_remote(path = '', options = {}, method = :get, payload = nil)
|
24
|
+
headers_and_params = headers.merge params_from(options)
|
25
|
+
url = "#{url_stub}#{path}"
|
26
|
+
# puts url # debug
|
27
|
+
YAML.load naked_remote(url, method, headers_and_params, payload)
|
28
|
+
rescue *RETRY_EXCEPTIONS => e
|
29
|
+
@retries ||= 0
|
30
|
+
raise e if (@retries += 1) > 3
|
31
|
+
retry
|
32
|
+
rescue => e # TODO: Find out why this doesn't fire if we rescue RestClient::Exception
|
33
|
+
managed_remote_exception e
|
34
|
+
end
|
35
|
+
|
36
|
+
def managed_remote_exception(e)
|
37
|
+
case e.class.to_s # TODO: Find out why this won't match the class except by name string
|
38
|
+
when 'RestClient::Unauthorized'
|
39
|
+
fail Mailchimp::Exception::APIKeyError, YAML.load(e.http_body)
|
40
|
+
when 'RestClient::BadRequest'
|
41
|
+
Mailchimp::Exception.parse_invalid_resource_exception YAML.load(e.http_body)
|
42
|
+
else
|
43
|
+
fail e
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def naked_remote(url, method, headers_and_params, payload = nil)
|
48
|
+
if [:get, :delete, :head, :options].include? method
|
49
|
+
remote_no_payload(url, method, headers_and_params)
|
50
|
+
else
|
51
|
+
remote_with_payload(url, payload, method, headers_and_params)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def remote_no_payload(url, method = :get, headers_and_params = {})
|
56
|
+
RestClient.__send__ method, url, headers_and_params
|
57
|
+
end
|
58
|
+
|
59
|
+
def remote_with_payload(url, payload, method = :post, headers_and_params = {})
|
60
|
+
RestClient.__send__ method, url, payload.to_json, headers_and_params
|
61
|
+
end
|
62
|
+
|
63
|
+
def headers
|
64
|
+
@headers ||= {
|
65
|
+
'Authorization' => "apikey #{@api_key}",
|
66
|
+
'User-Agent' => 'Mailchimp API v3 Ruby gem https://rubygems.org/gems/mailchimp_api_v3'
|
67
|
+
}.merge @extra_headers
|
68
|
+
end
|
69
|
+
|
70
|
+
def url_stub
|
71
|
+
@url_stub ||= "https://#{dc}.api.mailchimp.com/3.0"
|
72
|
+
end
|
73
|
+
|
74
|
+
def params_from(options = {})
|
75
|
+
options == {} ? {} : { 'params' => options }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'mailchimp/collection/paging'
|
2
|
+
|
3
|
+
module Mailchimp
|
4
|
+
module Collection
|
5
|
+
include Paging
|
6
|
+
|
7
|
+
def initialize(client, parent_path = '', options = {})
|
8
|
+
@client = client
|
9
|
+
@parent_path = parent_path
|
10
|
+
|
11
|
+
parse_options(options)
|
12
|
+
super page_children
|
13
|
+
end
|
14
|
+
|
15
|
+
def count
|
16
|
+
page['total_items']
|
17
|
+
end
|
18
|
+
|
19
|
+
def path
|
20
|
+
@path ||= "#{@parent_path}/#{self.class.path_key}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def find(data)
|
24
|
+
find_each do |instance|
|
25
|
+
matches = data.each do |k, v|
|
26
|
+
break false unless instance.__send__(k).casecmp(v).zero? # case-insensitive comparison
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
break instance if matches
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def create(data)
|
35
|
+
response = @client.post data, path
|
36
|
+
self.class.child_class.new @client, response, path
|
37
|
+
end
|
38
|
+
|
39
|
+
def first_or_create(data)
|
40
|
+
instance = find(data)
|
41
|
+
return instance if instance
|
42
|
+
create(data)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Mailchimp
|
2
|
+
module Collection
|
3
|
+
module Paging
|
4
|
+
DEFAULT_PAGE_SIZE = 500
|
5
|
+
|
6
|
+
def page
|
7
|
+
return @page if @page
|
8
|
+
@page = @client.get(path, fetch_options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def fetch_options
|
12
|
+
links_delim = self.class.data_key.empty? ? '' : '.'
|
13
|
+
|
14
|
+
{
|
15
|
+
'exclude_fields' => "#{self.class.data_key}#{links_delim}_links",
|
16
|
+
'offset' => offset,
|
17
|
+
'count' => page_size
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def page_array
|
22
|
+
@page_array ||= page[self.class.data_key]
|
23
|
+
end
|
24
|
+
|
25
|
+
def page_children
|
26
|
+
@page_children ||= page_array.map { |d| self.class.child_class.new @client, d, path }
|
27
|
+
end
|
28
|
+
|
29
|
+
def offset
|
30
|
+
@offset ||= 0
|
31
|
+
end
|
32
|
+
|
33
|
+
def page_size
|
34
|
+
@page_size ||= DEFAULT_PAGE_SIZE
|
35
|
+
end
|
36
|
+
|
37
|
+
def find_in_pages(options = {})
|
38
|
+
parse_options(options)
|
39
|
+
|
40
|
+
loop do
|
41
|
+
yield page_children
|
42
|
+
@offset += page_size
|
43
|
+
|
44
|
+
if offset > count
|
45
|
+
@offset = 0
|
46
|
+
break
|
47
|
+
end
|
48
|
+
|
49
|
+
invalidate_current_page
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_each
|
54
|
+
find_in_pages do |p|
|
55
|
+
p.each { |child| yield child }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_options(options = {})
|
60
|
+
@offset = options['start'] if options.key? 'start'
|
61
|
+
@page_size = options['page_size'] if options.key? 'page_size'
|
62
|
+
invalidate_current_page
|
63
|
+
end
|
64
|
+
|
65
|
+
def invalidate_current_page
|
66
|
+
@page = @page_array = @page_children = nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|