mailshake-ruby 0.1.0 → 0.3.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +80 -19
  3. data/lib/mailshake/activity.rb +14 -7
  4. data/lib/mailshake/campaigns.rb +10 -5
  5. data/lib/mailshake/client.rb +23 -2
  6. data/lib/mailshake/errors.rb +15 -0
  7. data/lib/mailshake/leads.rb +6 -3
  8. data/lib/mailshake/models/add_recipients_request.rb +8 -0
  9. data/lib/mailshake/models/added_recipients.rb +8 -0
  10. data/lib/mailshake/models/base_model.rb +79 -0
  11. data/lib/mailshake/models/campaign.rb +10 -0
  12. data/lib/mailshake/models/campaign_export.rb +8 -0
  13. data/lib/mailshake/models/campaign_export_request.rb +8 -0
  14. data/lib/mailshake/models/click.rb +11 -0
  15. data/lib/mailshake/models/created_leads.rb +8 -0
  16. data/lib/mailshake/models/lead.rb +11 -0
  17. data/lib/mailshake/models/lead_status.rb +8 -0
  18. data/lib/mailshake/models/list.rb +30 -0
  19. data/lib/mailshake/models/message.rb +8 -0
  20. data/lib/mailshake/models/open.rb +11 -0
  21. data/lib/mailshake/models/recipient.rb +8 -0
  22. data/lib/mailshake/models/reply.rb +11 -0
  23. data/lib/mailshake/models/sender.rb +8 -0
  24. data/lib/mailshake/models/sent_message.rb +11 -0
  25. data/lib/mailshake/models/user.rb +8 -0
  26. data/lib/mailshake/recipients.rb +8 -4
  27. data/lib/mailshake/senders.rb +2 -1
  28. data/lib/mailshake/team.rb +2 -1
  29. data/lib/mailshake/version.rb +1 -1
  30. data/lib/mailshake.rb +21 -0
  31. data/spec/examples.txt +89 -60
  32. data/spec/mailshake/activity_spec.rb +4 -2
  33. data/spec/mailshake/campaigns_spec.rb +10 -4
  34. data/spec/mailshake/client_spec.rb +54 -0
  35. data/spec/mailshake/leads_spec.rb +5 -2
  36. data/spec/mailshake/models/base_model_spec.rb +118 -0
  37. data/spec/mailshake/models/list_spec.rb +103 -0
  38. data/spec/mailshake/push_spec.rb +1 -0
  39. data/spec/mailshake/recipients_spec.rb +7 -3
  40. data/spec/mailshake/senders_spec.rb +2 -1
  41. data/spec/mailshake/team_spec.rb +2 -1
  42. metadata +24 -2
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mailshake
4
+ module Models
5
+ class SentMessage < BaseModel
6
+ has_one :recipient, Recipient
7
+ has_one :campaign, Campaign
8
+ has_one :message, Message
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mailshake
4
+ module Models
5
+ class User < BaseModel
6
+ end
7
+ end
8
+ end
@@ -3,7 +3,7 @@
3
3
  module Mailshake
4
4
  class Recipients < Base
5
5
  def add(campaign_id:, addresses: nil, list_of_emails: nil, csv_data: nil, add_as_new_list: nil, truncate_extra_fields: nil)
