sendlayer 1.0.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fcbeb3e895d3e5a69471af13feb2c2f85b02c6d8babda4e1417266caa848228d
4
+ data.tar.gz: e82bfac8473f5db810f7d465055b05ca0a92009eafc3c81204a97d0c143c9d00
5
+ SHA512:
6
+ metadata.gz: aa169ec23cc27a318feac68e0de833b5c89b9e142406d5bcbe3c365b4961c2d487c4f470a05754f330aacc160dc1a66d3c6c6d95f8cf5aef63bfa30f58945ed5
7
+ data.tar.gz: fa2bdd5529ed366c76b1da611240eb4d026b7dfbec0776537fecd260a1c906ed4d3144f0f6e20314c2d07709fed281b848873f435d36de94d922e41f8170f7f1
data/.env.example ADDED
@@ -0,0 +1,3 @@
1
+ # SendLayer API Key
2
+ # Get your API key from https://app.sendlayer.com
3
+ SENDLAYER_API_KEY=your-api-key-here
@@ -0,0 +1,101 @@
1
+ name: Publish Ruby Gem
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ permissions:
9
+ contents: write
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - name: Checkout code
16
+ uses: actions/checkout@v4
17
+
18
+ - name: Setup Ruby
19
+ uses: ruby/setup-ruby@v1
20
+ with:
21
+ ruby-version: '3.2'
22
+
23
+ - name: Install dependencies
24
+ run: |
25
+ gem install bundler
26
+ bundle install
27
+
28
+ - name: Run tests
29
+ run: bundle exec rspec
30
+
31
+ # - name: Run RuboCop
32
+ # run: bundle exec rubocop
33
+
34
+ publish:
35
+ needs: test
36
+ runs-on: ubuntu-latest
37
+
38
+ permissions:
39
+ contents: write
40
+ id-token: write
41
+
42
+ steps:
43
+ - uses: actions/checkout@v4
44
+ with:
45
+ persist-credentials: false
46
+
47
+ - name: Set up Ruby
48
+ uses: ruby/setup-ruby@v1
49
+ with:
50
+ bundler-cache: true
51
+ ruby-version: '3.2'
52
+
53
+ - name: Publish to RubyGems
54
+ uses: rubygems/release-gem@v1
55
+
56
+ github-release:
57
+ needs: publish
58
+ runs-on: ubuntu-latest
59
+ steps:
60
+ - name: Checkout code
61
+ uses: actions/checkout@v4
62
+ with:
63
+ fetch-depth: 0
64
+
65
+ - name: Setup Ruby
66
+ uses: ruby/setup-ruby@v1
67
+ with:
68
+ ruby-version: '3.2'
69
+
70
+ - name: Install dependencies
71
+ run: |
72
+ gem install bundler
73
+ bundle install
74
+
75
+ - name: Get version from VERSION file
76
+ id: get_version
77
+ run: |
78
+ VERSION=$(cat lib/sendlayer/version.rb | grep "VERSION = " | sed "s/VERSION = '\(.*\)'/\1/")
79
+ echo "VERSION=$VERSION" >> $GITHUB_ENV
80
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
81
+
82
+ - name: Create and push tag
83
+ run: |
84
+ git config --local user.email "github-actions[bot]@users.noreply.github.com"
85
+ git config --local user.name "github-actions[bot]"
86
+ git tag -a v${{ env.VERSION }} -m "Release v${{ env.VERSION }}"
87
+ git push origin v${{ env.VERSION }}
88
+
89
+ - name: Build gem
90
+ run: gem build sendlayer.gemspec
91
+
92
+
93
+ - name: Create GitHub Release
94
+ uses: softprops/action-gh-release@v1
95
+ with:
96
+ tag_name: v${{ steps.get_version.outputs.version }}
97
+ name: Release v${{ steps.get_version.outputs.version }}
98
+ files: sendlayer-${{ steps.get_version.outputs.version }}.gem
99
+ generate_release_notes: true
100
+ env:
101
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle
2
+ /public/assets
3
+ .env
4
+ /coverage
5
+
6
+ *.gem
7
+ *.gem.lock
8
+
9
+
10
+ /spec/examples.txt
data/.rubocop.yml ADDED
@@ -0,0 +1,27 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.7
3
+ NewCops: enable
4
+
5
+ Style/Documentation:
6
+ Enabled: false
7
+
8
+ Style/StringLiterals:
9
+ EnforcedStyle: double_quotes
10
+
11
+ Style/FrozenStringLiteralComment:
12
+ Enabled: true
13
+
14
+ Layout/LineLength:
15
+ Max: 120
16
+
17
+ Metrics/MethodLength:
18
+ Max: 20
19
+
20
+ Metrics/AbcSize:
21
+ Max: 20
22
+
23
+ Style/ClassAndModuleChildren:
24
+ Enabled: false
25
+
26
+ Style/WordArray:
27
+ MinSize: 3
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in sendlayer.gemspec
6
+ gemspec
7
+
8
+ group :development, :test do
9
+ gem "pry", "~> 0.14"
10
+ gem "pry-byebug", "~> 3.10"
11
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,106 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sendlayer (1.0.0)
5
+ mime-types (~> 3.4)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ addressable (2.8.7)
11
+ public_suffix (>= 2.0.2, < 7.0)
12
+ ast (2.4.3)
13
+ bigdecimal (3.3.0)
14
+ byebug (12.0.0)
15
+ coderay (1.1.3)
16
+ crack (1.0.0)
17
+ bigdecimal
18
+ rexml
19
+ diff-lcs (1.6.2)
20
+ docile (1.4.1)
21
+ hashdiff (1.2.1)
22
+ json (2.15.1)
23
+ language_server-protocol (3.17.0.5)
24
+ lint_roller (1.1.0)
25
+ logger (1.7.0)
26
+ method_source (1.1.0)
27
+ mime-types (3.7.0)
28
+ logger
29
+ mime-types-data (~> 3.2025, >= 3.2025.0507)
30
+ mime-types-data (3.2025.0924)
31
+ parallel (1.27.0)
32
+ parser (3.3.9.0)
33
+ ast (~> 2.4.1)
34
+ racc
35
+ prism (1.5.1)
36
+ pry (0.15.2)
37
+ coderay (~> 1.1)
38
+ method_source (~> 1.0)
39
+ pry-byebug (3.11.0)
40
+ byebug (~> 12.0)
41
+ pry (>= 0.13, < 0.16)
42
+ public_suffix (6.0.2)
43
+ racc (1.8.1)
44
+ rainbow (3.1.1)
45
+ rake (13.3.0)
46
+ regexp_parser (2.11.3)
47
+ rexml (3.4.4)
48
+ rspec (3.13.1)
49
+ rspec-core (~> 3.13.0)
50
+ rspec-expectations (~> 3.13.0)
51
+ rspec-mocks (~> 3.13.0)
52
+ rspec-core (3.13.5)
53
+ rspec-support (~> 3.13.0)
54
+ rspec-expectations (3.13.5)
55
+ diff-lcs (>= 1.2.0, < 2.0)
56
+ rspec-support (~> 3.13.0)
57
+ rspec-mocks (3.13.5)
58
+ diff-lcs (>= 1.2.0, < 2.0)
59
+ rspec-support (~> 3.13.0)
60
+ rspec-support (3.13.6)
61
+ rubocop (1.81.1)
62
+ json (~> 2.3)
63
+ language_server-protocol (~> 3.17.0.2)
64
+ lint_roller (~> 1.1.0)
65
+ parallel (~> 1.10)
66
+ parser (>= 3.3.0.2)
67
+ rainbow (>= 2.2.2, < 4.0)
68
+ regexp_parser (>= 2.9.3, < 3.0)
69
+ rubocop-ast (>= 1.47.1, < 2.0)
70
+ ruby-progressbar (~> 1.7)
71
+ unicode-display_width (>= 2.4.0, < 4.0)
72
+ rubocop-ast (1.47.1)
73
+ parser (>= 3.3.7.2)
74
+ prism (~> 1.4)
75
+ ruby-progressbar (1.13.0)
76
+ simplecov (0.22.0)
77
+ docile (~> 1.1)
78
+ simplecov-html (~> 0.11)
79
+ simplecov_json_formatter (~> 0.1)
80
+ simplecov-html (0.13.2)
81
+ simplecov_json_formatter (0.1.4)
82
+ unicode-display_width (3.2.0)
83
+ unicode-emoji (~> 4.1)
84
+ unicode-emoji (4.1.0)
85
+ webmock (3.25.1)
86
+ addressable (>= 2.8.0)
87
+ crack (>= 0.3.2)
88
+ hashdiff (>= 0.4.0, < 2.0.0)
89
+
90
+ PLATFORMS
91
+ arm64-darwin-22
92
+ x86_64-linux
93
+
94
+ DEPENDENCIES
95
+ bundler (~> 2.0)
96
+ pry (~> 0.14)
97
+ pry-byebug (~> 3.10)
98
+ rake (~> 13.0)
99
+ rspec (~> 3.0)
100
+ rubocop (~> 1.0)
101
+ sendlayer!
102
+ simplecov (~> 0.21)
103
+ webmock (~> 3.0)
104
+
105
+ BUNDLED WITH
106
+ 2.4.19
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 SendLayer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do 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.
data/README.md ADDED
@@ -0,0 +1,222 @@
1
+ <a href="https://sendlayer.com">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: light)" srcset="https://sendlayer.com/wp-content/themes/sendlayer-theme/assets/images/svg/logo-dark.svg">
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://sendlayer.com/wp-content/themes/sendlayer-theme/assets/images/svg/logo-light.svg">
5
+ <img alt="SendLayer Logo" width="200px" src="https://sendlayer.com/wp-content/themes/sendlayer-theme/assets/images/svg/logo-light.svg">
6
+ </picture>
7
+ </a>
8
+
9
+ ### SendLayer Ruby SDK
10
+
11
+ The official Ruby SDK for interacting with the SendLayer API, providing a simple and intuitive interface for sending emails, managing webhooks, and retrieving email events.
12
+
13
+ [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'sendlayer'
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ ```bash
26
+ bundle install
27
+ ```
28
+
29
+ Or install it yourself as:
30
+
31
+ ```bash
32
+ gem install sendlayer
33
+ ```
34
+
35
+ ## Quick Start
36
+
37
+ ```ruby
38
+ require 'sendlayer'
39
+
40
+ # Initialize the SDK
41
+ sendlayer = SendLayer::SendLayer.new('your-api-key')
42
+
43
+ # Send an email
44
+ response = sendlayer.emails.send(
45
+ from: 'sender@example.com',
46
+ to: 'recipient@example.com',
47
+ subject: 'Test Email',
48
+ text: 'This is a test email'
49
+ )
50
+
51
+ puts "Email sent! Message ID: #{response['MessageID']}"
52
+ ```
53
+
54
+ ## Features
55
+
56
+ - **Email Module**: Send emails with HTML/text content, attachments, CC/BCC, reply-to, custom headers, and tags
57
+ - **Webhooks Module**: Create, retrieve, and delete webhooks for various email events
58
+ - **Events Module**: Retrieve email events with filtering options
59
+ - **Error Handling**: Clear, typed errors for API and validation issues
60
+
61
+ ## Email
62
+
63
+ Send emails using the `SendLayer` client:
64
+
65
+ ```ruby
66
+ require 'sendlayer'
67
+
68
+ sendlayer = SendLayer::SendLayer.new('your-api-key')
69
+
70
+ # Simple email
71
+ response = sendlayer.emails.send(
72
+ from: 'sender@example.com',
73
+ to: 'recipient@example.com',
74
+ subject: 'Welcome!',
75
+ text: 'Welcome to our platform!'
76
+ )
77
+
78
+ # HTML email with sender name
79
+ response = sendlayer.emails.send(
80
+ from: { email: 'sender@example.com', name: 'Paulie Paloma' },
81
+ to: 'recipient@example.com',
82
+ subject: 'Welcome!',
83
+ html: '<h1>Welcome!</h1><p>Welcome to our platform!</p>'
84
+ )
85
+
86
+ # Complex email with multiple recipients and attachments
87
+ response = sendlayer.emails.send(
88
+ from: { email: 'sender@example.com', name: 'Sender' },
89
+ to: [
90
+ { email: 'recipient1@example.com', name: 'Recipient 1' },
91
+ { email: 'recipient2@example.com', name: 'Recipient 2' }
92
+ ],
93
+ subject: 'Complex Email',
94
+ text: 'Plain text fallback',
95
+ html: '<p>This is a <strong>test email</strong>!</p>',
96
+ cc: [{ email: 'cc@example.com', name: 'CC' }],
97
+ bcc: [{ email: 'bcc@example.com', name: 'BCC' }],
98
+ reply_to: [{ email: 'reply@example.com', name: 'Reply' }],
99
+ attachments: [
100
+ { path: 'path/to/file.pdf', type: 'application/pdf' }
101
+ ],
102
+ headers: { 'X-Custom-Header' => 'value' },
103
+ tags: ['tag1', 'tag2']
104
+ )
105
+ ```
106
+
107
+ ## Events
108
+
109
+ ```ruby
110
+ require 'sendlayer'
111
+ require 'time'
112
+
113
+ sendlayer = SendLayer::SendLayer.new('your-api-key')
114
+
115
+ # Get all events
116
+ all_events = sendlayer.events.get
117
+ puts "Total events: #{all_events['TotalRecords']}"
118
+
119
+ # Get filtered events (last 24 hours, opened)
120
+ end_time = Time.now
121
+ start_time = end_time - (24 * 60 * 60) # 24 hours ago
122
+
123
+ filtered_events = sendlayer.events.get(
124
+ start_date: start_time,
125
+ end_date: end_time,
126
+ event: 'opened'
127
+ )
128
+
129
+ puts "Filtered events: #{filtered_events['TotalRecords']}"
130
+ ```
131
+
132
+ ## Webhooks
133
+
134
+ ```ruby
135
+ require 'sendlayer'
136
+
137
+ sendlayer = SendLayer::SendLayer.new('your-api-key')
138
+
139
+ # Create a webhook
140
+ webhook = sendlayer.webhooks.create(
141
+ url: 'https://your-domain.com/webhook',
142
+ event: 'open'
143
+ )
144
+ puts "Webhook created: #{webhook['WebhookID']}"
145
+
146
+ # Get all webhooks
147
+ webhooks = sendlayer.webhooks.get
148
+ puts "Webhooks: #{webhooks}"
149
+
150
+ # Delete a webhook
151
+ sendlayer.webhooks.delete(123)
152
+ ```
153
+
154
+ ## Error Handling
155
+
156
+ The SDK provides specific exception types for different error scenarios:
157
+
158
+ ```ruby
159
+ require 'sendlayer'
160
+
161
+ begin
162
+ response = sendlayer.emails.send(
163
+ from: 'sender@example.com',
164
+ to: 'recipient@example.com',
165
+ subject: 'Test Email',
166
+ text: 'This is a test email'
167
+ )
168
+ rescue SendLayer::SendLayerAPIError => e
169
+ puts "API error: #{e.message} (Status: #{e.status_code})"
170
+ rescue SendLayer::SendLayerValidationError => e
171
+ puts "Validation error: #{e.message}"
172
+ rescue SendLayer::SendLayerAuthenticationError => e
173
+ puts "Authentication error: #{e.message}"
174
+ rescue SendLayer::SendLayerError => e
175
+ puts "SendLayer error: #{e.message}"
176
+ rescue => e
177
+ puts "Unexpected error: #{e.message}"
178
+ end
179
+ ```
180
+
181
+ ## Exception Types
182
+
183
+ - `SendLayer::SendLayerError`: Base exception for all SendLayer errors
184
+ - `SendLayer::SendLayerAPIError`: API-specific errors with status code and response data
185
+ - `SendLayer::SendLayerAuthenticationError`: Invalid API key or authentication issues
186
+ - `SendLayer::SendLayerValidationError`: Invalid parameters or validation errors
187
+ - `SendLayer::SendLayerNotFoundError`: Resource not found (404 errors)
188
+ - `SendLayer::SendLayerRateLimitError`: Rate limit exceeded (429 errors)
189
+ - `SendLayer::SendLayerInternalServerError`: Server errors (5xx errors)
190
+
191
+ ## Supported Events
192
+
193
+ ### Webhook Events
194
+ - `bounce`: Email bounced
195
+ - `click`: Link was clicked
196
+ - `open`: Email was opened
197
+ - `unsubscribe`: User unsubscribed
198
+ - `complaint`: User marked as spam
199
+ - `delivery`: Email was delivered
200
+
201
+ ### Event Tracking Events
202
+ - `accepted`: Email was accepted by the server
203
+ - `rejected`: Email was rejected
204
+ - `delivered`: Email was delivered
205
+ - `opened`: Email was opened
206
+ - `clicked`: Link was clicked
207
+ - `unsubscribed`: User unsubscribed
208
+ - `complained`: User marked as spam
209
+ - `failed`: Email delivery failed
210
+
211
+ ## Requirements
212
+
213
+ - Ruby 2.7.0 or higher
214
+ - mime-types gem
215
+
216
+ ## More Details
217
+
218
+ To learn more about using the SendLayer SDK, be sure to check our [Developer Documentation](https://developers.sendlayer.com/sdks/ruby).
219
+
220
+ ## License
221
+
222
+ MIT License - see [LICENSE](./LICENSE) file for details
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'rubocop/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ RuboCop::RakeTask.new
8
+
9
+ task default: [:spec, :rubocop]
10
+
11
+ desc 'Run all tests and code quality checks'
12
+ task ci: [:spec, :rubocop]
13
+
14
+ desc 'Build the gem'
15
+ task :build do
16
+ sh 'gem build sendlayer.gemspec'
17
+ end
18
+
19
+ desc 'Install the gem locally'
20
+ task install: :build do
21
+ sh 'gem install sendlayer-*.gem'
22
+ end
23
+
24
+ desc 'Clean up generated files'
25
+ task :clean do
26
+ sh 'rm -f sendlayer-*.gem'
27
+ sh 'rm -rf coverage/'
28
+ sh 'rm -rf .rspec_status'
29
+ end
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'sendlayer'
4
+
5
+ # Initialize the SDK
6
+ api_key = ENV['SENDLAYER_API_KEY']
7
+ unless api_key
8
+ puts "Please set SENDLAYER_API_KEY environment variable"
9
+ exit 1
10
+ end
11
+
12
+ sendlayer = SendLayer::SendLayer.new(api_key)
13
+
14
+ begin
15
+ # Get all events
16
+ puts "Retrieving all events..."
17
+ all_events = sendlayer.events.get
18
+ puts "✅ Retrieved events successfully!"
19
+ puts "Total events: #{all_events}"
20
+
21
+ # Get filtered events (last 7 days)
22
+ puts "\nRetrieving events from the last 7 days..."
23
+ end_time = Time.now
24
+ start_time = end_time - (7 * 24 * 60 * 60) # 7 days ago
25
+
26
+ filtered_events = sendlayer.events.get(
27
+ start_date: start_time,
28
+ end_date: end_time
29
+ )
30
+ puts "✅ Retrieved filtered events successfully!"
31
+ puts "Events in last 7 days: #{filtered_events['Events']}"
32
+
33
+ # Get specific event type (opened)
34
+ puts "\nRetrieving 'opened' events from the last 24 hours..."
35
+ end_time = Time.now
36
+ start_time = end_time - (24 * 60 * 60) # 24 hours ago
37
+
38
+ opened_events = sendlayer.events.get(
39
+ start_date: start_time,
40
+ end_date: end_time,
41
+ event: 'opened'
42
+ )
43
+ puts "✅ Retrieved 'opened' events successfully!"
44
+ puts "Opened events in last 24 hours: #{opened_events['TotalRecords']}"
45
+
46
+ # Display some sample events
47
+ if all_events['Events'] && !all_events['Events'].empty?
48
+ puts "\nSample events:"
49
+ all_events['Events'].first(3).each_with_index do |event, index|
50
+ puts " #{index + 1}. Event: #{event['Event']}, MessageID: #{event['MessageID']}, Timestamp: #{event['Timestamp']}"
51
+ end
52
+ end
53
+
54
+ rescue SendLayer::SendLayerError => e
55
+ puts "❌ SendLayer Error: #{e.message}"
56
+ rescue => e
57
+ puts "❌ Unexpected Error: #{e.message}"
58
+ end
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'sendlayer'
4
+
5
+
6
+ # Initialize the SDK
7
+ api_key = ENV['SENDLAYER_API_KEY']
8
+ unless api_key
9
+ puts "Please set SENDLAYER_API_KEY environment variable"
10
+ exit 1
11
+ end
12
+
13
+ sendlayer = SendLayer::SendLayer.new(api_key)
14
+
15
+ begin
16
+ # Send a simple email
17
+ puts "Sending a simple email..."
18
+ response = sendlayer.emails.send(
19
+ from: 'paulie@example.com',
20
+ to: 'pattie@example.com',
21
+ subject: 'Ruby SDK Test Email',
22
+ text: 'This is a test email sent using the SendLayer Ruby SDK!'
23
+ )
24
+ puts "✅ Email sent successfully!"
25
+ puts "Message ID: #{response}"
26
+
27
+ # Send an HTML email with sender name
28
+ puts "\nSending an HTML email..."
29
+ response = sendlayer.emails.send(
30
+ from: { email: 'paulie@example.com', name: 'Paulie Paloma' },
31
+ to: [
32
+ {name: 'Pattie Paloma', email:'pattie@example.com'},
33
+ {name: 'John Doe', email:'john@example.com'}
34
+ ],
35
+ subject: 'HTML Email Test',
36
+ html: '<h1>Hello from Ruby!</h1><p>This is an <strong>HTML</strong> email sent using the SendLayer Ruby SDK.</p>',
37
+ text: 'Hello from Ruby! This is an HTML email sent using the SendLayer Ruby SDK.',
38
+ attachments: [
39
+ {
40
+ path: './path/to/attachment.pdf',
41
+ type: 'application/pdf'
42
+ },
43
+ {
44
+ path: 'https://placehold.co/600x400.png',
45
+ type: 'image/png'
46
+ }
47
+ ]
48
+ )
49
+ puts "✅ HTML email sent successfully!"
50
+ puts "Message ID: #{response}"
51
+
52
+ rescue SendLayer::SendLayerError => e
53
+ puts "❌ SendLayer Error: #{e.message}"
54
+ rescue => e
55
+ puts "❌ Unexpected Error: #{e.message}"
56
+ end
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'sendlayer'
4
+
5
+ # Initialize the SDK
6
+ api_key = ENV['SENDLAYER_API_KEY']
7
+ unless api_key
8
+ puts "Please set SENDLAYER_API_KEY environment variable"
9
+ exit 1
10
+ end
11
+
12
+ sendlayer = SendLayer::SendLayer.new(api_key)
13
+
14
+ begin
15
+ # Create a webhook
16
+ puts "Creating a webhook..."
17
+ webhook = sendlayer.webhooks.create(
18
+ url: 'https://example.com/webhook',
19
+ event: 'open'
20
+ )
21
+ puts "✅ Webhook created successfully!"
22
+ puts "Webhook ID: #{webhook}"
23
+
24
+ # Get all webhooks
25
+ puts "\nRetrieving all webhooks..."
26
+ webhooks = sendlayer.webhooks.get
27
+ puts "✅ Retrieved #{webhooks.length} webhook(s)"
28
+ webhooks.each_with_index do |webhook, index|
29
+ puts " Webhooks: #{webhooks}"
30
+ end
31
+
32
+ # Delete the webhook we just created
33
+ # if webhook['WebhookID']
34
+ puts "\nDeleting webhook..."
35
+ sendlayer.webhooks.delete(27712)
36
+ puts "✅ Webhook deleted successfully!"
37
+ # end
38
+
39
+ rescue SendLayer::SendLayerError => e
40
+ puts "❌ SendLayer Error: #{e.message}"
41
+ rescue => e
42
+ puts "❌ Unexpected Error: #{e.message}"
43
+ end
@@ -0,0 +1,98 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+ require 'json'
4
+ require 'timeout'
5
+
6
+ module SendLayer
7
+ class Client
8
+ BASE_URL = 'https://console.sendlayer.com/api/v1/'
9
+
10
+ attr_reader :api_key, :base_url, :timeout, :attachment_url_timeout
11
+
12
+ def initialize(api_key, options = {})
13
+ @api_key = api_key
14
+ @base_url = options[:base_url] || BASE_URL
15
+ @timeout = options[:timeout] || 30
16
+ @attachment_url_timeout = options[:attachment_url_timeout] || 30000
17
+ end
18
+
19
+ def make_request(method, endpoint, data = nil, params = {})
20
+ uri = URI("#{@base_url}#{endpoint}")
21
+
22
+ # Add query parameters
23
+ unless params.empty?
24
+ uri.query = URI.encode_www_form(params)
25
+ end
26
+
27
+ http = Net::HTTP.new(uri.host, uri.port)
28
+ http.use_ssl = true
29
+ http.read_timeout = @timeout
30
+
31
+ case method.upcase
32
+ when 'GET'
33
+ request = Net::HTTP::Get.new(uri)
34
+ when 'POST'
35
+ request = Net::HTTP::Post.new(uri)
36
+ request.body = data.to_json if data
37
+ request['Content-Type'] = 'application/json'
38
+ when 'DELETE'
39
+ request = Net::HTTP::Delete.new(uri)
40
+ else
41
+ raise SendLayerError.new("Unsupported HTTP method: #{method}")
42
+ end
43
+
44
+ request['Authorization'] = "Bearer #{@api_key}"
45
+ request['User-Agent'] = "SendLayer-Ruby/#{::SendLayer::VERSION}"
46
+
47
+ begin
48
+ response = http.request(request)
49
+ handle_response(response)
50
+ rescue Timeout::Error
51
+ raise SendLayerError.new("Request timeout after #{@timeout} seconds")
52
+ rescue => e
53
+ raise SendLayerError.new("Network error: #{e.message}")
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def handle_response(response)
60
+ case response.code.to_i
61
+ when 200..299
62
+ return JSON.parse(response.body) if response.body && !response.body.empty?
63
+ return {}
64
+ when 401
65
+ raise SendLayerAuthenticationError.new("Invalid API key")
66
+ when 400
67
+ error_data = parse_error_response(response)
68
+ raise SendLayerValidationError.new(error_data['Error'] || 'Invalid request parameters')
69
+ when 404
70
+ error_data = parse_error_response(response)
71
+ raise SendLayerNotFoundError.new(error_data['Error'] || 'Resource not found')
72
+ when 422
73
+ error_data = parse_error_response(response)
74
+ raise SendLayerValidationError.new(error_data['Error'] || 'Validation failed')
75
+ when 429
76
+ error_data = parse_error_response(response)
77
+ raise SendLayerRateLimitError.new(error_data['Error'] || 'Rate limit exceeded')
78
+ when 500..599
79
+ error_data = parse_error_response(response)
80
+ raise SendLayerInternalServerError.new(error_data['Error'] || 'Internal server error')
81
+ else
82
+ error_data = parse_error_response(response)
83
+ raise SendLayerAPIError.new(
84
+ error_data['Error'] || "HTTP #{response.code} error",
85
+ response.code.to_i,
86
+ response.body
87
+ )
88
+ end
89
+ end
90
+
91
+ def parse_error_response(response)
92
+ return {} unless response.body
93
+ JSON.parse(response.body)
94
+ rescue JSON::ParserError
95
+ { 'Error' => response.body }
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,183 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+ require 'base64'
4
+ require 'securerandom'
5
+
6
+ # Optional dependency for MIME type detection
7
+ begin
8
+ require 'mime/types'
9
+ MIME_TYPES_AVAILABLE = true
10
+ rescue LoadError
11
+ MIME_TYPES_AVAILABLE = false
12
+ end
13
+
14
+ module SendLayer
15
+ class Emails
16
+ def initialize(client)
17
+ @client = client
18
+ end
19
+
20
+ def send(from:, to:, subject:, text: nil, html: nil, cc: nil, bcc: nil, reply_to: nil,
21
+ attachments: nil, headers: nil, tags: nil)
22
+
23
+ # Validate required parameters
24
+ raise SendLayerValidationError.new("Either 'text' or 'html' content must be provided") if text.nil? && html.nil?
25
+
26
+ # Prepare email data
27
+ email_data = {
28
+ From: normalize_recipient(from, 'sender'),
29
+ To: normalize_recipients(to, 'recipient'),
30
+ Subject: subject
31
+ }
32
+
33
+ if html
34
+ email_data[:ContentType] = "HTML"
35
+ email_data[:HTMLContent] = html
36
+ else
37
+ email_data[:ContentType] = "Text"
38
+ email_data[:PlainContent] = text
39
+ end
40
+ email_data[:CC] = normalize_recipients(cc) if cc
41
+ email_data[:BCC] = normalize_recipients(bcc) if bcc
42
+ email_data[:ReplyTo] = normalize_recipients(reply_to, 'reply_to') if reply_to
43
+ email_data[:Headers] = headers if headers
44
+
45
+ if tags
46
+ unless tags.is_a?(Array) && tags.all? { |t| t.is_a?(String) }
47
+ raise SendLayerValidationError.new('Tags must be a list of strings')
48
+ end
49
+ email_data[:Tags] = tags
50
+ end
51
+
52
+ # Handle attachments
53
+ if attachments && !attachments.empty?
54
+ email_data[:Attachments] = process_attachments(attachments)
55
+ end
56
+
57
+ @client.make_request('POST', 'email', email_data)
58
+ end
59
+
60
+ private
61
+
62
+ def normalize_recipient(recipient, role = 'recipient')
63
+ case recipient
64
+ when String
65
+ validate_email(recipient, role)
66
+ { email: recipient }
67
+ when Hash
68
+ validate_email(recipient[:email] || recipient['email'], role)
69
+ {
70
+ email: recipient[:email] || recipient['email'],
71
+ name: recipient[:name] || recipient['name']
72
+ }.compact
73
+ else
74
+ raise SendLayerValidationError.new("Invalid #{role} format: #{recipient.class}")
75
+ end
76
+ end
77
+
78
+ def normalize_recipients(recipients, role = 'recipient')
79
+ return nil if recipients.nil?
80
+
81
+ case recipients
82
+ when String
83
+ [normalize_recipient(recipients, role)]
84
+ when Array
85
+ recipients.map { |r| normalize_recipient(r, role) }
86
+ else
87
+ raise SendLayerValidationError.new("Invalid #{role}s format: #{recipients.class}")
88
+ end
89
+ end
90
+
91
+ def validate_email(email, role = 'recipient')
92
+ email_regex = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
93
+ unless email_regex.match?(email)
94
+ raise SendLayerValidationError.new("Invalid #{role} email address: #{email}")
95
+ end
96
+ email
97
+ end
98
+
99
+ def process_attachments(attachments)
100
+ attachments.map do |attachment|
101
+ case attachment
102
+ when Hash
103
+ path = attachment[:path] || attachment['path']
104
+ type = attachment[:type] || attachment['type']
105
+ if path.nil? || (path.respond_to?(:strip) && path.strip.empty?)
106
+ raise SendLayerValidationError.new("Attachment path is required")
107
+ end
108
+ encoded_content = read_attachment_content(path)
109
+ {
110
+ Content: encoded_content,
111
+ Type: type || detect_content_type(path),
112
+ Filename: File.basename(path),
113
+ Disposition: "attachment",
114
+ ContentID: SecureRandom.random_number(2**31 - 1)
115
+ }
116
+ when String
117
+ encoded_content = read_attachment_content(attachment)
118
+ {
119
+ Content: encoded_content,
120
+ Type: detect_content_type(attachment),
121
+ Filename: File.basename(attachment),
122
+ Disposition: "attachment",
123
+ ContentID: SecureRandom.random_number(2**31 - 1)
124
+ }
125
+ else
126
+ raise SendLayerValidationError.new("Invalid attachment format: #{attachment.class}")
127
+ end
128
+ end
129
+ end
130
+
131
+ def read_attachment_content(path)
132
+ uri = URI.parse(path) rescue nil
133
+ is_url = uri && (uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS))
134
+
135
+ if is_url
136
+ http = Net::HTTP.new(uri.host, uri.port)
137
+ http.use_ssl = uri.is_a?(URI::HTTPS)
138
+ timeout_ms = (@client.attachment_url_timeout || 30000)
139
+ http.read_timeout = (timeout_ms.to_f / 1000.0)
140
+ request = Net::HTTP::Get.new(uri)
141
+ response = http.request(request)
142
+ unless response.is_a?(Net::HTTPSuccess)
143
+ raise SendLayerValidationError.new("Error fetching remote file: HTTP #{response.code}")
144
+ end
145
+ return Base64.strict_encode64(response.body)
146
+ end
147
+
148
+ unless File.exist?(path)
149
+ raise SendLayerValidationError.new("Attachment file not found: #{path}")
150
+ end
151
+
152
+ content = File.binread(path)
153
+ Base64.strict_encode64(content)
154
+ rescue => e
155
+ raise SendLayerValidationError.new("Error reading attachment: #{e.message}")
156
+ end
157
+
158
+ def detect_content_type(path)
159
+ if MIME_TYPES_AVAILABLE
160
+ mime_type = MIME::Types.type_for(path).first
161
+ mime_type ? mime_type.content_type : 'application/octet-stream'
162
+ else
163
+ # Fallback to basic file extension detection
164
+ case File.extname(path).downcase
165
+ when '.pdf'
166
+ 'application/pdf'
167
+ when '.txt'
168
+ 'text/plain'
169
+ when '.html', '.htm'
170
+ 'text/html'
171
+ when '.jpg', '.jpeg'
172
+ 'image/jpeg'
173
+ when '.png'
174
+ 'image/png'
175
+ when '.gif'
176
+ 'image/gif'
177
+ else
178
+ 'application/octet-stream'
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,39 @@
1
+ module SendLayer
2
+ class Events
3
+ VALID_EVENTS = %w[accepted rejected delivered opened clicked unsubscribed complained failed].freeze
4
+
5
+ def initialize(client)
6
+ @client = client
7
+ end
8
+
9
+ def get(start_date: nil, end_date: nil, event: nil, message_id: nil, start_from: nil, retrieve_count: nil)
10
+ validate_events_params(start_date, end_date, event, retrieve_count)
11
+
12
+ params = {}
13
+ params[:StartDate] = start_date.to_i if start_date
14
+ params[:EndDate] = end_date.to_i if end_date
15
+ params[:Event] = event if event
16
+ params[:MessageID] = message_id if message_id
17
+ params[:StartFrom] = start_from if start_from
18
+ params[:RetrieveCount] = retrieve_count if retrieve_count
19
+
20
+ @client.make_request('GET', 'events', nil, params)
21
+ end
22
+
23
+ private
24
+
25
+ def validate_events_params(start_date, end_date, event, retrieve_count)
26
+ if start_date && end_date && end_date < start_date
27
+ raise SendLayerValidationError.new("End date must be after start date")
28
+ end
29
+
30
+ if event && !VALID_EVENTS.include?(event)
31
+ raise SendLayerValidationError.new("Invalid event: #{event}. Valid events: #{VALID_EVENTS.join(', ')}")
32
+ end
33
+
34
+ if retrieve_count && retrieve_count <= 0
35
+ raise SendLayerValidationError.new("RetrieveCount must be greater than 0")
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,26 @@
1
+ module SendLayer
2
+ class SendLayerError < StandardError
3
+ attr_reader :message
4
+
5
+ def initialize(message)
6
+ @message = message
7
+ super(message)
8
+ end
9
+ end
10
+
11
+ class SendLayerAPIError < SendLayerError
12
+ attr_reader :status_code, :response
13
+
14
+ def initialize(message, status_code = nil, response = nil)
15
+ @status_code = status_code
16
+ @response = response
17
+ super(message)
18
+ end
19
+ end
20
+
21
+ class SendLayerAuthenticationError < SendLayerError; end
22
+ class SendLayerValidationError < SendLayerError; end
23
+ class SendLayerNotFoundError < SendLayerError; end
24
+ class SendLayerRateLimitError < SendLayerError; end
25
+ class SendLayerInternalServerError < SendLayerError; end
26
+ end
@@ -0,0 +1,3 @@
1
+ module SendLayer
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,53 @@
1
+ module SendLayer
2
+ class Webhooks
3
+ VALID_EVENTS = %w[bounce click open unsubscribe complaint delivery].freeze
4
+
5
+ def initialize(client)
6
+ @client = client
7
+ end
8
+
9
+ def create(url:, event:)
10
+ validate_webhook_params(url, event)
11
+
12
+ webhook_data = {
13
+ WebhookURL: url,
14
+ Event: event
15
+ }
16
+
17
+ @client.make_request('POST', 'webhooks', webhook_data)
18
+ end
19
+
20
+ def get
21
+ @client.make_request('GET', 'webhooks')
22
+ end
23
+
24
+ def delete(webhook_id)
25
+ raise SendLayerValidationError.new("Webhook ID is required") if webhook_id.nil?
26
+
27
+ unless webhook_id.is_a?(Integer) && webhook_id > 0
28
+ raise SendLayerValidationError.new("Webhook ID must be a positive integer")
29
+ end
30
+
31
+ @client.make_request('DELETE', "webhooks/#{webhook_id}")
32
+ end
33
+
34
+ private
35
+
36
+ def validate_webhook_params(url, event)
37
+ raise SendLayerValidationError.new("URL is required") if url.nil? || url.empty?
38
+ raise SendLayerValidationError.new("Event is required") if event.nil? || event.empty?
39
+
40
+ unless VALID_EVENTS.include?(event)
41
+ raise SendLayerValidationError.new("Invalid event: #{event}. Valid event types include: #{VALID_EVENTS.join(', ')}")
42
+ end
43
+
44
+ # Basic URL validation
45
+ uri = URI.parse(url)
46
+ unless uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
47
+ raise SendLayerValidationError.new("Invalid URL: #{url}")
48
+ end
49
+ rescue URI::InvalidURIError
50
+ raise SendLayerValidationError.new("Invalid URL: #{url}")
51
+ end
52
+ end
53
+ end
data/lib/sendlayer.rb ADDED
@@ -0,0 +1,19 @@
1
+ require_relative 'sendlayer/version'
2
+ require_relative 'sendlayer/client'
3
+ require_relative 'sendlayer/emails'
4
+ require_relative 'sendlayer/webhooks'
5
+ require_relative 'sendlayer/events'
6
+ require_relative 'sendlayer/exceptions'
7
+
8
+ module SendLayer
9
+ class SendLayer
10
+ attr_reader :emails, :webhooks, :events
11
+
12
+ def initialize(api_key, options = {})
13
+ @client = Client.new(api_key, options)
14
+ @emails = Emails.new(@client)
15
+ @webhooks = Webhooks.new(@client)
16
+ @events = Events.new(@client)
17
+ end
18
+ end
19
+ end
data/sendlayer.gemspec ADDED
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/sendlayer/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "sendlayer"
7
+ spec.version = ::SendLayer::VERSION
8
+ spec.authors = ["SendLayer"]
9
+ spec.email = ["support@sendlayer.com"]
10
+
11
+ spec.summary = "Official Ruby SDK for SendLayer API"
12
+ spec.description = "The official Ruby SDK for interacting with the SendLayer API, providing a simple and intuitive interface for sending emails, managing webhooks, and retrieving email events."
13
+ spec.homepage = "https://github.com/sendlayer/sendlayer-ruby"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.7.0"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/sendlayer/sendlayer-ruby"
19
+ spec.metadata["changelog_uri"] = "https://github.com/sendlayer/sendlayer-ruby/blob/main/CHANGELOG.md"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
24
+ end
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ # Dependencies
30
+ spec.add_dependency "mime-types", "~> 3.4"
31
+
32
+ # Development dependencies
33
+ spec.add_development_dependency "bundler", "~> 2.0"
34
+ spec.add_development_dependency "rake", "~> 13.0"
35
+ spec.add_development_dependency "rspec", "~> 3.0"
36
+ spec.add_development_dependency "webmock", "~> 3.0"
37
+ spec.add_development_dependency "rubocop", "~> 1.0"
38
+ spec.add_development_dependency "simplecov", "~> 0.21"
39
+ end
metadata ADDED
@@ -0,0 +1,166 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sendlayer
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - SendLayer
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-10-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mime-types
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: webmock
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.21'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.21'
111
+ description: The official Ruby SDK for interacting with the SendLayer API, providing
112
+ a simple and intuitive interface for sending emails, managing webhooks, and retrieving
113
+ email events.
114
+ email:
115
+ - support@sendlayer.com
116
+ executables: []
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - ".env.example"
121
+ - ".github/workflows/publish.yml"
122
+ - ".gitignore"
123
+ - ".rubocop.yml"
124
+ - Gemfile
125
+ - Gemfile.lock
126
+ - LICENSE
127
+ - README.md
128
+ - Rakefile
129
+ - examples/events.rb
130
+ - examples/send_email.rb
131
+ - examples/webhooks.rb
132
+ - lib/sendlayer.rb
133
+ - lib/sendlayer/client.rb
134
+ - lib/sendlayer/emails.rb
135
+ - lib/sendlayer/events.rb
136
+ - lib/sendlayer/exceptions.rb
137
+ - lib/sendlayer/version.rb
138
+ - lib/sendlayer/webhooks.rb
139
+ - sendlayer.gemspec
140
+ homepage: https://github.com/sendlayer/sendlayer-ruby
141
+ licenses:
142
+ - MIT
143
+ metadata:
144
+ homepage_uri: https://github.com/sendlayer/sendlayer-ruby
145
+ source_code_uri: https://github.com/sendlayer/sendlayer-ruby
146
+ changelog_uri: https://github.com/sendlayer/sendlayer-ruby/blob/main/CHANGELOG.md
147
+ post_install_message:
148
+ rdoc_options: []
149
+ require_paths:
150
+ - lib
151
+ required_ruby_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: 2.7.0
156
+ required_rubygems_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ requirements: []
162
+ rubygems_version: 3.4.19
163
+ signing_key:
164
+ specification_version: 4
165
+ summary: Official Ruby SDK for SendLayer API
166
+ test_files: []