├── .ebert.yml
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── cindy.gemspec
├── lib
├── cindy.rb
├── cindy
│ ├── client.rb
│ ├── error.rb
│ └── version.rb
└── faraday
│ └── response
│ └── raise_cindy_error.rb
└── spec
├── cindy_spec.rb
└── spec_helper.rb
/.ebert.yml:
--------------------------------------------------------------------------------
1 | # This configuration was used Ebert to review the polydice/cindy repository
2 | # on bff7628dbc71fee9a5e3313733576ad1aa817992.
3 | # You can make this the default configuration for future reviews by moving this
4 | # file to your repository as `.ebert.yml` and pushing it to GitHub, and tweak
5 | # it as you wish - To know more on how to change this file to better review your
6 | # repository you can go to https://ebertapp.io/docs/config and see the configuration
7 | # details.
8 | ---
9 | styleguide: plataformatec/linters
10 | engines:
11 | reek:
12 | enabled: true
13 | fixme:
14 | enabled: true
15 | rubocop:
16 | enabled: true
17 | duplication:
18 | config:
19 | languages:
20 | - ruby
21 | enabled: true
22 | remark-lint:
23 | enabled: true
24 | exclude_paths:
25 | - spec
26 |
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | .bundle
4 | .config
5 | .yardoc
6 | Gemfile.lock
7 | InstalledFiles
8 | _yardoc
9 | coverage
10 | doc/
11 | lib/bundler/man
12 | pkg
13 | rdoc
14 | spec/reports
15 | test/tmp
16 | test/version_tmp
17 | tmp
18 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | sudo: false
3 | cache: bundler
4 | rvm:
5 | - 2.3.3
6 | - 2.4.1
7 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.2.1
2 |
3 | * Update dependencies [Idris Y](https://github.com/sld) [#4](https://github.com/polydice/cindy/pull/4)
4 |
5 | ## 0.2.0
6 |
7 | * Add create campaigns API [jonah honeyman](https://github.com/jonuts) [#3](https://github.com/polydice/cindy/pull/3)
8 |
9 | ## 0.1.1
10 |
11 | * Fix URL path issues when Sendy installed in a folder
12 |
13 | ## 0.1.0
14 |
15 | * First release
16 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # Specify your gem's dependencies in cindy.gemspec
4 | gemspec
5 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014-2015 Polydice, Inc. http://polydice.com
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.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cindy
2 |
3 | A lightweight and flexible Ruby SDK for [Sendy](http://sendy.co), a self-hosted email newsletter app.
4 |
5 | ## Usage
6 |
7 | The API of Cindy was basically implemented after [Sendy's API documentation](http://sendy.co/api).
8 |
9 | ### Client
10 |
11 | To use Cindy, first create a client instance:
12 |
13 | ```ruby
14 | c = Cindy.new "http://sendy.co/demo/", "QywLZqDddP2P//d6ntekf+GY82nLrHke"
15 | ```
16 |
17 | There're two parameters for initialize method:
18 |
19 | 1. API Endpoint - The URL for Sendy installation.
20 | 2. API Key - Optional, only for subscription status methods.
21 |
22 | ### Subscribe / Unsubscribe
23 |
24 | Then you can subscribe or unsubscribe from a list:
25 |
26 | ```ruby
27 | > c.subscribe list_id, "foo@bar.com", "My Name"
28 | => true
29 | > c.unsubscribe list_id, "foo@bar.com"
30 | => false
31 | ```
32 |
33 | ### Subscription Status
34 |
35 | To check subscription status for Email address:
36 |
37 | ```ruby
38 | > c.subscription_status list_id, "foo@bar.com"
39 | => "Unsubscribed"
40 | ```
41 |
42 | ### Active Subscriber Count
43 |
44 | To get active subscriber count of a list:
45 |
46 | ```ruby
47 | > c.active_subscriber_count list_id
48 | => 1660
49 | ```
50 |
51 | ### Create Campaign
52 |
53 | To create new campaign:
54 |
55 | ```ruby
56 | > c.create_campaign from_name: "foo", from_email: "foo@bar.com", reply_to: "foo@bar.com", subject: "Hello, world", html_text: "
Hello, world
"
57 | => Campaign created
58 | ```
59 |
60 | ### More Reference
61 |
62 | Check [Sendy's API documentation](http://sendy.co/api) to learn more about parameters and possible responses.
63 |
64 | ## License
65 |
66 | MIT License. Copyright 2013-2015 Polydice, Inc. http://polydice.com/
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/gem_tasks"
2 | require 'rspec/core/rake_task'
3 |
4 | RSpec::Core::RakeTask.new(:spec)
5 |
6 | task :default => :spec
7 |
--------------------------------------------------------------------------------
/cindy.gemspec:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | lib = File.expand_path('../lib', __FILE__)
3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4 | require 'cindy/version'
5 |
6 | Gem::Specification.new do |gem|
7 | gem.name = "cindy"
8 | gem.version = Cindy::VERSION
9 | gem.authors = ["Richard Lee"]
10 | gem.email = ["rl@polydice.com"]
11 | gem.description = %q{A lightweight and flexible Ruby SDK for Sendy, a self-hosted email newsletter app.}
12 | gem.summary = %q{Simple Ruby wrapper for Sendy API.}
13 | gem.homepage = "https://github.com/polydice/cindy"
14 |
15 | gem.files = `git ls-files`.split($/)
16 | gem.test_files = gem.files.grep(%r{^spec/})
17 | gem.require_paths = ["lib"]
18 |
19 | gem.add_dependency("faraday", "~> 0.9")
20 | gem.add_dependency('faraday_middleware', '~> 0.10')
21 | gem.add_dependency('hashie', '~> 3.4')
22 |
23 | gem.add_development_dependency("rake", "~> 10.4.2")
24 | gem.add_development_dependency("rspec", "~> 2.12.0")
25 | end
26 |
--------------------------------------------------------------------------------
/lib/cindy.rb:
--------------------------------------------------------------------------------
1 | require "cindy/version"
2 | require "cindy/client"
3 | require "cindy/error"
4 |
5 | module Cindy
6 |
7 | class << self
8 |
9 | def new(sendy_url, api_key = nil)
10 | # Alias method for Cindy::Client
11 | Cindy::Client.new(sendy_url, api_key)
12 | end
13 |
14 | end
15 |
16 | end
17 |
--------------------------------------------------------------------------------
/lib/cindy/client.rb:
--------------------------------------------------------------------------------
1 | require 'faraday'
2 | require 'faraday_middleware'
3 | require 'faraday/response/raise_cindy_error'
4 |
5 | module Cindy
6 | class Client
7 |
8 | def initialize(sendy_url, api_key = nil)
9 | @url = sendy_url
10 | @key = api_key || ENV['SENDY_API_KEY']
11 | end
12 |
13 | def create_campaign(opts={})
14 | post_opts = {}
15 | req_opts = %i(from_name from_email reply_to subject html_text)
16 | optional_opts = %i(plain_text list_ids brand_id send_campaign)
17 |
18 | req_opts.each do |opt|
19 | post_opts[opt] = opts.delete(opt) || raise(ArgumentError, "opt :#{opt} required")
20 | end
21 | post_opts.merge!(Hash[optional_opts.zip(opts.values_at(*optional_opts))])
22 | post_opts[:api_key] = @key
23 |
24 | response = connection.post "api/campaigns/create.php" do |req|
25 | req.body = post_opts
26 | end
27 |
28 | response.body
29 | end
30 |
31 | def subscribe(list_id, email, name = nil)
32 | response = connection.post "subscribe" do |req|
33 | params = {list: list_id, email: email, boolean: true}
34 | params[:name] = name if name
35 | req.body = params
36 | end
37 |
38 | !!(response.body =~ /^1$/)
39 | end
40 |
41 | def unsubscribe(list_id, email)
42 | response = connection.post "unsubscribe", {list: list_id, email: email, boolean: true}
43 |
44 | !!(response.body =~ /^1$/)
45 | end
46 |
47 | def subscription_status(list_id, email)
48 | response = connection.post "api/subscribers/subscription-status.php" do |req|
49 | req.body = {list_id: list_id, email: email, api_key: @key}
50 | end
51 |
52 | response.body
53 | end
54 |
55 | def active_subscriber_count(list_id)
56 | response = connection.post "api/subscribers/active-subscriber-count.php" do |req|
57 | req.body = {list_id: list_id, api_key: @key}
58 | end
59 |
60 | response.body.to_i
61 | end
62 |
63 | protected
64 |
65 | def connection
66 | @connection ||= Faraday.new(:url => @url) do |faraday|
67 | faraday.request :url_encoded
68 | faraday.adapter Faraday.default_adapter
69 |
70 | faraday.use ::Faraday::Response::RaiseCindyError
71 | faraday.use ::FaradayMiddleware::FollowRedirects
72 | faraday.use ::FaradayMiddleware::Mashify
73 | end
74 | end
75 |
76 | end
77 | end
78 |
--------------------------------------------------------------------------------
/lib/cindy/error.rb:
--------------------------------------------------------------------------------
1 | module Cindy
2 | # Generic error
3 | class Error < StandardError; end
4 |
5 | class NoDataPasssed < Error; end
6 |
7 | class APIKeyNotPassed < Error; end
8 |
9 | class InvalidAPIKey < Error; end
10 |
11 | class EmailNotPassed < Error; end
12 |
13 | class ListIDNotPassed < Error; end
14 |
15 | class ListDoesNotExist < Error; end
16 |
17 | class EmailDoesNotExistInList < Error; end
18 |
19 | class SomeFieldsAreMissing < Error; end
20 |
21 | class InvalidEmailAddress < Error; end
22 |
23 | class AlreadySubscribed < Error; end
24 |
25 | # Raised when Sendy returns a 400 HTTP status code
26 | class BadRequest < Error; end
27 |
28 | # Raised when Sendy returns a 401 HTTP status code
29 | class Unauthorized < Error; end
30 |
31 | # Raised when Sendy returns a 403 HTTP status code
32 | class Forbidden < Error; end
33 |
34 | # Raised when Sendy returns a 404 HTTP status code
35 | class NotFound < Error; end
36 |
37 | # Raised when Sendy returns a 406 HTTP status code
38 | class NotAcceptable < Error; end
39 |
40 | # Raised when Sendy returns a 422 HTTP status code
41 | class UnprocessableEntity < Error; end
42 |
43 | # Raised when Sendy returns a 500 HTTP status code
44 | class InternalServerError < Error; end
45 |
46 | # Raised when Sendy returns a 501 HTTP status code
47 | class NotImplemented < Error; end
48 |
49 | # Raised when Sendy returns a 502 HTTP status code
50 | class BadGateway < Error; end
51 |
52 | # Raised when Sendy returns a 503 HTTP status code
53 | class ServiceUnavailable < Error; end
54 | end
55 |
--------------------------------------------------------------------------------
/lib/cindy/version.rb:
--------------------------------------------------------------------------------
1 | module Cindy
2 | VERSION = "0.2.1"
3 | end
4 |
--------------------------------------------------------------------------------
/lib/faraday/response/raise_cindy_error.rb:
--------------------------------------------------------------------------------
1 | require 'faraday'
2 | require 'cindy/error'
3 |
4 | module Faraday
5 | class Response::RaiseCindyError < Response::Middleware
6 |
7 | def on_complete(response)
8 | case response[:body]
9 | when /No data passed/i
10 | when /Already subscribed./i
11 | raise ::Cindy::AlreadySubscribed
12 | when /Invalid email address./i
13 | raise ::Cindy::InvalidEmailAddress
14 | end
15 |
16 | # key = response[:status].to_i
17 | # raise ERROR_MAP[key].new(response) if ERROR_MAP.has_key? key
18 | end
19 |
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/cindy_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Cindy do
4 |
5 | describe ".new" do
6 |
7 | it "is a Cindy::Client" do
8 | expect(Cindy.new("http://foo.com/")).to be_a Cindy::Client
9 | end
10 |
11 | end
12 |
13 | end
14 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'rspec'
2 | require 'cindy'
3 |
4 | RSpec.configure do |config|
5 | config.color_enabled = true
6 | config.formatter = 'documentation'
7 | end
8 |
--------------------------------------------------------------------------------