6
- client.post("/recipients/add", camelize_params(
6
+ response = client.post("/recipients/add", camelize_params(
7
7
  campaign_id: campaign_id,
8
8
  addresses: addresses,
9
9
  list_of_emails: list_of_emails,
@@ -11,28 +11,32 @@ module Mailshake
11
11
  add_as_new_list: add_as_new_list,
12
12
  truncate_extra_fields: truncate_extra_fields
13
13
  ))
14
+ Models::AddRecipientsRequest.new(response)
14
15
  end
15
16
 
16
17
  def add_status(status_id:)
17
- client.get("/recipients/add-status", camelize_params(status_id: status_id))
18
+ response = client.get("/recipients/add-status", camelize_params(status_id: status_id))
19
+ Models::AddedRecipients.new(response)
18
20
  end
19
21
 
20
22
  def list(campaign_id:, filter: nil, search: nil, next_token: nil, per_page: nil)
21
- client.get("/recipients/list", camelize_params(
23
+ response = client.get("/recipients/list", camelize_params(
22
24
  campaign_id: campaign_id,
23
25
  filter: filter,
24
26
  search: search,
25
27
  next_token: next_token,
26
28
  per_page: per_page
27
29
  ))
30
+ Models::List.new(response, Models::Recipient)
28
31
  end
29
32
 
30
33
  def get(recipient_id: nil, campaign_id: nil, email_address: nil)
31
- client.get("/recipients/get", camelize_params(
34
+ response = client.get("/recipients/get", camelize_params(
32
35
  recipient_id: recipient_id,
33
36
  campaign_id: campaign_id,
34
37
  email_address: email_address
35
38
  ))
39
+ Models::Recipient.new(response)
36
40
  end
37
41
 
38
42
  def pause(campaign_id:, email_address:)
@@ -3,7 +3,8 @@
3
3
  module Mailshake
4
4
  class Senders < Base
5
5
  def list(search: nil, next_token: nil, per_page: nil)
6
- client.get("/senders/list", camelize_params(search: search, next_token: next_token, per_page: per_page))
6
+ response = client.get("/senders/list", camelize_params(search: search, next_token: next_token, per_page: per_page))
7
+ Models::List.new(response, Models::Sender)
7
8
  end
8
9
  end
9
10
  end
@@ -3,7 +3,8 @@
3
3
  module Mailshake
4
4
  class Team < Base
5
5
  def list_members(search: nil, next_token: nil, per_page: nil)
6
- client.get("/team/list-members", camelize_params(search: search, next_token: next_token, per_page: per_page))
6
+ response = client.get("/team/list-members", camelize_params(search: search, next_token: next_token, per_page: per_page))
7
+ Models::List.new(response, Models::User)
7
8
  end
8
9
  end
9
10
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mailshake
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/mailshake.rb CHANGED
@@ -6,6 +6,27 @@ require 'json'
6
6
  require_relative 'mailshake/version'
7
7
  require_relative 'mailshake/errors'
8
8
  require_relative 'mailshake/configuration'
9
+
10
+ # Models must be loaded before resource classes
11
+ require_relative 'mailshake/models/base_model'
12
+ require_relative 'mailshake/models/list'
13
+ require_relative 'mailshake/models/user'
14
+ require_relative 'mailshake/models/sender'
15
+ require_relative 'mailshake/models/message'
16
+ require_relative 'mailshake/models/campaign'
17
+ require_relative 'mailshake/models/recipient'
18
+ require_relative 'mailshake/models/lead'
19
+ require_relative 'mailshake/models/sent_message'
20
+ require_relative 'mailshake/models/open'
21
+ require_relative 'mailshake/models/click'
22
+ require_relative 'mailshake/models/reply'
23
+ require_relative 'mailshake/models/lead_status'
24
+ require_relative 'mailshake/models/created_leads'
25
+ require_relative 'mailshake/models/add_recipients_request'
26
+ require_relative 'mailshake/models/added_recipients'
27
+ require_relative 'mailshake/models/campaign_export_request'
28
+ require_relative 'mailshake/models/campaign_export'
29
+
9
30
  require_relative 'mailshake/client'
10
31
  require_relative 'mailshake/base'
11
32
  require_relative 'mailshake/campaigns'
data/spec/examples.txt CHANGED
@@ -1,60 +1,89 @@
1
- example_id | status | run_time |
2
- --------------------------------------------- | ------ | --------------- |
3
- ./spec/mailshake/activity_spec.rb[1:1:1] | passed | 0.00026 seconds |
4
- ./spec/mailshake/activity_spec.rb[1:1:2] | passed | 0.00036 seconds |
5
- ./spec/mailshake/activity_spec.rb[1:2:1] | passed | 0.00044 seconds |
6
- ./spec/mailshake/activity_spec.rb[1:3:1] | passed | 0.0004 seconds |
7
- ./spec/mailshake/activity_spec.rb[1:4:1] | passed | 0.00051 seconds |
8
- ./spec/mailshake/activity_spec.rb[1:5:1] | passed | 0.00041 seconds |
9
- ./spec/mailshake/activity_spec.rb[1:6:1] | passed | 0.00034 seconds |
10
- ./spec/mailshake/activity_spec.rb[1:7:1] | passed | 0.00039 seconds |
11
- ./spec/mailshake/campaigns_spec.rb[1:1:1] | passed | 0.00037 seconds |
12
- ./spec/mailshake/campaigns_spec.rb[1:1:2] | passed | 0.00034 seconds |
13
- ./spec/mailshake/campaigns_spec.rb[1:2:1] | passed | 0.00033 seconds |
14
- ./spec/mailshake/campaigns_spec.rb[1:3:1] | passed | 0.00036 seconds |
15
- ./spec/mailshake/campaigns_spec.rb[1:4:1] | passed | 0.00029 seconds |
16
- ./spec/mailshake/campaigns_spec.rb[1:5:1] | passed | 0.00029 seconds |
17
- ./spec/mailshake/campaigns_spec.rb[1:6:1] | passed | 0.00033 seconds |
18
- ./spec/mailshake/campaigns_spec.rb[1:7:1] | passed | 0.00033 seconds |
19
- ./spec/mailshake/client_spec.rb[1:1:1] | passed | 0.00031 seconds |
20
- ./spec/mailshake/client_spec.rb[1:1:2:1] | passed | 0.00054 seconds |
21
- ./spec/mailshake/client_spec.rb[1:2:1] | passed | 0.00022 seconds |
22
- ./spec/mailshake/client_spec.rb[1:2:2] | passed | 0.002 seconds |
23
- ./spec/mailshake/client_spec.rb[1:2:3] | passed | 0.00041 seconds |
24
- ./spec/mailshake/client_spec.rb[1:2:4] | passed | 0.00045 seconds |
25
- ./spec/mailshake/client_spec.rb[1:3:1] | passed | 0.00039 seconds |
26
- ./spec/mailshake/client_spec.rb[1:4:1] | passed | 0.00029 seconds |
27
- ./spec/mailshake/client_spec.rb[1:5:1] | passed | 0.00038 seconds |
28
- ./spec/mailshake/client_spec.rb[1:5:2] | passed | 0.00043 seconds |
29
- ./spec/mailshake/client_spec.rb[1:5:3] | passed | 0.00038 seconds |
30
- ./spec/mailshake/client_spec.rb[1:5:4] | passed | 0.00042 seconds |
31
- ./spec/mailshake/client_spec.rb[1:5:5] | passed | 0.00064 seconds |
32
- ./spec/mailshake/configuration_spec.rb[1:1:1] | passed | 0.00004 seconds |
33
- ./spec/mailshake/configuration_spec.rb[1:1:2] | passed | 0.00004 seconds |
34
- ./spec/mailshake/configuration_spec.rb[1:2:1] | passed | 0.00004 seconds |
35
- ./spec/mailshake/configuration_spec.rb[1:2:2] | passed | 0.00004 seconds |
36
- ./spec/mailshake/configuration_spec.rb[1:2:3] | passed | 0.00005 seconds |
37
- ./spec/mailshake/configuration_spec.rb[1:3:1] | passed | 0.00005 seconds |
38
- ./spec/mailshake/configuration_spec.rb[1:3:2] | passed | 0.00004 seconds |
39
- ./spec/mailshake/leads_spec.rb[1:1:1] | passed | 0.00037 seconds |
40
- ./spec/mailshake/leads_spec.rb[1:1:2] | passed | 0.00043 seconds |
41
- ./spec/mailshake/leads_spec.rb[1:2:1] | passed | 0.00047 seconds |
42
- ./spec/mailshake/leads_spec.rb[1:3:1] | passed | 0.00039 seconds |
43
- ./spec/mailshake/leads_spec.rb[1:4:1] | passed | 0.00035 seconds |
44
- ./spec/mailshake/leads_spec.rb[1:5:1] | passed | 0.00037 seconds |
45
- ./spec/mailshake/leads_spec.rb[1:6:1] | passed | 0.00041 seconds |
46
- ./spec/mailshake/push_spec.rb[1:1:1] | passed | 0.00183 seconds |
47
- ./spec/mailshake/push_spec.rb[1:1:2] | passed | 0.00026 seconds |
48
- ./spec/mailshake/push_spec.rb[1:2:1] | passed | 0.00032 seconds |
49
- ./spec/mailshake/recipients_spec.rb[1:1:1] | passed | 0.00035 seconds |
50
- ./spec/mailshake/recipients_spec.rb[1:2:1] | passed | 0.00074 seconds |
51
- ./spec/mailshake/recipients_spec.rb[1:3:1] | passed | 0.0005 seconds |
52
- ./spec/mailshake/recipients_spec.rb[1:3:2] | passed | 0.00049 seconds |
53
- ./spec/mailshake/recipients_spec.rb[1:4:1] | passed | 0.00112 seconds |
54
- ./spec/mailshake/recipients_spec.rb[1:5:1] | passed | 0.01614 seconds |
55
- ./spec/mailshake/recipients_spec.rb[1:6:1] | passed | 0.00037 seconds |
56
- ./spec/mailshake/recipients_spec.rb[1:7:1] | passed | 0.00035 seconds |
57
- ./spec/mailshake/senders_spec.rb[1:1:1] | passed | 0.00027 seconds |
58
- ./spec/mailshake/senders_spec.rb[1:1:2] | passed | 0.00054 seconds |
59
- ./spec/mailshake/team_spec.rb[1:1:1] | passed | 0.0003 seconds |
60
- ./spec/mailshake/team_spec.rb[1:1:2] | passed | 0.00039 seconds |
1
+ example_id | status | run_time |
2
+ ------------------------------------------------- | ------ | --------------- |
3
+ ./spec/mailshake/activity_spec.rb[1:1:1] | passed | 0.00034 seconds |
4
+ ./spec/mailshake/activity_spec.rb[1:1:2] | passed | 0.00045 seconds |
5
+ ./spec/mailshake/activity_spec.rb[1:2:1] | passed | 0.00038 seconds |
6
+ ./spec/mailshake/activity_spec.rb[1:3:1] | passed | 0.00043 seconds |
7
+ ./spec/mailshake/activity_spec.rb[1:4:1] | passed | 0.00041 seconds |
8
+ ./spec/mailshake/activity_spec.rb[1:5:1] | passed | 0.00043 seconds |
9
+ ./spec/mailshake/activity_spec.rb[1:6:1] | passed | 0.00043 seconds |
10
+ ./spec/mailshake/activity_spec.rb[1:7:1] | passed | 0.00044 seconds |
11
+ ./spec/mailshake/campaigns_spec.rb[1:1:1] | passed | 0.0004 seconds |
12
+ ./spec/mailshake/campaigns_spec.rb[1:1:2] | passed | 0.00053 seconds |
13
+ ./spec/mailshake/campaigns_spec.rb[1:2:1] | passed | 0.0006 seconds |
14
+ ./spec/mailshake/campaigns_spec.rb[1:3:1] | passed | 0.00033 seconds |
15
+ ./spec/mailshake/campaigns_spec.rb[1:4:1] | passed | 0.0006 seconds |
16
+ ./spec/mailshake/campaigns_spec.rb[1:5:1] | passed | 0.00037 seconds |
17
+ ./spec/mailshake/campaigns_spec.rb[1:6:1] | passed | 0.00035 seconds |
18
+ ./spec/mailshake/campaigns_spec.rb[1:7:1] | passed | 0.00059 seconds |
19
+ ./spec/mailshake/client_spec.rb[1:1:1] | passed | 0.00004 seconds |
20
+ ./spec/mailshake/client_spec.rb[1:1:2:1] | passed | 0.00005 seconds |
21
+ ./spec/mailshake/client_spec.rb[1:2:1] | passed | 0.00028 seconds |
22
+ ./spec/mailshake/client_spec.rb[1:2:2] | passed | 0.00033 seconds |
23
+ ./spec/mailshake/client_spec.rb[1:2:3] | passed | 0.0003 seconds |
24
+ ./spec/mailshake/client_spec.rb[1:2:4] | passed | 0.00042 seconds |
25
+ ./spec/mailshake/client_spec.rb[1:3:1] | passed | 0.00049 seconds |
26
+ ./spec/mailshake/client_spec.rb[1:4:1] | passed | 0.00066 seconds |
27
+ ./spec/mailshake/client_spec.rb[1:5:1] | passed | 0.00031 seconds |
28
+ ./spec/mailshake/client_spec.rb[1:5:2] | passed | 0.00031 seconds |
29
+ ./spec/mailshake/client_spec.rb[1:5:3] | passed | 0.00213 seconds |
30
+ ./spec/mailshake/client_spec.rb[1:5:4] | passed | 0.00108 seconds |
31
+ ./spec/mailshake/client_spec.rb[1:5:5] | passed | 0.00031 seconds |
32
+ ./spec/mailshake/client_spec.rb[1:5:6] | passed | 0.00037 seconds |
33
+ ./spec/mailshake/client_spec.rb[1:5:7] | passed | 0.00028 seconds |
34
+ ./spec/mailshake/client_spec.rb[1:5:8] | passed | 0.00039 seconds |
35
+ ./spec/mailshake/configuration_spec.rb[1:1:1] | passed | 0.00003 seconds |
36
+ ./spec/mailshake/configuration_spec.rb[1:1:2] | passed | 0.00003 seconds |
37
+ ./spec/mailshake/configuration_spec.rb[1:2:1] | passed | 0.00003 seconds |
38
+ ./spec/mailshake/configuration_spec.rb[1:2:2] | passed | 0.00003 seconds |
39
+ ./spec/mailshake/configuration_spec.rb[1:2:3] | passed | 0.00003 seconds |
40
+ ./spec/mailshake/configuration_spec.rb[1:3:1] | passed | 0.00003 seconds |
41
+ ./spec/mailshake/configuration_spec.rb[1:3:2] | passed | 0.00003 seconds |
42
+ ./spec/mailshake/leads_spec.rb[1:1:1] | passed | 0.00041 seconds |
43
+ ./spec/mailshake/leads_spec.rb[1:1:2] | passed | 0.00043 seconds |
44
+ ./spec/mailshake/leads_spec.rb[1:2:1] | passed | 0.00209 seconds |
45
+ ./spec/mailshake/leads_spec.rb[1:3:1] | passed | 0.00039 seconds |
46
+ ./spec/mailshake/leads_spec.rb[1:4:1] | passed | 0.01393 seconds |
47
+ ./spec/mailshake/leads_spec.rb[1:5:1] | passed | 0.00032 seconds |
48
+ ./spec/mailshake/leads_spec.rb[1:6:1] | passed | 0.00031 seconds |
49
+ ./spec/mailshake/models/base_model_spec.rb[1:1:1] | passed | 0.00005 seconds |
50
+ ./spec/mailshake/models/base_model_spec.rb[1:1:2] | passed | 0.00004 seconds |
51
+ ./spec/mailshake/models/base_model_spec.rb[1:1:3] | passed | 0.00004 seconds |
52
+ ./spec/mailshake/models/base_model_spec.rb[1:2:1] | passed | 0.00004 seconds |
53
+ ./spec/mailshake/models/base_model_spec.rb[1:2:2] | passed | 0.00028 seconds |
54
+ ./spec/mailshake/models/base_model_spec.rb[1:3:1] | passed | 0.00045 seconds |
55
+ ./spec/mailshake/models/base_model_spec.rb[1:3:2] | passed | 0.00006 seconds |
56
+ ./spec/mailshake/models/base_model_spec.rb[1:3:3] | passed | 0.00048 seconds |
57
+ ./spec/mailshake/models/base_model_spec.rb[1:3:4] | passed | 0.00007 seconds |
58
+ ./spec/mailshake/models/base_model_spec.rb[1:4:1] | passed | 0.00004 seconds |
59
+ ./spec/mailshake/models/base_model_spec.rb[1:4:2] | passed | 0.00005 seconds |
60
+ ./spec/mailshake/models/base_model_spec.rb[1:5:1] | passed | 0.00004 seconds |
61
+ ./spec/mailshake/models/base_model_spec.rb[1:6:1] | passed | 0.00003 seconds |
62
+ ./spec/mailshake/models/base_model_spec.rb[1:6:2] | passed | 0.00003 seconds |
63
+ ./spec/mailshake/models/list_spec.rb[1:1:1] | passed | 0.00006 seconds |
64
+ ./spec/mailshake/models/list_spec.rb[1:1:2] | passed | 0.00007 seconds |
65
+ ./spec/mailshake/models/list_spec.rb[1:1:3] | passed | 0.00004 seconds |
66
+ ./spec/mailshake/models/list_spec.rb[1:2:1] | passed | 0.00004 seconds |
67
+ ./spec/mailshake/models/list_spec.rb[1:2:2] | passed | 0.00004 seconds |
68
+ ./spec/mailshake/models/list_spec.rb[1:3:1] | passed | 0.00005 seconds |
69
+ ./spec/mailshake/models/list_spec.rb[1:3:2] | passed | 0.00006 seconds |
70
+ ./spec/mailshake/models/list_spec.rb[1:3:3] | passed | 0.00005 seconds |
71
+ ./spec/mailshake/models/list_spec.rb[1:3:4] | passed | 0.00007 seconds |
72
+ ./spec/mailshake/models/list_spec.rb[1:3:5] | passed | 0.00005 seconds |
73
+ ./spec/mailshake/models/list_spec.rb[1:4:1] | passed | 0.00005 seconds |
74
+ ./spec/mailshake/models/list_spec.rb[1:5:1] | passed | 0.00006 seconds |
75
+ ./spec/mailshake/push_spec.rb[1:1:1] | passed | 0.00036 seconds |
76
+ ./spec/mailshake/push_spec.rb[1:1:2] | passed | 0.00032 seconds |
77
+ ./spec/mailshake/push_spec.rb[1:2:1] | passed | 0.00138 seconds |
78
+ ./spec/mailshake/recipients_spec.rb[1:1:1] | passed | 0.00052 seconds |
79
+ ./spec/mailshake/recipients_spec.rb[1:2:1] | passed | 0.00036 seconds |
80
+ ./spec/mailshake/recipients_spec.rb[1:3:1] | passed | 0.00127 seconds |
81
+ ./spec/mailshake/recipients_spec.rb[1:3:2] | passed | 0.00145 seconds |
82
+ ./spec/mailshake/recipients_spec.rb[1:4:1] | passed | 0.00167 seconds |
83
+ ./spec/mailshake/recipients_spec.rb[1:5:1] | passed | 0.00124 seconds |
84
+ ./spec/mailshake/recipients_spec.rb[1:6:1] | passed | 0.00138 seconds |
85
+ ./spec/mailshake/recipients_spec.rb[1:7:1] | passed | 0.00047 seconds |
86
+ ./spec/mailshake/senders_spec.rb[1:1:1] | passed | 0.00043 seconds |
87
+ ./spec/mailshake/senders_spec.rb[1:1:2] | passed | 0.00044 seconds |
88
+ ./spec/mailshake/team_spec.rb[1:1:1] | passed | 0.00031 seconds |
89
+ ./spec/mailshake/team_spec.rb[1:1:2] | passed | 0.00051 seconds |
@@ -13,7 +13,8 @@ RSpec.describe Mailshake::Activity do
13
13
  headers: { "Content-Type" => "application/json" })
14
14
 
15
15
  result = activity.sent
16
- expect(result["results"]).to eq([])
16
+ expect(result).to be_a(Mailshake::Models::List)
17
+ expect(result.results).to eq([])
17
18
  end
18
19
 
19
20
  it "passes campaign and pagination params" do
@@ -34,7 +35,8 @@ RSpec.describe Mailshake::Activity do
34
35
  headers: { "Content-Type" => "application/json" })
35
36
 
36
37
  result = activity.opens(campaign_id: "1", exclude_duplicates: "true")
37
- expect(result["results"]).to eq([])
38
+ expect(result).to be_a(Mailshake::Models::List)
39
+ expect(result.results).to eq([])
38
40
  end
39
41
  end
40
42
 
@@ -13,7 +13,8 @@ RSpec.describe Mailshake::Campaigns do
13
13
  headers: { "Content-Type" => "application/json" })
14
14
 
15
15
  result = campaigns.list
16
- expect(result["results"]).to eq([])
16
+ expect(result).to be_a(Mailshake::Models::List)
17
+ expect(result.results).to eq([])
17
18
  end
18
19
 
19
20
  it "passes search and pagination params" do
@@ -34,7 +35,8 @@ RSpec.describe Mailshake::Campaigns do
34
35
  headers: { "Content-Type" => "application/json" })
35
36
 
36
37
  result = campaigns.get(campaign_id: "1")
37
- expect(result["title"]).to eq("Test Campaign")
38
+ expect(result).to be_a(Mailshake::Models::Campaign)
39
+ expect(result.title).to eq("Test Campaign")
38
40
  end
39
41
  end
40
42
 
@@ -46,7 +48,8 @@ RSpec.describe Mailshake::Campaigns do
46
48
  headers: { "Content-Type" => "application/json" })
47
49
 
48
50
  result = campaigns.create(title: "New Campaign", sender_id: 5)
49
- expect(result["title"]).to eq("New Campaign")
51
+ expect(result).to be_a(Mailshake::Models::Campaign)
52
+ expect(result.title).to eq("New Campaign")
50
53
  end
51
54
  end
52
55
 
@@ -80,6 +83,7 @@ RSpec.describe Mailshake::Campaigns do
80
83
  headers: { "Content-Type" => "application/json" })
