pew_pew 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/.gitignore +19 -0
  2. data/.rspec +2 -0
  3. data/.yardopts +1 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE +22 -0
  6. data/README.md +84 -0
  7. data/Rakefile +3 -0
  8. data/lib/pew_pew.rb +32 -0
  9. data/lib/pew_pew/client.rb +90 -0
  10. data/lib/pew_pew/config.rb +15 -0
  11. data/lib/pew_pew/domain.rb +9 -0
  12. data/lib/pew_pew/resource.rb +52 -0
  13. data/lib/pew_pew/resources/bounces.rb +41 -0
  14. data/lib/pew_pew/resources/campaigns.rb +141 -0
  15. data/lib/pew_pew/resources/complaints.rb +40 -0
  16. data/lib/pew_pew/resources/lists.rb +121 -0
  17. data/lib/pew_pew/resources/logs.rb +12 -0
  18. data/lib/pew_pew/resources/mailboxes.rb +41 -0
  19. data/lib/pew_pew/resources/messages.rb +37 -0
  20. data/lib/pew_pew/resources/routes.rb +57 -0
  21. data/lib/pew_pew/resources/stats.rb +12 -0
  22. data/lib/pew_pew/resources/unsubscribes.rb +43 -0
  23. data/lib/pew_pew/response.rb +11 -0
  24. data/lib/pew_pew/version.rb +3 -0
  25. data/pew_pew.gemspec +20 -0
  26. data/spec/fixtures/bounces.yml +144 -0
  27. data/spec/fixtures/campaigns.yml +368 -0
  28. data/spec/fixtures/complaints.yml +142 -0
  29. data/spec/fixtures/image.png +0 -0
  30. data/spec/fixtures/lists.yml +403 -0
  31. data/spec/fixtures/logs.yml +133 -0
  32. data/spec/fixtures/mailboxes.yml +141 -0
  33. data/spec/fixtures/messages.yml +196 -0
  34. data/spec/fixtures/mime.eml +34 -0
  35. data/spec/fixtures/routes.yml +225 -0
  36. data/spec/fixtures/stats.yml +61 -0
  37. data/spec/fixtures/unsubscribes.yml +219 -0
  38. data/spec/pew_pew/client_spec.rb +51 -0
  39. data/spec/pew_pew/config_spec.rb +38 -0
  40. data/spec/pew_pew/resource_spec.rb +46 -0
  41. data/spec/pew_pew/resources/bounces_spec.rb +78 -0
  42. data/spec/pew_pew/resources/campaigns_spec.rb +228 -0
  43. data/spec/pew_pew/resources/complaints_spec.rb +69 -0
  44. data/spec/pew_pew/resources/lists_spec.rb +283 -0
  45. data/spec/pew_pew/resources/logs_spec.rb +28 -0
  46. data/spec/pew_pew/resources/mailboxes_spec.rb +59 -0
  47. data/spec/pew_pew/resources/messages_spec.rb +53 -0
  48. data/spec/pew_pew/resources/routes_spec.rb +135 -0
  49. data/spec/pew_pew/resources/stats_spec.rb +26 -0
  50. data/spec/pew_pew/resources/unsubscribes_spec.rb +99 -0
  51. data/spec/pew_pew/response_spec.rb +21 -0
  52. data/spec/pew_pew_spec.rb +13 -0
  53. data/spec/spec_helper.rb +17 -0
  54. data/spec/support/contexts/api_requests.rb +23 -0
  55. data/spec/support/contexts/domain_resource.rb +17 -0
  56. data/spec/support/vcr.rb +6 -0
  57. metadata +211 -0
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .rvmrc
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ bin
11
+ coverage
12
+ doc
13
+ lib/bundler/man
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1 @@
1
+ --markup markdown
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Tyler Hunt
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,84 @@
1
+ # Pew Pew
2
+
3
+ A Ruby client library for using the [Mailgun] web service.
4
+
5
+ [mailgun]: https://mailgun.net/
6
+
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's `Gemfile`:
11
+
12
+ ``` ruby
13
+ gem 'pew_pew'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install pew_pew
23
+
24
+
25
+ ## Configuration
26
+
27
+ You must have a valid API key to use the Mailgun API. If you don't yet have
28
+ one, you can [sign up here][api-key].
29
+
30
+ [api-key]: http://www.mailgun.net/signup
31
+
32
+ You can use the following method to configure your API key and domain:
33
+
34
+ ``` ruby
35
+ PewPew.configure do |config|
36
+ config.api_key = ENV['MAILGUN_API_KEY']
37
+ config.domain = ENV['MAILGUN_DOMAIN'] # optional
38
+ end
39
+ ```
40
+
41
+ If you'd like to use multiple instances of the API with different keys, you can
42
+ instantiate `PewPew::Client` directly and treat those instances the same as you
43
+ would the `PewPew` module:
44
+
45
+ ``` ruby
46
+ pew_pew = PewPew::Client.new
47
+
48
+ pew_pew.configure do |config|
49
+ config.api_key = ENV['MAILGUN_API_KEY']
50
+ config.domain = ENV['MAILGUN_DOMAIN'] # optional
51
+ end
52
+ ```
53
+
54
+
55
+ ## Usage
56
+
57
+ Once the API key has been configured, resources can be called on the `PewPew`
58
+ module directly or off your client instances:
59
+
60
+ ``` ruby
61
+ PewPew.messages.send_email(
62
+ to: 'to@example.com',
63
+ from: 'from@example.com',
64
+ subject: 'Test',
65
+ text: 'This is a test message.'
66
+ )
67
+ ```
68
+
69
+ For resources that require a domain, you may pass it as an option when calling
70
+ the resource. If a domain has been configured, it will be used as the default.
71
+
72
+ ``` ruby
73
+ PewPew.stats.all # uses the configured domain
74
+ PewPew.stats(domain: 'example.com').all # uses example.com
75
+ ```
76
+
77
+
78
+ ## Contributing
79
+
80
+ 1. Fork it
81
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
82
+ 3. Commit your changes (`git commit -am 'Add some feature.'`)
83
+ 4. Push to the branch (`git push origin my-new-feature`)
84
+ 5. Create a new Pull Request
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'bundler/gem_tasks'
@@ -0,0 +1,32 @@
1
+ require 'pew_pew/version'
2
+ require 'relax'
3
+
4
+ module PewPew
5
+ extend Relax::Delegator[:client]
6
+
7
+ autoload :Client, 'pew_pew/client'
8
+ autoload :Config, 'pew_pew/config'
9
+ autoload :Domain, 'pew_pew/domain'
10
+ autoload :Resource, 'pew_pew/resource'
11
+ autoload :Response, 'pew_pew/response'
12
+
13
+ module Resources
14
+ autoload :Bounces, 'pew_pew/resources/bounces'
15
+ autoload :Campaigns, 'pew_pew/resources/campaigns'
16
+ autoload :Complaints, 'pew_pew/resources/complaints'
17
+ autoload :Lists, 'pew_pew/resources/lists'
18
+ autoload :Logs, 'pew_pew/resources/logs'
19
+ autoload :Mailboxes, 'pew_pew/resources/mailboxes'
20
+ autoload :Messages, 'pew_pew/resources/messages'
21
+ autoload :Routes, 'pew_pew/resources/routes'
22
+ autoload :Stats, 'pew_pew/resources/stats'
23
+ autoload :Unsubscribes, 'pew_pew/resources/unsubscribes'
24
+ end
25
+
26
+ # @return a memoized instance of the client
27
+ # @!visibility private
28
+ def self.client
29
+ @client ||= Client.new
30
+ end
31
+ private_class_method :client
32
+ end
@@ -0,0 +1,90 @@
1
+ module PewPew
2
+ class Client
3
+ include Relax::Client
4
+
5
+ # Returns a new client instance and configures its default values.
6
+ def initialize
7
+ config.base_uri = Config::BASE_URI
8
+ config.user_agent = Config::USER_AGENT
9
+ config.extend(Config)
10
+ end
11
+
12
+ # Builds a new instance of the bounces resource for the given domain.
13
+ #
14
+ # @option options [String] :domain the domain name
15
+ # @return <Resources::Bounces> an instance of the bounces resource
16
+ def bounces(options={})
17
+ Resources::Bounces.new(self, options)
18
+ end
19
+
20
+ # Builds a new instance of the campaigns resource for the given domain.
21
+ #
22
+ # @option options [String] :domain the domain name
23
+ # @return <Resources::Campaigns> an instance of the complaints resource
24
+ def campaigns(options={})
25
+ Resources::Campaigns.new(self, options)
26
+ end
27
+
28
+ # Builds a new instance of the complaints resource for the given domain.
29
+ #
30
+ # @option options [String] :domain the domain name
31
+ # @return <Resources::Complaints> an instance of the complaints resource
32
+ def complaints(options={})
33
+ Resources::Complaints.new(self, options)
34
+ end
35
+
36
+ # Builds a new instance of the lists resource.
37
+ #
38
+ # @return <Resources::Lists> an instance of the lists resource
39
+ def lists
40
+ Resources::Lists.new(self)
41
+ end
42
+
43
+ # Builds a new instance of the logs resource for the given domain.
44
+ #
45
+ # @option options [String] :domain the domain name
46
+ # @return <Resources::Logs> an instance of the logs resource
47
+ def logs(options={})
48
+ Resources::Logs.new(self, options)
49
+ end
50
+
51
+ # Builds a new instance of the mailboxes resource for the given domain.
52
+ #
53
+ # @option options [String] :domain the domain name
54
+ # @return <Resources::Mailboxes> an instance of the mailboxes resource
55
+ def mailboxes(options={})
56
+ Resources::Mailboxes.new(self, options)
57
+ end
58
+
59
+ # Builds a new instance of the messages resource for the given domain.
60
+ #
61
+ # @option options [String] :domain the domain name
62
+ # @return <Resources::Messages> an instance of the messages resource
63
+ def messages(options={})
64
+ Resources::Messages.new(self, options)
65
+ end
66
+
67
+ # Builds a new instance of the routes resource.
68
+ #
69
+ # @return <Resources::Routes> an instance of the routes resource
70
+ def routes
71
+ Resources::Routes.new(self)
72
+ end
73
+
74
+ # Builds a new instance of the stats resource for the given domain.
75
+ #
76
+ # @option options [String] :domain the domain name
77
+ # @return <Resources::Stats> an instance of the stats resource
78
+ def stats(options={})
79
+ Resources::Stats.new(self, options)
80
+ end
81
+
82
+ # Builds a new instance of the unsubscribes resource for the given domain.
83
+ #
84
+ # @option options [String] :domain the domain name
85
+ # @return <Resources::Unsubscribes> an instance of the unsubscribes resource
86
+ def unsubscribes(options={})
87
+ Resources::Unsubscribes.new(self, options)
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,15 @@
1
+ module PewPew
2
+ module Config
3
+ BASE_URI = 'https://api.mailgun.net/v2'
4
+ USER_AGENT = "PewPew Ruby Gem #{PewPew::VERSION}"
5
+ USERNAME = 'api'
6
+
7
+ # The Mailgun API key used to authenticate requests.
8
+ # @return [String]
9
+ attr :api_key, true
10
+
11
+ # The default domain to use for resources that require a domain.
12
+ # @return [String]
13
+ attr :domain, true
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ module PewPew
2
+ module Domain
3
+ def domain
4
+ @domain ||= @options[:domain] || config.domain
5
+ end
6
+ private :domain
7
+ end
8
+ private_constant :Domain
9
+ end
@@ -0,0 +1,52 @@
1
+ require 'faraday_middleware'
2
+
3
+ module PewPew
4
+ module Resource
5
+ include Relax::Resource
6
+
7
+ class ResponseDecorator < Faraday::Response::Middleware
8
+ def on_complete(env)
9
+ if env[:body].is_a?(Array)
10
+ items = env[:body]
11
+ env[:body] = Response.new(items: items, total_count: items.length)
12
+ end
13
+
14
+ env[:body].status = env[:status]
15
+ end
16
+ end
17
+
18
+ def get(*)
19
+ super.body
20
+ end
21
+ private :get
22
+
23
+ def post(*)
24
+ super.body
25
+ end
26
+ private :post
27
+
28
+ def put(*)
29
+ super.body
30
+ end
31
+ private :put
32
+
33
+ def delete(*)
34
+ super.body
35
+ end
36
+ private :delete
37
+
38
+ def connection
39
+ super do |builder|
40
+ builder.basic_auth(Config::USERNAME, config.api_key)
41
+
42
+ builder.use(ResponseDecorator)
43
+ builder.response(:mashify, mash_class: Response)
44
+ builder.response(:json)
45
+
46
+ builder.request(:multipart)
47
+ builder.request(:url_encoded)
48
+ end
49
+ end
50
+ private :connection
51
+ end
52
+ end
@@ -0,0 +1,41 @@
1
+ module PewPew
2
+ module Resources
3
+ class Bounces
4
+ include Resource
5
+ include Domain
6
+
7
+ # Fetches all bounces.
8
+ #
9
+ # @return [Mash] the response body
10
+ def all
11
+ get("#{domain}/bounces")
12
+ end
13
+
14
+ # Fetch all bounces by address.
15
+ #
16
+ # @param address [String] the address to fetch bounces for
17
+ # @return [Mash] the response body
18
+ def find(address)
19
+ get("#{domain}/bounces/#{address}")
20
+ end
21
+
22
+ # Creates a new bounce.
23
+ #
24
+ # @option params [String] :address the email address to add
25
+ # @option params [String] :code error code (defaults to 550)
26
+ # @option params [String] :error error description (defaults to blank)
27
+ # @return [Mash] the response body
28
+ def create(params)
29
+ post("#{domain}/bounces", params)
30
+ end
31
+
32
+ # Removes existing bounces.
33
+ #
34
+ # @param address_or_id [String] the address to remove all the bounces for
35
+ # @return [Mash] the response body
36
+ def remove(address_or_id)
37
+ delete("#{domain}/bounces/#{address_or_id}")
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,141 @@
1
+ module PewPew
2
+ module Resources
3
+ class Campaigns
4
+ include Resource
5
+ include Domain
6
+
7
+ # Fetches all campaigns for the domain.
8
+ #
9
+ # @return [Mash] the response body
10
+ def all
11
+ get("#{domain}/campaigns")
12
+ end
13
+
14
+ # Fetch a campaign by ID.
15
+ #
16
+ # @param id [String] the ID of the campaign to be fetched
17
+ # @return [Mash] the response body
18
+ def find(id)
19
+ get("#{domain}/campaigns/#{id}")
20
+ end
21
+
22
+ # Creates a new campaign.
23
+ #
24
+ # @option params [String] :name the name of the campaign
25
+ # @option params [String] :id optional campaign identifier (defaults to
26
+ # an generated ID)
27
+ # @return [Mash] the response body
28
+ def create(params)
29
+ post("#{domain}/campaigns", params)
30
+ end
31
+
32
+ # Removes an existing campaign and all associated data.
33
+ #
34
+ # @param id [String] the ID of the campaign to be deleted
35
+ # @return [Mash] the response body
36
+ def remove(id)
37
+ delete("#{domain}/campaigns/#{id}")
38
+ end
39
+
40
+ # Fetch the events for a campaign, including clicks, opens, unsubscribes,
41
+ # bounces and complaints.
42
+ #
43
+ # @param id [String] a campaign ID
44
+ # @option params [String, Array<String>] :event optional event filter
45
+ # (clicked, opened, unsubscribed, bounced, or complained)
46
+ # @option params [String] :address optional recipient email address
47
+ # @option params [String] :country optional two-letter country code
48
+ # @option params [String] :region optional region name or code
49
+ # @option params [Fixnum] :limit number of records to return (maximum 100)
50
+ # @option params [Fixnum] :page results page number
51
+ # @option params [Boolean] :count if true, return counts instead of
52
+ # records
53
+ # @return [Mash] the response body
54
+ def events(id, params={})
55
+ get("#{domain}/campaigns/#{id}/events", params)
56
+ end
57
+
58
+ # Fetches a summary of the results for a given campaign, like numbers of
59
+ # clicks, opens, etc. Includes unique numbers (e.g. number of unique
60
+ # recipients who clicked) as well.
61
+ #
62
+ # @param id [String] a campaign ID
63
+ # @option params [String] :group_by optional grouping (domain or
64
+ # daily_hour)
65
+ # @return [Mash] the response body
66
+ def stats(id, params={})
67
+ params[:groupby] = params.delete(:group_by) if params[:group_by]
68
+ get("#{domain}/campaigns/#{id}/stats", params)
69
+ end
70
+
71
+ # Fetches clicks aggregated by one or more parameters.
72
+ #
73
+ # @param id [String] a campaign ID
74
+ # @option params [String] :group_by optional grouping (link, recipient,
75
+ # domain, country, region, city, month, day, hour, minute, daily_hour)
76
+ # @option params [String] :country optional two-letter country code
77
+ # @option params [String] :region optional region name or code
78
+ # @option params [String] :city optional city name
79
+ # @option params [Fixnum] :limit number of records to return (maximum 100)
80
+ # @option params [Fixnum] :page results page number
81
+ # @option params [Boolean] :count if true, return counts instead of
82
+ # records
83
+ # @return [Mash] the response body
84
+ def clicks(id, params={})
85
+ params[:groupby] = params.delete(:group_by) if params[:group_by]
86
+ get("#{domain}/campaigns/#{id}/clicks", params)
87
+ end
88
+
89
+ # Fetches opens aggregated by one or more parameters.
90
+ #
91
+ # @param id [String] a campaign ID
92
+ # @option params [String] :group_by optional grouping (recipient, domain,
93
+ # country, region, city, month, day, hour, minute, daily_hour)
94
+ # @option params [String] :country optional two-letter country code
95
+ # @option params [String] :region optional region name or code
96
+ # @option params [String] :city optional city name
97
+ # @option params [Fixnum] :limit number of records to return (maximum 100)
98
+ # @option params [Fixnum] :page results page number
99
+ # @option params [Boolean] :count if true, return counts instead of
100
+ # records
101
+ # @return [Mash] the response body
102
+ def opens(id, params={})
103
+ params[:groupby] = params.delete(:group_by) if params[:group_by]
104
+ get("#{domain}/campaigns/#{id}/opens", params)
105
+ end
106
+
107
+ # Fetches unsubscribes aggregated by one or more parameters.
108
+ #
109
+ # @param id [String] a campaign ID
110
+ # @option params [String] :group_by optional grouping (domain, country,
111
+ # region, city, month, day, hour, minute, daily_hour)
112
+ # @option params [String] :country optional two-letter country code
113
+ # @option params [String] :region optional region name or code
114
+ # @option params [String] :city optional city name
115
+ # @option params [Fixnum] :limit number of records to return (maximum 100)
116
+ # @option params [Fixnum] :page results page number
117
+ # @option params [Boolean] :count if true, return counts instead of
118
+ # records
119
+ # @return [Mash] the response body
120
+ def unsubscribes(id, params={})
121
+ params[:groupby] = params.delete(:group_by) if params[:group_by]
122
+ get("#{domain}/campaigns/#{id}/unsubscribes", params)
123
+ end
124
+
125
+ # Fetches complaints aggregated by one or more parameters.
126
+ #
127
+ # @param id [String] a campaign ID
128
+ # @option params [String] :group_by optional grouping (recipient, domain,
129
+ # month, day, hour, minute, daily_hour)
130
+ # @option params [Fixnum] :limit number of records to return (maximum 100)
131
+ # @option params [Fixnum] :page results page number
132
+ # @option params [Boolean] :count if true, return counts instead of
133
+ # records
134
+ # @return [Mash] the response body
135
+ def complaints(id, params={})
136
+ params[:groupby] = params.delete(:group_by) if params[:group_by]
137
+ get("#{domain}/campaigns/#{id}/complaints", params)
138
+ end
139
+ end
140
+ end
141
+ end