mailchimp_api_v3 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +38 -0
  3. data/.hound.yml +4 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +26 -0
  6. data/CODE_OF_CONDUCT.md +15 -0
  7. data/Gemfile +2 -0
  8. data/Guardfile +16 -0
  9. data/LICENSE +29 -0
  10. data/README.md +44 -0
  11. data/lib/mailchimp.rb +8 -0
  12. data/lib/mailchimp/account.rb +14 -0
  13. data/lib/mailchimp/client.rb +44 -0
  14. data/lib/mailchimp/client/remote.rb +79 -0
  15. data/lib/mailchimp/collection.rb +45 -0
  16. data/lib/mailchimp/collection/paging.rb +70 -0
  17. data/lib/mailchimp/exception.rb +40 -0
  18. data/lib/mailchimp/instance.rb +31 -0
  19. data/lib/mailchimp/interest.rb +9 -0
  20. data/lib/mailchimp/interest_categories.rb +23 -0
  21. data/lib/mailchimp/interest_category.rb +19 -0
  22. data/lib/mailchimp/interests.rb +25 -0
  23. data/lib/mailchimp/list.rb +20 -0
  24. data/lib/mailchimp/lists.rb +21 -0
  25. data/lib/mailchimp/member.rb +15 -0
  26. data/lib/mailchimp/members.rb +23 -0
  27. data/lib/mailchimp/version.rb +3 -0
  28. data/mailchimp_api_v3.gemspec +34 -0
  29. data/spec/fixtures/cassettes/mailchimp.yml +710 -0
  30. data/spec/fixtures/cassettes/members.yml +266 -0
  31. data/spec/mailchimp/account_spec.rb +17 -0
  32. data/spec/mailchimp/client_spec.rb +56 -0
  33. data/spec/mailchimp/exception_spec.rb +45 -0
  34. data/spec/mailchimp/interest_categories_spec.rb +55 -0
  35. data/spec/mailchimp/interest_category_spec.rb +27 -0
  36. data/spec/mailchimp/interest_spec.rb +22 -0
  37. data/spec/mailchimp/interests_spec.rb +49 -0
  38. data/spec/mailchimp/list_spec.rb +36 -0
  39. data/spec/mailchimp/lists_spec.rb +15 -0
  40. data/spec/mailchimp/member_spec.rb +21 -0
  41. data/spec/mailchimp/members_spec.rb +34 -0
  42. data/spec/mailchimp_spec.rb +20 -0
  43. data/spec/spec_helper.rb +29 -0
  44. data/spec/support/api_key.rb +7 -0
  45. data/spec/support/vcr_setup.rb +14 -0
  46. 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
@@ -0,0 +1,4 @@
1
+ ---
2
+ ruby:
3
+ enabled: true
4
+ config_file: .rubocop.yml
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
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
@@ -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
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
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,8 @@
1
+ require 'mailchimp/version'
2
+ require 'mailchimp/client'
3
+
4
+ module Mailchimp
5
+ def self.connect(api_key = nil)
6
+ Client.new api_key
7
+ end
8
+ end
@@ -0,0 +1,14 @@
1
+ module Mailchimp
2
+ class Account
3
+ PATH_KEY = DATA_KEY = ''
4
+ include Instance
5
+
6
+ def id
7
+ account_id
8
+ end
9
+
10
+ def name
11
+ account_name
12
+ end
13
+ end
14
+ end
@@ -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