81
84
 
82
85
  result = campaigns.export(campaign_ids: [1, 2], export_type: "csv", timezone: "US/Pacific")
86
+ expect(result).to be_a(Mailshake::Models::CampaignExportRequest)
83
87
  expect(result["statusID"]).to eq(42)
84
88
  end
85
89
  end
@@ -92,7 +96,9 @@ RSpec.describe Mailshake::Campaigns do
92
96
  headers: { "Content-Type" => "application/json" })
93
97
 
94
98
  result = campaigns.export_status(status_id: "42")
95
- expect(result["isFinished"]).to be true
99
+ expect(result).to be_a(Mailshake::Models::CampaignExport)
100
+ expect(result.is_finished).to be true
101
+ expect(result.csv_download_url).to eq("https://example.com/file.csv")
96
102
  end
97
103
  end
98
104
  end
@@ -80,6 +80,7 @@ RSpec.describe Mailshake::Client do
80
80
  headers: { "Content-Type" => "application/json" })
81
81
 
82
82
  result = client.me
83
+ expect(result).to be_a(Mailshake::Models::User)
83
84
  expect(result["email"]).to eq("test@example.com")
84
85
  end
85
86
  end
@@ -128,5 +129,58 @@ RSpec.describe Mailshake::Client do
128
129
 
