heya 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1aedd0a6d25dc8185eacb59e5e279f6dc6c2135d3980005c1d5b5c82a351c403
4
- data.tar.gz: c6007415e758afe5d998ebbe1e9b8ae920e25e4c6abffa9da6c6a9fb1d713ae7
3
+ metadata.gz: 125b5f898dd3d7841fd0e50ae05d424ec56d2818bb011a43a083f224d15aa3ea
4
+ data.tar.gz: 23a1fd76278729817ab0317dff81ab816cabd9f09b323e8f723cb5c8f9186eff
5
5
  SHA512:
6
- metadata.gz: fce537c6eefe599dd9aa3b2be7fdb2c7ce0839c15a7e82256aaf168691103a57ee4e76b146d5cc8d91e7111b546ecbde3041e01e98ff59ac98ef74ce42935fb0
7
- data.tar.gz: bceb804f1400eccc44cbacd182007160bbec6b5595613224fff5792596b2661abeeb259bc36e7222bc043cf341754fa8cffb33462828836978fb980b75d5d453
6
+ metadata.gz: 22b4d72ffa9e69068ce5a77008b76047cf3f1379d330f233b0a4f174a6a89d3a91ab3353e1941b4b1262a4898316cdc1b14e3842468e4fd9bbffa301234c3e62
7
+ data.tar.gz: bc5c5604c1e1d95adb14fe4435817431c4af2f471a5df7d6bfd4ec100d6a5607cbd4121a31d66660cde7e9a892b84126bf6b6e2d15676afbd36af2fad958910e
@@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.2.0] - 2020-04-14
10
+ ### Added
11
+ - Added licensing.
12
+ - Added `rescue_from` (rescuable) in campaigns.
13
+
9
14
  ## [0.1.0] - 2020-03-19
10
15
  ### Added
11
16
  - Initial release. 👋
data/README.md CHANGED
@@ -1,30 +1,9 @@
1
1
  # Heya 👋
