pew_pew 0.0.1

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.
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