129
130
  expect { client.get("/error") }.to raise_error(Mailshake::APIError)
130
131
  end
132
+
133
+ it "raises LimitReachedError when response contains limit_reached error code" do
134
+ stub_request(:get, "#{base_url}/quota")
135
+ .to_return(
136
+ status: 200,
137
+ body: {
138
+ error: "limit_reached",
139
+ message: "Please wait and try again after: 2026-04-02T16:00:00Z",
140
+ retryAfter: "2026-04-02T16:00:00Z"
141
+ }.to_json,
142
+ headers: { "Content-Type" => "application/json" }
143
+ )
144
+
145
+ expect { client.get("/quota") }.to raise_error(Mailshake::LimitReachedError) do |error|
146
+ expect(error.retry_after).to eq("2026-04-02T16:00:00Z")
147
+ expect(error.message).to eq("Please wait and try again after: 2026-04-02T16:00:00Z")
148
+ expect(error.status_code).to eq(200)
149
+ end
150
+ end
151
+
152
+ it "raises LimitReachedError when errorCode field is used" do
153
+ stub_request(:post, "#{base_url}/quota")
154
+ .to_return(
155
+ status: 400,
156
+ body: {
157
+ errorCode: "limit_reached",
158
+ message: "Quota exceeded",
159
+ retryAfter: "2026-04-02T17:00:00Z"
160
+ }.to_json,
161
+ headers: { "Content-Type" => "application/json" }
162
+ )
163
+
164
+ expect { client.post("/quota", {}) }.to raise_error(Mailshake::LimitReachedError) do |error|
165
+ expect(error.retry_after).to eq("2026-04-02T17:00:00Z")
166
+ end
167
+ end
168
+
169
+ it "raises MonthlyRecipientLimitError when response contains exceeds_monthly_recipients" do
170
+ stub_request(:post, "#{base_url}/recipients")
171
+ .to_return(
172
+ status: 400,
173
+ body: {
174
+ error: "exceeds_monthly_recipients",
175
+ message: "You have exceeded your monthly recipient limit"
176
+ }.to_json,
177
+ headers: { "Content-Type" => "application/json" }
178
+ )
179
+
180
+ expect { client.post("/recipients", {}) }.to raise_error(Mailshake::MonthlyRecipientLimitError) do |error|
181
+ expect(error.message).to eq("You have exceeded your monthly recipient limit")
182
+ expect(error.status_code).to eq(400)
183
+ end
184
+ end
131
185
  end