2
2
  ![Test](https://github.com/honeybadger-io/heya/workflows/Test/badge.svg)
3
+ [![Gem Version](https://badge.fury.io/rb/heya.svg)](https://badge.fury.io/rb/heya)
3
4
  [![Maintainability](https://api.codeclimate.com/v1/badges/a6416e63ffc426715857/maintainability)](https://codeclimate.com/github/honeybadger-io/heya/maintainability)
4
5
 
5
- Heya is a customer communication and automation framework for Ruby on Rails. It's as robust as the hosted alternatives, without the integration and compliance nightmare.
6
-
7
- ## Why Heya?
8
- Imagine onboarding and engaging your users with full access to their data, without maintaining a single 3rd-party integration or stressing over compliance. That's Heya: a new way to email your users.
9
-
10
- Heya lives inside your Rails app, giving you full access to your customer data with the power of Ruby and Rails for building automated email campaigns.
11
-
12
- If you've ever used 3rd-party email or marketing platforms, you've felt the pain of integrating, syncing customer data, and maintaining complex logic and rules--all of which is unnecessary in Heya.
13
-
14
- Email automation becomes startlingly simple when your application framework *is* your automation tool.
15
-
16
- ## Who is Heya for?
17
- - SaaS developers who are marketers at heart
18
- - SaaS marketers who are developers at heart
19
- - Development and marketing teams who work closely together
20
-
21
- ## Stuff we believe
22
- - You should avoid duplicating customer data
23
- - Segments should live as close to home as possible
24
- - So should automation
25
- - So should content
26
- - Code is for everyone, not just developers
27
- - Heya is the best way to engage your users
6
+ Heya is a campaign mailer for Rails. Think of it like ActionMailer, but for timed email sequences. It can also perform other actions like sending a text message.
28
7
 
29
8
  ## Getting started
30
9
  Getting started with Heya is easy:
@@ -21,5 +21,17 @@ module Heya
21
21
  template_name: step.name.underscore
22
22
  )
23
23
  end
24
+
25
+ protected
26
+
27
+ def _prefixes
28
+ @_prefixes_with_campaign_path ||= begin
29
+ if params.is_a?(Hash) && (campaign_name = params[:step]&.campaign&.name&.underscore)
30
+ super | ["heya/campaign_mailer/#{campaign_name}"]
31
+ else
32
+ super
33
+ end
34
+ end
35
+ end
24
36
  end
25
37
  end
@@ -3,6 +3,7 @@
3
3
  require "heya/version"
4
4
  require "heya/engine"
5
5
  require "heya/config"
6
+ require "heya/license"
6
7
  require "heya/campaigns/action"
7
8
  require "heya/campaigns/actions/email"
8
9
  require "heya/campaigns/actions/block"
@@ -45,4 +46,45 @@ module Heya
45
46
  return user.send(segment) if segment.is_a?(Symbol)
46
47
  segment.call(user)
47
48
  end
49
+
50
+ def verify_license!
51
+ unless File.file?(config.license_file)
52
+ puts(<<-NOTICE.strip_heredoc)
53
+ This copy of Heya is licensed for non-commercial non-profit, or 30-day trial usage only.
54
+ For a commercial use license, please visit https://www.heya.email
55
+ NOTICE
56
+ return
57
+ end
58
+
59
+ begin
60
+ license = License.import(File.read(config.license_file))
61
+ rescue License::ImportError
62
+ warn(<<-NOTICE.strip_heredoc)
63
+ Your Heya license is invalid.
64
+ If you need support, please visit https://www.heya.email
65
+ NOTICE
66
+ return
67
+ end
68
+
69
+ if license.expired?
70
+ warn(<<-NOTICE.strip_heredoc)
71
+ Your Heya license has expired.
72
+ To update your license, please visit https://www.heya.email
73
+ NOTICE
74
+ return
75
+ end
76
+
77
+ if (max_user_count = license.restrictions[:user_count]&.to_i)
78
+ user_count = config.user_type.constantize.count
79
+ if user_count > max_user_count
80
+ warn(<<-NOTICE.strip_heredoc)
81
+ Your app exceeds the number of users for your Heya license.
82
+ To upgrade your license, please visit https://www.heya.email
83
+ NOTICE
84
+ end
85
+ return # rubocop:disable Style/RedundantReturn
86
+ end
87
+
88
+ # Valid license
89
+ end
48
90
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/descendants_tracker"
4
+ require "active_support/rescuable"
4
5
 
5
6
  module Heya
6
7
  module Campaigns
@@ -11,6 +12,7 @@ module Heya
11
12
 
12
13
  include Singleton
13
14
  include GlobalID::Identification
15
+ include ActiveSupport::Rescuable
14
16
 
15
17
  def initialize
16
18
  self.steps = []
@@ -27,11 +29,21 @@ module Heya
27
29
  def add(user, restart: false, concurrent: false, send_now: true)
28
30
  return false unless Heya.in_segments?(user, *__segments)
29
31
 
30
- restart && CampaignReceipt
31
- .where(user: user, step_gid: steps.map(&:gid))
32
- .delete_all
32
+ membership = CampaignMembership.where(user: user, campaign_gid: gid)
33
+ if membership.exists?
34
+ return false unless restart
35
+ membership.delete_all
36
+ end
37
+
38
+ if restart
39
+ CampaignReceipt
40
+ .where(user: user, step_gid: steps.map(&:gid))
41
+ .delete_all
42
+ end
33
43
 
34
- CampaignMembership.where(user: user, campaign_gid: gid, concurrent: concurrent).first_or_create!
44
+ membership.create! do |m|
45
+ m.concurrent = concurrent
46
+ end
35
47
 
36
48
  if send_now && (step = steps.first) && step.wait <= 0
37
49
  Scheduler.process(self, step, user)
@@ -61,6 +73,10 @@ module Heya
61
73
  @user_class ||= self.class.user_type.constantize
62
74
  end
63
75
 
76
+ def handle_exception(exception)
77
+ rescue_with_handler(exception) || raise(exception)
78
+ end
79
+
64
80
  attr_accessor :steps
65
81
 
66
82
  private
@@ -89,11 +105,7 @@ module Heya
89
105
  instance
90
106
  end
91
107
 
92
- def handle_exception(exception)
93
- raise exception
94
- end
95
-
96
- delegate :steps, :add, :remove, :users, :gid, :user_class, to: :instance
108
+ delegate :steps, :add, :remove, :users, :gid, :user_class, :handle_exception, to: :instance
97
109
 
98
110
  def default(**params)
99
111
  self.__defaults = __defaults.merge(params).freeze
@@ -6,6 +6,7 @@ module Heya
6
6
  class Config < OpenStruct
7
7
  def initialize
8
8
  super(
9
+ license_file: nil,
9
10
  user_type: "User",
10
11
  campaigns: OpenStruct.new(
11
12
  priority: [],
@@ -15,5 +15,16 @@ module Heya
15
15
  require_dependency(c)
16
16
  end
17
17
  end
18
+
19
+ config.after_initialize do
20
+ license_key = File.expand_path("../../../license_key.pub", __FILE__)
21
+ License.encryption_key = File.read(license_key) if File.file?(license_key)
22
+
23
+ Heya.configure do |config|
24
+ config.license_file ||= Rails.root.join("config/Heya.heya-license")
25
+ end
26
+
27
+ Heya.verify_license!
28
+ end
18
29
  end
19
30
  end
@@ -0,0 +1,179 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The MIT License (MIT)
4
+ #
5
+ # Copyright (c) 2015 GitLab B.V.
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in
15
+ # all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ # THE SOFTWARE.
24
+
25
+ require "openssl"
26
+ require "date"
27
+ require "json"
28
+ require "base64"
29
+
30
+ require "heya/license/encryptor"
31
+ require "heya/license/boundary"
32
+
33
+ module Heya
34
+ class License
35
+ class Error < StandardError; end
36
+ class ImportError < Error; end
37
+ class ValidationError < Error; end
38
+
39
+ class << self
40
+ attr_reader :encryption_key
41
+ @encryption_key = nil
42
+
43
+ def encryption_key=(key)
44
+ key = OpenSSL::PKey::RSA.new(key.to_s) unless key&.is_a?(OpenSSL::PKey::RSA)
45
+ @encryption_key = key
46
+ @encryptor = nil
47
+ end
48
+
49
+ def encryptor
50
+ @encryptor ||= Encryptor.new(encryption_key)
51
+ end
52
+
53
+ def import(data)
54
+ if data.nil?
55
+ raise ImportError, "No license data."
56
+ end
57
+
58
+ data = Boundary.remove_boundary(data)
59
+
60
+ begin
61
+ license_json = encryptor.decrypt(data)
62
+ rescue Encryptor::Error
63
+ raise ImportError, "License data could not be decrypted."
64
+ end
65
+
66
+ begin
67
+ attributes = JSON.parse(license_json)
68
+ rescue JSON::ParseError
69
+ raise ImportError, "License data is invalid JSON."
70
+ end
71
+
72
+ new(attributes)
73
+ end
74
+ end
75
+
76
+ attr_reader :version
77
+ attr_accessor :licensee, :starts_at, :expires_at
78
+ attr_accessor :restrictions
79
+
80
+ def initialize(attributes = {})
81
+ load_attributes(attributes)
82
+ end
83
+
84
+ def valid?
85
+ return false if !licensee || !licensee.is_a?(Hash) || licensee.length == 0
86
+ return false if !starts_at || !starts_at.is_a?(Date)
87
+ return false if expires_at && !expires_at.is_a?(Date)
88
+ return false if restrictions && !restrictions.is_a?(Hash)
89
+
90
+ true
91
+ end
92
+
93
+ def validate!
94
+ raise ValidationError, "License is invalid" unless valid?
95
+ end
96
+
97
+ def will_expire?
98
+ expires_at
99
+ end
100
+
101
+ def expired?
102
+ will_expire? && Date.today >= expires_at
103
+ end
104
+
105
+ def restricted?(key = nil)
106
+ if key
107
+ restricted? && restrictions.has_key?(key)
108
+ else
109
+ restrictions && restrictions.length >= 1
110
+ end
111
+ end
112
+
113
+ def attributes
114
+ hash = {}
115
+
116
+ hash["version"] = version
117
+ hash["licensee"] = licensee
118
+
119
+ hash["starts_at"] = starts_at
120
+ hash["expires_at"] = expires_at if will_expire?
121
+
122
+ hash["restrictions"] = restrictions if restricted?
123
+
124
+ hash
125
+ end
126
+
127
+ def to_json
128
+ JSON.dump(attributes)
129
+ end
130
+
131
+ def export(boundary: nil)
132
+ validate!
133
+
134
+ data = self.class.encryptor.encrypt(to_json)
135
+
136
+ if boundary
137
+ data = Boundary.add_boundary(data, boundary)
138
+ end
139
+
140
+ data
141
+ end
142
+
143
+ private
144
+
145
+ def load_attributes(attributes)
146
+ attributes = Hash[attributes.map { |k, v| [k.to_s, v] }]
147
+
148
+ version = attributes["version"] || 1
149
+ unless version && version == 1
150
+ raise ArgumentError, "Version is too new"
151
+ end
152
+
153
+ @version = version
154
+
155
+ @licensee = attributes["licensee"]
156
+
157
+ %w[starts_at expires_at].each do |attr|
158
+ value = attributes[attr]
159
+ if value.is_a?(String)
160
+ value = begin
161
+ Date.parse(value)
162
+ rescue
163
+ nil
164
+ end
165
+ end
166
+
167
+ next unless value
168
+
169
+ send("#{attr}=", value)
170
+ end
171
+
172
+ restrictions = attributes["restrictions"]
173
+ if restrictions&.is_a?(Hash)
174
+ restrictions = Hash[restrictions.map { |k, v| [k.to_sym, v] }]
175
+ @restrictions = restrictions
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The MIT License (MIT)
4
+ #
5
+ # Copyright (c) 2015 GitLab B.V.
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in
15
+ # all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ # THE SOFTWARE.
24
+
25
+ module Heya
26
+ class License
27
+ module Boundary
28
+ BOUNDARY_START = /(\A|\r?\n)-*BEGIN .+? LICENSE-*\r?\n/.freeze
29
+ BOUNDARY_END = /\r?\n-*END .+? LICENSE-*(\r?\n|\z)/.freeze
30
+
31
+ class << self
32
+ def add_boundary(data, product_name)
33
+ data = remove_boundary(data)
34
+
35
+ product_name.upcase!
36
+
37
+ pad = lambda do |message, width|
38
+ total_padding = [width - message.length, 0].max
39
+
40
+ padding = total_padding / 2.0
41
+ [
42
+ "-" * padding.ceil,
43
+ message,
44
+ "-" * padding.floor
45
+ ].join
46
+ end
47
+
48
+ [
49
+ pad.call("BEGIN #{product_name} LICENSE", 60),
50
+ data.strip,
51
+ pad.call("END #{product_name} LICENSE", 60)
52
+ ].join("\n")
53
+ end
54
+
55
+ def remove_boundary(data)
56
+ after_boundary = data.split(BOUNDARY_START).last
57
+ in_boundary = after_boundary.split(BOUNDARY_END).first
58
+
59
+ in_boundary
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The MIT License (MIT)
4
+ #
5
+ # Copyright (c) 2015 GitLab B.V.
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in
15
+ # all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ # THE SOFTWARE.
24
+
25
+ module Heya
26
+ class License
27
+ class Encryptor
28
+ class Error < StandardError; end
29
+ class KeyError < Error; end
30
+ class DecryptionError < Error; end
31
+
32
+ attr_accessor :key
33
+
34
+ def initialize(key)
35
+ if key && !key.is_a?(OpenSSL::PKey::RSA)
36
+ raise KeyError, "No RSA encryption key provided."
37
+ end
38
+
39
+ @key = key
40
+ end
41
+
42
+ def encrypt(data)
43
+ unless key.private?
44
+ raise KeyError, "Provided key is not a private key."
45
+ end
46
+
47
+ # Encrypt the data using symmetric AES encryption.
48
+ cipher = OpenSSL::Cipher::AES128.new(:CBC)
49
+ cipher.encrypt
50
+ aes_key = cipher.random_key
51
+ aes_iv = cipher.random_iv
52
+
53
+ encrypted_data = cipher.update(data) + cipher.final
54
+
55
+ # Encrypt the AES key using asymmetric RSA encryption.
56
+ encrypted_key = key.private_encrypt(aes_key)
57
+
58
+ encryption_data = {
59
+ "data" => Base64.encode64(encrypted_data),
60
+ "key" => Base64.encode64(encrypted_key),
61
+ "iv" => Base64.encode64(aes_iv)
62
+ }
63
+
64
+ json_data = JSON.dump(encryption_data)
65
+ Base64.encode64(json_data)
66
+ end
67
+
68
+ def decrypt(data)
69
+ unless key.public?
70
+ raise KeyError, "Provided key is not a public key."
71
+ end
72
+
73
+ json_data = Base64.decode64(data.chomp)
74
+
75
+ begin
76
+ encryption_data = JSON.parse(json_data)
77
+ rescue JSON::ParserError
78
+ raise DecryptionError, "Encryption data is invalid JSON."
79
+ end
80
+
81
+ unless %w[data key iv].all? { |key| encryption_data[key] }
82
+ raise DecryptionError, "Required field missing from encryption data."
83
+ end
84
+
85
+ encrypted_data = Base64.decode64(encryption_data["data"])
86
+ encrypted_key = Base64.decode64(encryption_data["key"])
87
+ aes_iv = Base64.decode64(encryption_data["iv"])
88
+
89
+ begin
90
+ # Decrypt the AES key using asymmetric RSA encryption.
91
+ aes_key = self.key.public_decrypt(encrypted_key)
92
+ rescue OpenSSL::PKey::RSAError
93
+ raise DecryptionError, "AES encryption key could not be decrypted."
94
+ end
95
+
96
+ # Decrypt the data using symmetric AES encryption.
97
+ cipher = OpenSSL::Cipher::AES128.new(:CBC)
98
+ cipher.decrypt
99
+
100
+ begin
101
+ cipher.key = aes_key
102
+ rescue OpenSSL::Cipher::CipherError
103
+ raise DecryptionError, "AES encryption key is invalid."
104
+ end
105
+
106
+ begin
107
+ cipher.iv = aes_iv
108
+ rescue OpenSSL::Cipher::CipherError
109
+ raise DecryptionError, "AES IV is invalid."
110
+ end
111
+
112
+ begin
113
+ data = cipher.update(encrypted_data) + cipher.final
114
+ rescue OpenSSL::Cipher::CipherError
115
+ raise DecryptionError, "Data could not be decrypted."
116
+ end
117
+
118
+ data
119
+ end
120
+ end
121
+ end
122
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Heya
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -0,0 +1,9 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAycgMy1EVcD50WlDhfMDq
3
+ 0wvLZb1iF1Hes5IU37QZeswq4qy5MZKBx8669Jy04QH3RnrY0XyO6c1bnr2rnv8M
4
+ NZL8M0ibGpG0NWv0O2tzaVb7706Vm/VuQGR9BuFSe8ZlKRyvMJpk1Kl8NKeRl+iu
5
+ KMN7AK48/b7/stB8P5NVb/8LCOUfIkcOZcTZN9YJvXbyq5i/i2i2IRgxib0zmkU3
6
+ yCOVZnvW0spaH+Z6SuXTZ/wefXM9XRCpshuEvBnS8vihcyQioFkswF2An4FL8B+N
7
+ AWaeA3NcNJEiUFsCpp0uUOVqVZ2avhpATB7fAG4Qh3JauajhWBrqSJYZn2W6uyDp
8
+ fQIDAQAB
9
+ -----END PUBLIC KEY-----
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: heya
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Wood
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-20 00:00:00.000000000 Z
11
+ date: 2020-04-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -108,8 +108,12 @@ files:
108
108
  - lib/heya/campaigns/step_action_job.rb
109
109
  - lib/heya/config.rb
110
110
  - lib/heya/engine.rb
111
+ - lib/heya/license.rb
112
+ - lib/heya/license/boundary.rb
113
+ - lib/heya/license/encryptor.rb
111
114
  - lib/heya/version.rb
112
115
  - lib/tasks/heya_tasks.rake
116
+ - license_key.pub
113
117
  homepage: https://github.com/honeybadger-io/heya
114
118
  licenses:
115
119
  - Prosperity Public License
@@ -129,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
133
  - !ruby/object:Gem::Version
130
134
  version: '0'
131
135
  requirements: []
132
- rubygems_version: 3.0.3
136
+ rubygems_version: 3.1.2
133
137
  signing_key:
134
138
  specification_version: 4
135
139
  summary: "Heya \U0001F44B"