├── .env_sample
├── twilio_sendgrid_logo.png
├── static
└── img
│ ├── github-fork.png
│ └── github-sign-up.png
├── test
├── test_helper.rb
└── test.rb
├── Gemfile
├── lib
├── smtpapi
│ └── version.rb
└── smtpapi.rb
├── Makefile
├── USAGE.md
├── Rakefile
├── .gitignore
├── use_cases
└── README.md
├── .github
└── workflows
│ ├── pr-lint.yml
│ └── test-and-deploy.yml
├── TROUBLESHOOTING.md
├── .rubocop.yml
├── smtpapi.gemspec
├── LICENSE
├── PULL_REQUEST_TEMPLATE.md
├── examples
└── example.rb
├── .rubocop_todo.yml
├── FIRST_TIMERS.md
├── README.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
└── CHANGELOG.md
/.env_sample:
--------------------------------------------------------------------------------
1 | SENDGRID_API_KEY=
--------------------------------------------------------------------------------
/twilio_sendgrid_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sendgrid/smtpapi-ruby/HEAD/twilio_sendgrid_logo.png
--------------------------------------------------------------------------------
/static/img/github-fork.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sendgrid/smtpapi-ruby/HEAD/static/img/github-fork.png
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | if RUBY_VERSION.equal?('2.7')
2 | require 'simplecov'
3 | SimpleCov.start
4 | end
5 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # Specify your gem's dependencies in smtpapi.gemspec
4 | gemspec
5 |
--------------------------------------------------------------------------------
/lib/smtpapi/version.rb:
--------------------------------------------------------------------------------
1 | #
2 | # SendGrid smtpapi
3 | #
4 | module Smtpapi
5 | VERSION = '0.1.12'.freeze
6 | end
7 |
--------------------------------------------------------------------------------
/static/img/github-sign-up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sendgrid/smtpapi-ruby/HEAD/static/img/github-sign-up.png
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: install test
2 |
3 | install:
4 | gem install bundler:1.14.6; bundle install
5 |
6 | test:
7 | rake test
8 |
--------------------------------------------------------------------------------
/USAGE.md:
--------------------------------------------------------------------------------
1 | # Usage
2 |
3 | - [SendGrid Docs](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html)
4 | - [Example Code](examples)
5 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'bundler/gem_tasks'
2 | require 'rake/testtask'
3 |
4 | Rake::TestTask.new do |t|
5 | t.libs << 'test'
6 | t.test_files = FileList['test/test*.rb']
7 | t.verbose = true
8 | end
9 |
--------------------------------------------------------------------------------
/.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 | .env
--------------------------------------------------------------------------------
/use_cases/README.md:
--------------------------------------------------------------------------------
1 | This directory provides examples for specific use cases. Please [open an issue](https://github.com/sendgrid/smtpapi-ruby/issues) or make a pull request for any use cases you would like us to document here. Thank you!
2 |
3 | # Table of Contents
4 |
--------------------------------------------------------------------------------
/.github/workflows/pr-lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint PR
2 | on:
3 | pull_request_target:
4 | types: [ opened, edited, synchronize, reopened ]
5 |
6 | jobs:
7 | validate:
8 | name: Validate title
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: amannn/action-semantic-pull-request@v4
12 | with:
13 | types: chore docs fix feat test misc
14 | env:
15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
16 |
--------------------------------------------------------------------------------
/TROUBLESHOOTING.md:
--------------------------------------------------------------------------------
1 | If you have a SendGrid issue, please contact our [support team](https://support.sendgrid.com).
2 |
3 | ## Table of Contents
4 | * [Viewing the Request Header](#request-header)
5 |
6 |
7 | ## Viewing the Request Header
8 |
9 | When debugging or testing, it may be useful to examine the raw request header to compare against the [documented format](https://sendgrid.com/docs/API_Reference/api_v3.html).
10 |
11 | You can do this like so:
12 |
13 | ```ruby
14 | puts header
15 | ```
16 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | inherit_from: .rubocop_todo.yml
2 |
3 | # Allow "set_" and "get_" for prefix of method
4 | AccessorMethodName:
5 | Enabled: false
6 |
7 | # Increase max line number of method
8 | MethodLength:
9 | CountComments: true # count full line comments?
10 | Max: 20
11 |
12 | # Increase the number of params
13 | ParameterLists:
14 | Max: 6
15 | CountKeywordArgs: true
16 |
17 | # Increase the max AbcSize
18 | AbcSize:
19 | Max: 34
20 |
21 | # Increase the max line number of class
22 | ClassLength:
23 | Max: 250
24 | CountComments: true
25 |
26 | CyclomaticComplexity:
27 | Max: 12
28 |
29 | PerceivedComplexity:
30 | Max: 12
31 |
32 | Metrics/LineLength:
33 | Max: 100
--------------------------------------------------------------------------------
/smtpapi.gemspec:
--------------------------------------------------------------------------------
1 | lib = File.expand_path('lib', __dir__)
2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3 | require 'smtpapi/version'
4 |
5 | Gem::Specification.new do |spec|
6 | spec.name = 'smtpapi'
7 | spec.version = Smtpapi::VERSION
8 | spec.authors = ['Wataru Sato', 'SendGrid']
9 | spec.email = ['awwa500@gmail.com', 'community@sendgrid.com']
10 | spec.summary = 'Smtpapi library for SendGrid.'
11 | spec.description = 'Smtpapi library for SendGrid.'
12 | spec.homepage = 'https://github.com/sendgrid/smtpapi-ruby'
13 | spec.license = 'MIT'
14 |
15 | spec.files = `git ls-files -z`.split("\x0")
16 | spec.executables = spec.files.grep(/^bin/) { |f| File.basename(f) }
17 | spec.test_files = spec.files.grep(/^(test|spec|features)/)
18 | spec.require_paths = ['lib']
19 |
20 | spec.add_development_dependency 'rake'
21 | spec.add_development_dependency('test-unit', '~> 3.0')
22 | spec.add_development_dependency('simplecov', '~> 0.18.5')
23 | end
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (C) 2023, Twilio SendGrid, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 | of the Software, and to permit persons to whom the Software is furnished to do
10 | so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
16 |
17 | # Fixes #
18 |
19 | A short description of what this PR does.
20 |
21 | ### Checklist
22 | - [x] I acknowledge that all my contributions will be made under the project's license
23 | - [ ] I have made a material change to the repo (functionality, testing, spelling, grammar)
24 | - [ ] I have read the [Contribution Guidelines](https://github.com/sendgrid/smtpapi-ruby/blob/main/CONTRIBUTING.md) and my PR follows them
25 | - [ ] I have titled the PR appropriately
26 | - [ ] I have updated my branch with the main branch
27 | - [ ] I have added tests that prove my fix is effective or that my feature works
28 | - [ ] I have added the necessary documentation about the functionality in the appropriate .md file
29 | - [ ] I have added inline documentation to the code I modified
30 |
31 | If you have questions, please file a [support ticket](https://support.sendgrid.com), or create a GitHub Issue in this repository.
32 |
--------------------------------------------------------------------------------
/examples/example.rb:
--------------------------------------------------------------------------------
1 | require_relative '../lib/smtpapi'
2 | header = Smtpapi::Header.new
3 |
4 | ## add_to
5 | header.add_to('test1@example.com')
6 | header.add_to('test2@example.com', 'other')
7 | # header.add_to(['test1@example.com', 'test2@example.com'])
8 |
9 | ## set_tos
10 | # header.set_tos(['you@youremail.com', 'other@otheremail.com'])
11 | # to => ['you@youremail.com', 'other@otheremail.com']
12 |
13 | ## add_substitution
14 | # sub = {keep: ['secret']}
15 | header.add_substitution('keep', ['secret'])
16 | # sub = {keep: ['secret'], other: ['one', 'two']}
17 | header.add_substitution('other', %w(one two))
18 |
19 | ## set_substitutions
20 | # header.set_substitutions({'keep' => 'secret'}) # sub = {keep: ['secret']}
21 |
22 | ## add_unique_arg
23 | header.add_unique_arg('cat', 'dogs')
24 |
25 | ## set_unique_args
26 | # header.set_unique_args({'cow' => 'chicken'})
27 | # header.set_unique_args({'dad' => 'proud'})
28 |
29 | ## add_category
30 | # category = ['tactics']
31 | header.add_category('tactics')
32 | # category = ['tactics', 'advanced']
33 | header.add_category('advanced')
34 |
35 | ## set_categories
36 | # category = ['tactics', 'advanced']
37 | # header.set_categories(['tactics', 'advanced'])
38 |
39 | ## add_section
40 | header.add_section('-charge-', 'This ship is useless.')
41 | header.add_section('-bomber-', 'Only for sad vikings.')
42 |
43 | ## set_sections
44 | # header.set_sections({'-charge-' => 'This ship is useless.'})
45 |
46 | ## add_filter
47 | header.add_filter('footer', 'enable', 1)
48 | header.add_filter('footer', 'text/html', 'boo')
49 |
50 | ## set_filters
51 | # filter = {
52 | # 'footer' => {
53 | # 'setting' => {
54 | # 'enable' => 1,
55 | # "text/plain" => 'You can haz footers!'
56 | # }
57 | # }
58 | # }
59 | # header.set_filters(filter)
60 |
61 | ## set_send_at
62 | lt = Time.local(2014, 8, 29, 17, 56, 35)
63 | header.set_send_at(lt)
64 |
65 | ## set_send_each_at
66 | # lt1 = Time.local(2014, 8, 29, 17, 56, 35)
67 | # lt2 = Time.local(2013, 12, 31, 0, 0, 0)
68 | # lt3 = Time.local(2015, 9, 1, 4, 5, 6)
69 | # header.set_send_each_at([lt1, lt2, lt3])
70 |
71 | ## asm_group_id
72 | # This is to specify an ASM Group for the message.
73 | # See: https://sendgrid.com/docs/User_Guide/advanced_suppression_manager.html
74 | header.set_asm_group(2)
75 |
76 | ## set_ip_pool
77 | # Using IP Pools with the SMTP API Header
78 | # See: https://sendgrid.com/docs/API_Reference/Web_API_v3/IP_Management/ip_pools.html)
79 | header.set_ip_pool('test_pool')
80 |
81 | print header.to_json
82 |
--------------------------------------------------------------------------------
/.rubocop_todo.yml:
--------------------------------------------------------------------------------
1 | # This configuration was generated by
2 | # `rubocop --auto-gen-config`
3 | # on 2022-01-25 23:22:53 UTC using RuboCop version 1.22.2.
4 | # The point is for the user to remove these configuration records
5 | # one by one as the offenses are removed from the code base.
6 | # Note that changes in the inspected code, or installation of new
7 | # versions of RuboCop, may require this file to be generated again.
8 |
9 | # Offense count: 1
10 | # Cop supports --auto-correct.
11 | # Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include.
12 | # Include: **/*.gemspec
13 | Gemspec/OrderedDependencies:
14 | Exclude:
15 | - 'smtpapi.gemspec'
16 |
17 | # Offense count: 1
18 | # Configuration parameters: Include.
19 | # Include: **/*.gemspec
20 | Gemspec/RequiredRubyVersion:
21 | Exclude:
22 | - 'smtpapi.gemspec'
23 |
24 | # Offense count: 1
25 | # Cop supports --auto-correct.
26 | # Configuration parameters: EnforcedStyle.
27 | # SupportedStyles: final_newline, final_blank_line
28 | Layout/TrailingEmptyLines:
29 | Exclude:
30 | - 'Gemfile'
31 |
32 | # Offense count: 1
33 | # Configuration parameters: IgnoredMethods.
34 | Metrics/CyclomaticComplexity:
35 | Max: 12
36 |
37 | # Offense count: 1
38 | # Configuration parameters: IgnoredMethods.
39 | Metrics/PerceivedComplexity:
40 | Max: 12
41 |
42 | # Offense count: 2
43 | # Cop supports --auto-correct.
44 | # Configuration parameters: EnforcedStyle.
45 | # SupportedStyles: separated, grouped
46 | Style/AccessorGrouping:
47 | Exclude:
48 | - 'lib/smtpapi.rb'
49 |
50 | # Offense count: 1
51 | # Cop supports --auto-correct.
52 | # Configuration parameters: EnforcedStyle.
53 | # SupportedStyles: prefer_alias, prefer_alias_method
54 | Style/Alias:
55 | Exclude:
56 | - 'lib/smtpapi.rb'
57 |
58 | # Offense count: 8
59 | # Cop supports --auto-correct.
60 | # Configuration parameters: EnforcedStyle.
61 | # SupportedStyles: always, always_true, never
62 | Style/FrozenStringLiteralComment:
63 | Exclude:
64 | - 'Gemfile'
65 | - 'Rakefile'
66 | - 'examples/example.rb'
67 | - 'lib/smtpapi.rb'
68 | - 'lib/smtpapi/version.rb'
69 | - 'smtpapi.gemspec'
70 | - 'test/test.rb'
71 | - 'test/test_helper.rb'
72 |
73 | # Offense count: 1
74 | # Cop supports --auto-correct.
75 | # Configuration parameters: UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
76 | # SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
77 | Style/HashSyntax:
78 | EnforcedStyle: hash_rockets
79 |
80 | # Offense count: 3
81 | # Cop supports --auto-correct.
82 | # Configuration parameters: PreferredDelimiters.
83 | Style/PercentLiteralDelimiters:
84 | Exclude:
85 | - 'examples/example.rb'
86 | - 'test/test.rb'
87 |
88 | # Offense count: 2
89 | # Cop supports --auto-correct.
90 | # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
91 | # SupportedStyles: single_quotes, double_quotes
92 | Style/StringLiterals:
93 | Exclude:
94 | - 'Gemfile'
95 |
--------------------------------------------------------------------------------
/FIRST_TIMERS.md:
--------------------------------------------------------------------------------
1 | # How To Contribute to Twilio SendGrid Repositories via GitHub
2 | Contributing to the Twilio SendGrid repositories is easy! All you need to do is find an open issue (see the bottom of this page for a list of repositories containing open issues), fix it and submit a pull request. Once you have submitted your pull request, the team can easily review it before it is merged into the repository.
3 |
4 | To make a pull request, follow these steps:
5 |
6 | 1. Log into GitHub. If you do not already have a GitHub account, you will have to create one in order to submit a change. Click the Sign up link in the upper right-hand corner to create an account. Enter your username, password, and email address. If you are an employee of Twilio SendGrid, please use your full name with your GitHub account and enter Twilio SendGrid as your company so we can easily identify you.
7 |
8 |
9 |
10 | 2. __[Fork](https://help.github.com/fork-a-repo/)__ the [smtpapi-php](https://github.com/sendgrid/smtpapi-php) repository:
11 |
12 |
13 |
14 | 3. __Clone__ your fork via the following commands:
15 |
16 | ```bash
17 | # Clone your fork of the repo into the current directory
18 | git clone https://github.com/your_username/smtpapi-php
19 | # Navigate to the newly cloned directory
20 | cd smtpapi-php
21 | # Assign the original repo to a remote called "upstream"
22 | git remote add upstream https://github.com/sendgrid/smtpapi-php
23 | ```
24 |
25 | > Don't forget to replace *your_username* in the URL by your real GitHub username.
26 |
27 | 4. __Create a new topic branch__ (off the main project development branch) to contain your feature, change, or fix:
28 |
29 | ```bash
30 | git checkout -b
31 | ```
32 |
33 | 5. __Commit your changes__ in logical chunks.
34 |
35 | Please adhere to these [git commit message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) or your code is unlikely be merged into the main project. Use Git's [interactive rebase](https://help.github.com/articles/interactive-rebase) feature to tidy up your commits before making them public. Probably you will also have to create tests (if needed) or create or update the example code that demonstrates the functionality of this change to the code.
36 |
37 | 6. __Locally merge (or rebase)__ the upstream development branch into your topic branch:
38 |
39 | ```bash
40 | git pull [--rebase] upstream main
41 | ```
42 |
43 | 7. __Push__ your topic branch up to your fork:
44 |
45 | ```bash
46 | git push origin
47 | ```
48 |
49 | 8. __[Open a Pull Request](https://help.github.com/articles/creating-a-pull-request/#changing-the-branch-range-and-destination-repository/)__ with a clear title and description against the `main` branch. All tests must be passing before we will review the PR.
50 |
51 | ## Important notice
52 |
53 | Before creating a pull request, make sure that you respect the repository's constraints regarding contributions. You can find them in the [CONTRIBUTING.md](CONTRIBUTING.md) file.
54 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](https://github.com/sendgrid/smtpapi-ruby/actions/workflows/test-and-deploy.yml)
4 | [](https://badge.fury.io/rb/smtpapi)
5 | [](LICENSE)
6 | [](https://twitter.com/sendgrid)
7 | [](https://github.com/sendgrid/smtpapi-ruby/graphs/contributors)
8 |
9 | **This ruby gem allows you to quickly and more easily generate SendGrid X-SMTPAPI headers.**
10 |
11 | # Announcements
12 | All updates to this library is documented in our [CHANGELOG](CHANGELOG.md).
13 |
14 | # Table of Contents
15 | - [Installation](#installation)
16 | - [Quick Start](#quick-start)
17 | - [Usage](#usage)
18 | - [How to Contribute](#contribute)
19 | - [About](#about)
20 | - [Support](#support)
21 | - [License](#license)
22 |
23 |
24 | # Installation
25 |
26 | ## Prerequisites
27 |
28 | - Ruby version >= 2.4
29 | - The SendGrid service, starting at the [free level](https://sendgrid.com/free?source=smtpapi-ruby)
30 |
31 | ## Install Package
32 |
33 | Add this line to your application's Gemfile:
34 |
35 | ```bash
36 | gem 'smtpapi'
37 | ```
38 |
39 | And then execute:
40 |
41 | ```bash
42 | bundle
43 | ```
44 |
45 | Or install it yourself as:
46 |
47 | ```bash
48 | gem install smtpapi
49 | ```
50 |
51 | ## Setup Environment Variables
52 |
53 | ### Environment Variable
54 |
55 | Update the development environment with your [SENDGRID_API_KEY](https://app.sendgrid.com/settings/api_keys), for example:
56 |
57 | ```bash
58 | echo "export SENDGRID_API_KEY='YOUR_API_KEY'" > sendgrid.env
59 | echo "sendgrid.env" >> .gitignore
60 | source ./sendgrid.env
61 | ```
62 |
63 |
64 | # Quick Start
65 |
66 | ```ruby
67 | header = Smtpapi::Header.new
68 | header.add_to('test@example.com')
69 | print header.to_json
70 | ```
71 |
72 |
73 | # Usage
74 |
75 | - [SendGrid Docs](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html)
76 | - [Example Code](examples)
77 |
78 |
79 | # How to Contribute
80 |
81 | We encourage contribution to our libraries, please see our [CONTRIBUTING](CONTRIBUTING.md) guide for details.
82 |
83 | Quick links:
84 |
85 | - [Improvements to the Codebase](CONTRIBUTING.md#improvements_to_the_codebase)
86 | - [Review Pull Requests](CONTRIBUTING.md#code-reviews)
87 |
88 | ## Credits
89 |
90 | This library was created by [Wataru Sato](https://github.com/awwa) and is now maintained by SendGrid.
91 |
92 |
93 | # About
94 |
95 | smtpapi-ruby is maintained and funded by Twilio SendGrid, Inc. The names and logos for smtpapi-ruby are trademarks of Twilio SendGrid, Inc.
96 |
97 |
98 | # Support
99 |
100 | If you need with SendGrid, please check the [Twilio SendGrid Support Help Center](https://support.sendgrid.com).
101 |
102 |
103 | # License
104 |
105 | [The MIT License (MIT)](LICENSE)
106 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | - Using welcoming and inclusive language
18 | - Being respectful of differing viewpoints and experiences
19 | - Gracefully accepting constructive criticism
20 | - Focusing on what is best for the community
21 | - Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | - The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | - Trolling, insulting/derogatory comments, and personal or political attacks
28 | - Public or private harassment
29 | - Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | - Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at open-source@twilio.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
--------------------------------------------------------------------------------
/.github/workflows/test-and-deploy.yml:
--------------------------------------------------------------------------------
1 | name: Test and Deploy
2 | on:
3 | push:
4 | branches: [ '*' ]
5 | tags: [ '*' ]
6 | pull_request:
7 | branches: [ main ]
8 | schedule:
9 | # Run automatically at 8AM PST Monday-Friday
10 | - cron: '0 15 * * 1-5'
11 | workflow_dispatch:
12 |
13 | jobs:
14 | test:
15 | name: Test
16 | runs-on: ubuntu-latest
17 | timeout-minutes: 20
18 | strategy:
19 | matrix:
20 | ruby: [ '2.4', '2.5', '2.6', '2.7', '3.0', '3.1', 'jruby-9.2' ]
21 | steps:
22 | - name: Checkout smtpapi-ruby
23 | uses: actions/checkout@v2
24 | with:
25 | fetch-depth: 0
26 |
27 | - name: Set up Ruby
28 | uses: ruby/setup-ruby@v1
29 | with:
30 | ruby-version: ${{ matrix.ruby }}
31 | bundler-cache: true
32 |
33 | - run: make install
34 |
35 | - name: Set up linter
36 | run: bundle add rubocop --version "~> 1.24.1" --group "development" --skip-install
37 | if: ${{ matrix.ruby != '2.4' }}
38 |
39 | - run: bundle install --with development && bundle exec rake install
40 |
41 | - name: Run linter
42 | run: bundle exec rubocop
43 | if: ${{ matrix.ruby != '2.4' }}
44 |
45 | - name: Run tests
46 | run: make test
47 |
48 | deploy:
49 | name: Deploy
50 | if: success() && github.ref_type == 'tag'
51 | needs: [ test ]
52 | runs-on: ubuntu-latest
53 | steps:
54 | - name: Checkout smtpapi-ruby
55 | uses: actions/checkout@v2
56 | with:
57 | fetch-depth: 0
58 |
59 | - name: Set up Ruby
60 | uses: ruby/setup-ruby@v1
61 | with:
62 | ruby-version: 3.1
63 | bundler-cache: true
64 |
65 | - run: make install
66 |
67 | - name: Create GitHub Release
68 | uses: sendgrid/dx-automator/actions/release@main
69 | env:
70 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
71 |
72 | - name: Publish to Rubygems
73 | env:
74 | GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_AUTH_TOKEN }}
75 | run: |
76 | mkdir -p $HOME/.gem
77 | touch $HOME/.gem/credentials
78 | chmod 0600 $HOME/.gem/credentials
79 | printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
80 | gem build *.gemspec
81 | gem push *.gem
82 |
83 | - name: Submit metric to Datadog
84 | uses: sendgrid/dx-automator/actions/datadog-release-metric@main
85 | env:
86 | DD_API_KEY: ${{ secrets.DATADOG_API_KEY }}
87 |
88 | notify-on-failure:
89 | name: Slack notify on failure
90 | if: failure() && github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || github.ref_type == 'tag')
91 | needs: [ test, deploy ]
92 | runs-on: ubuntu-latest
93 | steps:
94 | - uses: rtCamp/action-slack-notify@v2
95 | env:
96 | SLACK_COLOR: failure
97 | SLACK_ICON_EMOJI: ':github:'
98 | SLACK_MESSAGE: ${{ format('Test *{0}*, Deploy *{1}*, {2}/{3}/actions/runs/{4}', needs.test.result, needs.deploy.result, github.server_url, github.repository, github.run_id) }}
99 | SLACK_TITLE: Action Failure - ${{ github.repository }}
100 | SLACK_USERNAME: GitHub Actions
101 | SLACK_MSG_AUTHOR: twilio-dx
102 | SLACK_FOOTER: Posted automatically using GitHub Actions
103 | SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
104 | MSG_MINIMAL: true
105 |
--------------------------------------------------------------------------------
/lib/smtpapi.rb:
--------------------------------------------------------------------------------
1 | $LOAD_PATH.unshift File.dirname(__FILE__)
2 | require 'smtpapi/version'
3 | require 'json'
4 |
5 | module Smtpapi
6 | #
7 | # SendGrid smtpapi header implementation
8 | #
9 | class Header
10 | attr_reader :to, :sub, :section, :category, :unique_args, :filters
11 | attr_reader :send_at, :send_each_at, :asm_group_id, :ip_pool
12 |
13 | def initialize
14 | @to = []
15 | @sub = {}
16 | @section = {}
17 | @category = []
18 | @unique_args = {}
19 | @filters = {}
20 | @send_at = nil
21 | @send_each_at = []
22 | @asm_group_id = nil
23 | @ip_pool = nil
24 | end
25 |
26 | def add_to(address, name = nil)
27 | if address.is_a?(Array)
28 | @to.concat(address)
29 | else
30 | value = address
31 | value = "#{name} <#{address}>" unless name.nil?
32 | @to.push(value)
33 | end
34 | self
35 | end
36 |
37 | def set_tos(addresses)
38 | @to = addresses
39 | self
40 | end
41 |
42 | def add_substitution(sub, values)
43 | @sub[sub] = values
44 | self
45 | end
46 |
47 | def set_substitutions(key_value_pairs)
48 | @sub = key_value_pairs
49 | self
50 | end
51 |
52 | def add_section(key, value)
53 | @section[key] = value
54 | self
55 | end
56 |
57 | def set_sections(key_value_pairs)
58 | @section = key_value_pairs
59 | self
60 | end
61 |
62 | def add_unique_arg(key, value)
63 | @unique_args[key] = value
64 | self
65 | end
66 |
67 | def set_unique_args(key_value_pairs)
68 | @unique_args = key_value_pairs
69 | self
70 | end
71 |
72 | def add_category(category)
73 | @category.push(category)
74 | self
75 | end
76 |
77 | def set_categories(categories)
78 | @category = categories
79 | self
80 | end
81 |
82 | def add_filter(filter_name, parameter_name, parameter_value)
83 | @filters[filter_name] = {} if @filters[filter_name].nil?
84 | @filters[filter_name]['settings'] = {} if @filters[filter_name]['settings'].nil?
85 | @filters[filter_name]['settings'][parameter_name] = parameter_value
86 | self
87 | end
88 |
89 | def set_filters(filters)
90 | @filters = filters
91 | self
92 | end
93 |
94 | def set_send_at(send_at)
95 | @send_at = send_at
96 | self
97 | end
98 |
99 | def set_send_each_at(send_each_at)
100 | @send_each_at = send_each_at
101 | self
102 | end
103 |
104 | def set_asm_group(group_id)
105 | @asm_group_id = group_id
106 | self
107 | end
108 |
109 | def set_ip_pool(pool_name)
110 | @ip_pool = pool_name
111 | self
112 | end
113 |
114 | def json_string
115 | escape_unicode(to_array.to_json)
116 | end
117 | alias_method :to_json, :json_string
118 |
119 | def escape_unicode(str)
120 | str.unpack('U*').map do |i|
121 | if i > 65_535
122 | "\\u#{format('%04x', ((i - 0x10000) / 0x400 + 0xD800))}" \
123 | "\\u#{format('%04x', ((i - 0x10000) % 0x400 + 0xDC00))}"
124 | elsif i > 127
125 | "\\u#{format('%04x', i)}"
126 | else
127 | i.chr('UTF-8')
128 | end
129 | end.join
130 | end
131 |
132 | protected
133 |
134 | def to_array
135 | data = {}
136 | data['to'] = @to unless @to.empty?
137 | data['sub'] = @sub unless @sub.empty?
138 | data['section'] = @section unless @section.empty?
139 | data['unique_args'] = @unique_args unless @unique_args.empty?
140 | data['category'] = @category unless @category.empty?
141 | data['filters'] = @filters unless @filters.empty?
142 | data['send_at'] = @send_at.to_i unless @send_at.nil?
143 | data['asm_group_id'] = @asm_group_id.to_i unless @asm_group_id.nil?
144 | data['ip_pool'] = @ip_pool unless @ip_pool.nil?
145 | str_each_at = []
146 |
147 | @send_each_at.each do |val|
148 | str_each_at.push(val.to_i)
149 | end
150 |
151 | data['send_each_at'] = str_each_at unless str_each_at.empty?
152 |
153 | data
154 | end
155 | end
156 | end
157 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Hello! Thank you for choosing to help contribute to one of the SendGrid open source libraries. There are many ways you can contribute and help is always welcome. We simply ask that you follow the following contribution policies.
2 |
3 | - [Improvements to the Codebase](#improvements-to-the-codebase)
4 | - [Understanding the Code Base](#understanding-the-codebase)
5 | - [Testing](#testing)
6 | - [Style Guidelines & Naming Conventions](#style-guidelines-and-naming-conventions)
7 | - [Creating a Pull Request](#creating-a-pull-request)
8 | - [Code Reviews](#code-reviews)
9 |
10 |
11 | ## Improvements to the Codebase
12 |
13 | We welcome direct contributions to the smtpapi-ruby code base. Thank you!
14 |
15 | ### Development Environment ###
16 |
17 | #### Install and Run Locally ####
18 |
19 | ##### Prerequisites #####
20 |
21 | - Ruby version 2.2
22 | - The SendGrid Service, starting at the [free level](https://sendgrid.com/free?source=smtpapi-ruby))
23 |
24 | ##### Initial setup: #####
25 |
26 | ```bash
27 | git clone https://github.com/sendgrid/smtpapi-ruby.git
28 | cd smtpapi-ruby
29 | ```
30 |
31 | ##### Execute: #####
32 |
33 | See the [examples folder](examples) to get started quickly.
34 |
35 | To run the example:
36 |
37 | ```bash
38 | ruby examples/example.rb
39 | ```
40 |
41 |
42 | ## Understanding the Code Base
43 |
44 | **/examples**
45 |
46 | Working examples that demonstrate usage.
47 |
48 | **/tess**
49 |
50 | Unit tests
51 |
52 | **/lib/smtpapi**
53 |
54 | Source code.
55 |
56 |
57 | ## Testing
58 |
59 | All PRs require passing tests before the PR will be reviewed.
60 |
61 | All test files are in the [`test`](test) directory.
62 |
63 | For the purposes of contributing to this repo, please update the [`test.rb`](test/test.rb) file with unit tests as you modify the code.
64 |
65 | To run the tests:
66 |
67 | ```bash
68 | rake test
69 | ```
70 |
71 |
72 | ## Style Guidelines & Naming Conventions
73 |
74 | Generally, we follow the style guidelines as suggested by the official language. However, we ask that you conform to the styles that already exist in the library. If you wish to deviate, please explain your reasoning.
75 |
76 | - [Community Driven Style Guide](https://github.com/bbatsov/ruby-style-guide)
77 |
78 | Please run your code through:
79 |
80 | - [rubocop](https://github.com/bbatsov/rubocop).
81 |
82 | ## Creating a Pull Request
83 |
84 | 1. [Fork](https://help.github.com/fork-a-repo/) the project, clone your fork,
85 | and configure the remotes:
86 |
87 | ```bash
88 | # Clone your fork of the repo into the current directory
89 | git clone https://github.com/sendgrid/smtpapi-ruby
90 | # Navigate to the newly cloned directory
91 | cd smtpapi-ruby
92 | # Assign the original repo to a remote called "upstream"
93 | git remote add upstream https://github.com/sendgrid/smtpapi-ruby
94 | ```
95 |
96 | 2. If you cloned a while ago, get the latest changes from upstream:
97 |
98 | ```bash
99 | git checkout
100 | git pull upstream
101 | ```
102 |
103 | 3. Create a new topic branch (off the main project development branch) to
104 | contain your feature, change, or fix:
105 |
106 | ```bash
107 | git checkout -b
108 | ```
109 |
110 | 4. Commit your changes in logical chunks. Please adhere to these [git commit
111 | message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
112 | or your code is unlikely to be merged into the main project. Use Git's
113 | [interactive rebase](https://help.github.com/articles/interactive-rebase)
114 | feature to tidy up your commits before making them public.
115 |
116 | 4a. Create tests.
117 |
118 | 4b. Create or update the example code that demonstrates the functionality of this change to the code.
119 |
120 | 5. Locally merge (or rebase) the upstream development branch into your topic branch:
121 |
122 | ```bash
123 | git pull [--rebase] upstream main
124 | ```
125 |
126 | 6. Push your topic branch up to your fork:
127 |
128 | ```bash
129 | git push origin
130 | ```
131 |
132 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)
133 | with a clear title and description against the `main` branch. All tests must be passing before we will review the PR.
134 |
135 |
136 | ## Code Reviews
137 |
138 | If you can, please look at open PRs and review them. Give feedback and help us merge these PRs much faster! If you don't know how, Github has some great information on how to review a Pull Request.
139 |
--------------------------------------------------------------------------------
/test/test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 | require 'test/unit'
3 | require './lib/smtpapi'
4 |
5 | #
6 | # SmtpapiTest implementation
7 | #
8 | class SmtpapiTest < Test::Unit::TestCase
9 | def test_version
10 | assert_equal('0.1.12', Smtpapi::VERSION)
11 | end
12 |
13 | def test_empty
14 | header = Smtpapi::Header.new
15 | assert_equal('{}', header.json_string)
16 | end
17 |
18 | def test_add_to
19 | header = Smtpapi::Header.new
20 | header.add_to('you@youremail.com')
21 | header.add_to('other@otheremail.com', 'Other Name')
22 | assert_equal(
23 | '{"to":["you@youremail.com","Other Name "]}',
24 | header.json_string
25 | )
26 | end
27 |
28 | def test_add_to_array
29 | header = Smtpapi::Header.new
30 | header.add_to(['you@youremail.com', 'my@myemail.com'])
31 | assert_equal(
32 | '{"to":["you@youremail.com","my@myemail.com"]}',
33 | header.json_string
34 | )
35 | end
36 |
37 | def test_set_tos
38 | header = Smtpapi::Header.new
39 | header.set_tos(['you@youremail.com', 'other@otheremail.com'])
40 | assert_equal(
41 | '{"to":["you@youremail.com","other@otheremail.com"]}',
42 | header.json_string
43 | )
44 | end
45 |
46 | def test_add_substitution
47 | header = Smtpapi::Header.new
48 | header.add_substitution('keep', ['secret'])
49 | header.add_substitution('other', %w(one two))
50 | assert_equal(
51 | '{"sub":{"keep":["secret"],"other":["one","two"]}}',
52 | header.json_string
53 | )
54 | end
55 |
56 | def test_set_substitutions
57 | header = Smtpapi::Header.new
58 | header.set_substitutions('keep' => ['secret'])
59 | assert_equal('{"sub":{"keep":["secret"]}}', header.json_string)
60 | end
61 |
62 | def test_add_section
63 | header = Smtpapi::Header.new
64 | header.add_section('-charge-', 'This ship is useless.')
65 | header.add_section('-bomber-', 'Only for sad vikings.')
66 | assert_equal(
67 | '{"section":'\
68 | '{"-charge-":"This ship is useless.",'\
69 | '"-bomber-":"Only for sad vikings."}}',
70 | header.json_string
71 | )
72 | end
73 |
74 | def test_set_sections
75 | header = Smtpapi::Header.new
76 | header.set_sections('-charge-' => 'This ship is useless.')
77 | assert_equal(
78 | '{"section":{"-charge-":"This ship is useless."}}',
79 | header.json_string
80 | )
81 | end
82 |
83 | def test_add_unique_arg
84 | header = Smtpapi::Header.new
85 | header.add_unique_arg('cat', 'dogs')
86 | assert_equal('{"unique_args":{"cat":"dogs"}}', header.json_string)
87 | end
88 |
89 | def test_set_unique_args
90 | header = Smtpapi::Header.new
91 | header.set_unique_args('cow' => 'chicken')
92 | header.set_unique_args('dad' => 'proud')
93 | assert_equal('{"unique_args":{"dad":"proud"}}', header.json_string)
94 | end
95 |
96 | def test_add_category
97 | header = Smtpapi::Header.new
98 | header.add_category('tactics')
99 | header.add_category('advanced')
100 | assert_equal('{"category":["tactics","advanced"]}', header.json_string)
101 | end
102 |
103 | def test_set_categories
104 | header = Smtpapi::Header.new
105 | header.set_categories(%w(tactics advanced))
106 | assert_equal('{"category":["tactics","advanced"]}', header.json_string)
107 | end
108 |
109 | def test_add_filter
110 | header = Smtpapi::Header.new
111 | header.add_filter('footer', 'enable', 1)
112 | header.add_filter('footer', 'text/html', 'boo')
113 | assert_equal(
114 | '{"filters":'\
115 | '{"footer":'\
116 | '{"settings":'\
117 | '{"enable":1,'\
118 | '"text/html":"boo"'\
119 | '}'\
120 | '}'\
121 | '}'\
122 | '}',
123 | header.json_string
124 | )
125 | end
126 |
127 | def test_set_filters
128 | header = Smtpapi::Header.new
129 | filter = {
130 | 'footer' => {
131 | 'setting' => {
132 | 'enable' => 1,
133 | 'text/plain' => 'You can haz footers!'
134 | }
135 | }
136 | }
137 | header.set_filters(filter)
138 | assert_equal(
139 | '{"filters":'\
140 | '{"footer":'\
141 | '{"setting":'\
142 | '{"enable":1,"text/plain":"You can haz footers!"}'\
143 | '}'\
144 | '}'\
145 | '}',
146 | header.json_string
147 | )
148 | end
149 |
150 | def test_add_category_unicode
151 | header = Smtpapi::Header.new
152 | header.add_category('Martí')
153 | header.add_category('天破活殺')
154 | header.add_category('天翔十字鳳')
155 | assert_equal(
156 | '{"category":'\
157 | '["Mart\\u00ed",'\
158 | '"\\u5929\\u7834\\u6d3b\\u6bba",'\
159 | '"\\u5929\\u7fd4\\u5341\\u5b57\\u9cf3"]}',
160 | header.json_string
161 | )
162 | header.add_category('鼖')
163 | assert_equal(
164 | '{"category":'\
165 | '["Mart\\u00ed",'\
166 | '"\\u5929\\u7834\\u6d3b\\u6bba",'\
167 | '"\\u5929\\u7fd4\\u5341\\u5b57\\u9cf3",'\
168 | '"\\ud87e\\ude1b"]}',
169 | header.json_string
170 | )
171 | end
172 |
173 | def test_sent_send_at
174 | header = Smtpapi::Header.new
175 | localtime = Time.local(2014, 8, 29, 17, 56, 35)
176 | header.set_send_at(localtime)
177 |
178 | assert_equal("{\"send_at\":#{localtime.to_i}}", header.json_string)
179 | end
180 |
181 | def test_send_each_at
182 | header = Smtpapi::Header.new
183 | localtime1 = Time.local(2014, 8, 29, 17, 56, 35)
184 | localtime2 = Time.local(2013, 12, 31, 0, 0, 0)
185 | localtime3 = Time.local(2015, 9, 1, 4, 5, 6)
186 | header.set_send_each_at([localtime1, localtime2, localtime3])
187 |
188 | assert_equal(
189 | '{"send_each_at":'\
190 | "[#{localtime1.to_i},#{localtime2.to_i},#{localtime3.to_i}]"\
191 | '}',
192 | header.json_string
193 | )
194 | end
195 |
196 | def test_asm_group_id
197 | header = Smtpapi::Header.new
198 | header.set_asm_group(2)
199 |
200 | assert_equal('{"asm_group_id":2}', header.json_string)
201 | end
202 |
203 | def test_ip_pool
204 | header = Smtpapi::Header.new
205 | header.set_ip_pool('test_pool')
206 |
207 | assert_equal('{"ip_pool":"test_pool"}', header.json_string)
208 | end
209 |
210 | # def test_docker_exists
211 | # assert(File.file?('./Dockerfile') || File.file?('./docker/Dockerfile'))
212 | # end
213 |
214 | # def test_docker_compose_exists
215 | # assert(
216 | # File.file?('./docker-compose.yml') ||
217 | # File.file?('./docker/docker-compose.yml')
218 | # )
219 | # end
220 |
221 | def test_env_sample_exists
222 | assert(File.file?('./.env_sample'))
223 | end
224 |
225 | def test_gitignore_exists
226 | assert(File.file?('./.gitignore'))
227 | end
228 |
229 | def test_github_actions_exists
230 | assert(File.file?('./.github/workflows/test-and-deploy.yml'))
231 | end
232 |
233 | def test_changelog_exists
234 | assert(File.file?('./CHANGELOG.md'))
235 | end
236 |
237 | def test_code_of_conduct_exists
238 | assert(File.file?('./CODE_OF_CONDUCT.md'))
239 | end
240 |
241 | def test_contributing_exists
242 | assert(File.file?('./CONTRIBUTING.md'))
243 | end
244 |
245 | def test_license_exists
246 | assert(File.file?('./LICENSE'))
247 | end
248 |
249 | def test_pull_request_template_exists
250 | assert(File.file?('./PULL_REQUEST_TEMPLATE.md'))
251 | end
252 |
253 | def test_readme_exists
254 | assert(File.file?('./README.md'))
255 | end
256 |
257 | def test_troubleshooting_exists
258 | assert(File.file?('./TROUBLESHOOTING.md'))
259 | end
260 |
261 | # def test_usage_exists
262 | # assert(File.file?('./USAGE.md'))
263 | # end
264 |
265 | # def test_use_cases_exists
266 | # assert(File.file?('./USE_CASES.md'))
267 | # end
268 |
269 | def test_license_date_is_updated
270 | license_year = IO.read('LICENSE').match(
271 | /Copyright \(C\) (\d{4}), Twilio SendGrid/
272 | )[1]
273 | current_year = Time.new.year
274 |
275 | assert_equal(current_year, license_year.to_i)
276 | end
277 | end
278 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 | All notable changes to this project will be documented in this file.
3 |
4 | This project adheres to [Semantic Versioning](http://semver.org/).
5 |
6 | [2022-03-09] Version 0.1.12
7 | ---------------------------
8 | **Library - Chore**
9 | - [PR #102](https://github.com/sendgrid/smtpapi-ruby/pull/102): push Datadog Release Metric upon deploy success. Thanks to [@eshanholtz](https://github.com/eshanholtz)!
10 |
11 |
12 | [2022-02-09] Version 0.1.11
13 | ---------------------------
14 | **Library - Chore**
15 | - [PR #101](https://github.com/sendgrid/smtpapi-ruby/pull/101): upgrade supported language versions. Thanks to [@childish-sambino](https://github.com/childish-sambino)!
16 | - [PR #100](https://github.com/sendgrid/smtpapi-ruby/pull/100): add gh release to workflow. Thanks to [@shwetha-manvinkurke](https://github.com/shwetha-manvinkurke)!
17 | - [PR #99](https://github.com/sendgrid/smtpapi-ruby/pull/99): migrate to gh actions. Thanks to [@beebzz](https://github.com/beebzz)!
18 |
19 |
20 | [2022-01-12] Version 0.1.10
21 | ---------------------------
22 | **Library - Chore**
23 | - [PR #98](https://github.com/sendgrid/smtpapi-ruby/pull/98): update license year. Thanks to [@JenniferMah](https://github.com/JenniferMah)!
24 |
25 |
26 | [2020-09-02] Version 0.1.9
27 | --------------------------
28 | **Library - Chore**
29 | - [PR #71](https://github.com/sendgrid/smtpapi-ruby/pull/71): Conformance To Style Standards With RuboCop & TravisCI. Thanks to [@alanunruh](https://github.com/alanunruh)!
30 |
31 |
32 | [2020-08-19] Version 0.1.8
33 | --------------------------
34 | **Library - Docs**
35 | - [PR #72](https://github.com/sendgrid/smtpapi-ruby/pull/72): Update *.md files using Grammarly. Thanks to [@anatolyyyyyy](https://github.com/anatolyyyyyy)!
36 |
37 | **Library - Chore**
38 | - [PR #96](https://github.com/sendgrid/smtpapi-ruby/pull/96): update GitHub branch references to use HEAD. Thanks to [@thinkingserious](https://github.com/thinkingserious)!
39 |
40 |
41 | [2020-08-05] Version 0.1.7
42 | --------------------------
43 | **Library - Chore**
44 | - [PR #36](https://github.com/sendgrid/smtpapi-ruby/pull/36): fix CodeClimate suggestions. Thanks to [@valterbarros](https://github.com/valterbarros)!
45 |
46 |
47 | [2020-07-22] Version 0.1.6
48 | --------------------------
49 | **Library - Docs**
50 | - [PR #82](https://github.com/sendgrid/smtpapi-ruby/pull/82): Create usage.md file. Thanks to [@prathamsharma92](https://github.com/prathamsharma92)!
51 | - [PR #83](https://github.com/sendgrid/smtpapi-ruby/pull/83): add code review section in contributions. Thanks to [@prathamsharma92](https://github.com/prathamsharma92)!
52 |
53 |
54 | [2020-03-04] Version 0.1.5
55 | --------------------------
56 | **Library - Chore**
57 | - [PR #95](https://github.com/sendgrid/smtpapi-ruby/pull/95): add Ruby 2.7 to Travis. Thanks to [@childish-sambino](https://github.com/childish-sambino)!
58 |
59 |
60 | [2020-02-19] Version 0.1.4
61 | --------------------------
62 | **Library - Chore**
63 | - [PR #87](https://github.com/sendgrid/smtpapi-ruby/pull/87): Add test coverage analysis. Thanks to [@RolandBurrows](https://github.com/RolandBurrows)!
64 |
65 |
66 | [2020-01-22] Version 0.1.3
67 | --------------------------
68 | **Library - Fix**
69 | - [PR #94](https://github.com/sendgrid/smtpapi-ruby/pull/94): add skip_cleanup flag to fix travis deploy. Thanks to [@thinkingserious](https://github.com/thinkingserious)!
70 |
71 |
72 | [2020-01-22] Version 0.1.2
73 | --------------------------
74 | **Library - Docs**
75 | - [PR #93](https://github.com/sendgrid/smtpapi-ruby/pull/93): baseline all the templated markdown docs. Thanks to [@childish-sambino](https://github.com/childish-sambino)!
76 |
77 |
78 | [2020-01-09] Version 0.1.1
79 | --------------------------
80 | **Library - Chore**
81 | - [PR #92](https://github.com/sendgrid/smtpapi-ruby/pull/92): Auto-deploy upon successful tagged commit. Thanks to [@thinkingserious](https://github.com/thinkingserious)!
82 | - [PR #50](https://github.com/sendgrid/smtpapi-ruby/pull/50): Update License date range. Thanks to [@shucon](https://github.com/shucon)!
83 | - [PR #85](https://github.com/sendgrid/smtpapi-ruby/pull/85): updated Readme.md. Thanks to [@rahulpuroht](https://github.com/rahulpuroht)!
84 | - [PR #67](https://github.com/sendgrid/smtpapi-ruby/pull/67): Add Use Cases directory. Thanks to [@eyewritecode](https://github.com/eyewritecode)!
85 | - [PR #65](https://github.com/sendgrid/smtpapi-ruby/pull/65): Update LICENSE.txt. Thanks to [@nocategory](https://github.com/nocategory)!
86 | - [PR #64](https://github.com/sendgrid/smtpapi-ruby/pull/64): Resolve linting issues. Thanks to [@mrala](https://github.com/mrala)!
87 | - [PR #46](https://github.com/sendgrid/smtpapi-ruby/pull/46): Replaced .length > 0 with .!empty?. Thanks to [@nvzard](https://github.com/nvzard)!
88 | - [PR #35](https://github.com/sendgrid/smtpapi-ruby/pull/35): Replaced .length > 0 with !empty #32. Thanks to [@181514912](https://github.com/181514912)!
89 | - [PR #42](https://github.com/sendgrid/smtpapi-ruby/pull/42): Added unittest to check for specific repo files. Thanks to [@mptap](https://github.com/mptap)!
90 | - [PR #40](https://github.com/sendgrid/smtpapi-ruby/pull/40): Update LICENSE.txt. Thanks to [@GaryLeutheuser](https://github.com/GaryLeutheuser)!
91 | - [PR #41](https://github.com/sendgrid/smtpapi-ruby/pull/41): Add License end year test. Thanks to [@diegous](https://github.com/diegous)!
92 | - [PR #43](https://github.com/sendgrid/smtpapi-ruby/pull/43): Update LICENSE.txt. Thanks to [@astromoose](https://github.com/astromoose)!
93 | - [PR #26](https://github.com/sendgrid/smtpapi-ruby/pull/26): add a .env_sample file. Thanks to [@thepriefy](https://github.com/thepriefy)!
94 | - [PR #24](https://github.com/sendgrid/smtpapi-ruby/pull/24): add Gem badge to README file. Thanks to [@thepriefy](https://github.com/thepriefy)!
95 | - [PR #28](https://github.com/sendgrid/smtpapi-ruby/pull/28): Added the gem badge. Thanks to [@roooodcastro](https://github.com/roooodcastro)!
96 | - [PR #44](https://github.com/sendgrid/smtpapi-ruby/pull/44): Rename PULL_REQUEST_TEMPLATE.md. Thanks to [@thepriefy](https://github.com/thepriefy)!
97 | - [PR #25](https://github.com/sendgrid/smtpapi-ruby/pull/25): add pull request template. Thanks to [@rohitdaryanani](https://github.com/rohitdaryanani)!
98 | - [PR #31](https://github.com/sendgrid/smtpapi-ruby/pull/31): Added .codeclimate.yml. Thanks to [@veruz](https://github.com/veruz)!
99 | - [PR #18](https://github.com/sendgrid/smtpapi-ruby/pull/18): add .github/ISSUE_TEMPLATE. Thanks to [@thepriefy](https://github.com/thepriefy)!
100 | - [PR #13](https://github.com/sendgrid/smtpapi-ruby/pull/13): Add/update badges on README. Thanks to [@jessecalton](https://github.com/jessecalton)!
101 | - [PR #12](https://github.com/sendgrid/smtpapi-ruby/pull/12): more SEO friendly url. Thanks to [@ladhadha](https://github.com/ladhadha)!
102 |
103 | **Library - Fix**
104 | - [PR #69](https://github.com/sendgrid/smtpapi-ruby/pull/69): Remove duplicate text in CONTRIBUTING.md. Thanks to [@eyewritecode](https://github.com/eyewritecode)!
105 | - [PR #17](https://github.com/sendgrid/smtpapi-ruby/pull/17): Fixed some Grammatical errors in CONTRIBUTING.md. Thanks to [@mohsincl](https://github.com/mohsincl)!
106 | - [PR #16](https://github.com/sendgrid/smtpapi-ruby/pull/16): update contributing and readme - fix typo and ToC. Thanks to [@pushkyn](https://github.com/pushkyn)!
107 |
108 | **Library - Docs**
109 | - [PR #15](https://github.com/sendgrid/smtpapi-ruby/pull/15): update README.md. Thanks to [@thepriefy](https://github.com/thepriefy)!
110 | - [PR #14](https://github.com/sendgrid/smtpapi-ruby/pull/14): add table of contents in README.md. Thanks to [@thepriefy](https://github.com/thepriefy)!
111 | - [PR #9](https://github.com/sendgrid/smtpapi-ruby/pull/9): Add TROUBLESHOOTING.md. Thanks to [@colto](https://github.com/colto)!
112 | - [PR #7](https://github.com/sendgrid/smtpapi-ruby/pull/7): Added Code of Conduct. Thanks to [@gr8shivam](https://github.com/gr8shivam)!
113 | - [PR #5](https://github.com/sendgrid/smtpapi-ruby/pull/5): Update README.md. Thanks to [@ciceropablo](https://github.com/ciceropablo)!
114 |
115 |
116 | [2015-04-06] Version 0.1.0
117 | --------------------------
118 | ### Changed
119 | - Change home to SendGrid
120 | - Add unit test dev dep
121 |
--------------------------------------------------------------------------------