132
186
  end
@@ -13,7 +13,8 @@ RSpec.describe Mailshake::Leads do
13
13
  headers: { "Content-Type" => "application/json" })
14
14
 
15
15
  result = leads.list
16
- expect(result["results"]).to eq([])
16
+ expect(result).to be_a(Mailshake::Models::List)
17
+ expect(result.results).to eq([])
17
18
  end
18
19
 
19
20
  it "passes filter params" do
@@ -34,7 +35,8 @@ RSpec.describe Mailshake::Leads do
34
35
  headers: { "Content-Type" => "application/json" })
35
36
 
36
37
  result = leads.get(lead_id: "42")
37
- expect(result["status"]).to eq("open")
38
+ expect(result).to be_a(Mailshake::Models::Lead)
39
+ expect(result.status).to eq("open")
38
40
  end
39
41
  end
40
42
 
@@ -46,6 +48,7 @@ RSpec.describe Mailshake::Leads do
46
48
  headers: { "Content-Type" => "application/json" })
47
49
 
48
50
  result = leads.create(campaign_id: 1, email_addresses: ["john@example.com"])
51
+ expect(result).to be_a(Mailshake::Models::CreatedLeads)
49
52
  expect(result["results"].length).to eq(1)
50
53
  end
51
54
  end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe Mailshake::Models::BaseModel do
6
+ describe "creating from hash with camelCase keys" do
7
+ it "creates a model from a hash" do
8
+ model = described_class.new("firstName" => "John", "lastName" => "Doe")
9
+ expect(model.first_name).to eq("John")
10
+ expect(model.last_name).to eq("Doe")
11
+ end
12
+
13
+ it "handles single-word keys" do
14
+ model = described_class.new("id" => 42, "title" => "Test")
15
+ expect(model.id).to eq(42)
16
+ expect(model.title).to eq("Test")
17
+ end
18
+
19
+ it "handles keys with multiple capitals" do
20
+ model = described_class.new("emailAddress" => "test@example.com", "teamID" => 5)
21
+ expect(model.email_address).to eq("test@example.com")
22
+ expect(model.team_id).to eq(5)
23
+ end
24
+ end
25
+
26
+ describe "snake_case accessor methods" do
27
+ it "defines accessor methods for all keys" do
28
+ model = described_class.new("isArchived" => true, "createdDate" => "2026-01-01")
29
+ expect(model.is_archived).to be true
30
+ expect(model.created_date).to eq("2026-01-01")
31
+ end
32
+
33
+ it "responds to accessor methods" do
34
+ model = described_class.new("firstName" => "John")
35
+ expect(model.respond_to?(:first_name)).to be true
36
+ expect(model.respond_to?(:nonexistent)).to be false
37
+ end
38
+ end
39
+
40
+ describe "nested model hydration" do
41
+ it "hydrates has_one associations" do
42
+ campaign = Mailshake::Models::Campaign.new(
43
+ "id" => 1,
44
+ "title" => "Test",
45
+ "sender" => { "id" => 10, "emailAddress" => "sender@example.com" }
46
+ )
47
+ expect(campaign.sender).to be_a(Mailshake::Models::Sender)
48
+ expect(campaign.sender.id).to eq(10)
49
+ expect(campaign.sender.email_address).to eq("sender@example.com")
50
+ end
51
+
52
+ it "hydrates has_many associations" do
53
+ campaign = Mailshake::Models::Campaign.new(
54
+ "id" => 1,
55
+ "title" => "Test",
56
+ "messages" => [
57
+ { "id" => 1, "subject" => "Hello" },
58
+ { "id" => 2, "subject" => "Follow up" }
59
+ ]
60
+ )
61
+ expect(campaign.messages).to be_an(Array)
62
+ expect(campaign.messages.length).to eq(2)
63
+ expect(campaign.messages.first).to be_a(Mailshake::Models::Message)
64
+ expect(campaign.messages.first.subject).to eq("Hello")
65
+ end
66
+
67
+ it "handles nil nested values" do
68
+ campaign = Mailshake::Models::Campaign.new("id" => 1, "sender" => nil)
69
+ expect(campaign.sender).to be_nil
70
+ end
71
+
72
+ it "handles deeply nested models" do
73
+ lead = Mailshake::Models::Lead.new(
74
+ "id" => 1,
75
+ "campaign" => {
76
+ "id" => 10,
77
+ "sender" => { "id" => 20, "emailAddress" => "sender@example.com" }
78
+ }
79
+ )
80
+ expect(lead.campaign).to be_a(Mailshake::Models::Campaign)
81
+ expect(lead.campaign.sender).to be_a(Mailshake::Models::Sender)
82
+ expect(lead.campaign.sender.email_address).to eq("sender@example.com")
83
+ end
84
+ end
85
+
86
+ describe "[] backwards compatibility" do
87
+ it "accesses original hash keys via []" do
88
+ model = described_class.new("firstName" => "John", "emailAddress" => "john@example.com")
89
+ expect(model["firstName"]).to eq("John")
90
+ expect(model["emailAddress"]).to eq("john@example.com")
91
+ end
92
+
93
+ it "returns nil for missing keys" do
94
+ model = described_class.new("id" => 1)
95
+ expect(model["missing"]).to be_nil
96
+ end
97
+ end
98
+
99
+ describe "#to_h" do
100
+ it "returns the original hash" do
101
+ data = { "id" => 1, "firstName" => "John", "emailAddress" => "john@example.com" }
102
+ model = described_class.new(data)
103
+ expect(model.to_h).to eq(data)
104
+ end
105
+ end
106
+
107
+ describe "with empty hash" do
108
+ it "handles empty hash" do
109
+ model = described_class.new({})
110
+ expect(model.to_h).to eq({})
111
+ end
112
+
113
+ it "handles nil" do
114
+ model = described_class.new(nil)
115
+ expect(model.to_h).to eq({})
116
+ end
117
+ end
118
+ end