refcode 0.0.1 → 0.1.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 +4 -4
- data/README.md +27 -2
- data/lib/refcode.rb +2 -36
- data/lib/refcode/encodable.rb +47 -0
- data/lib/refcode/encoder.rb +31 -0
- data/lib/refcode/version.rb +1 -1
- data/test/refcode_test.rb +39 -0
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 420403fbddbdbbfe612045ab19119651a54f8740
|
4
|
+
data.tar.gz: 41307cc055dfbea7997fc7e66f20dffbcf94ff9a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3fd63ca01f994071ac81ab8545f1880433a661806d73aa81797aeb83fa4763ea7f636777926f823b2df1115df90a3ab463c0c6a74424cceef958274d2095284
|
7
|
+
data.tar.gz: 172f2f19bc9a3d6f02c75adc665bda99f4a12f5c6edbf825e4c4845cfd42de3fc88e9da06f215fbcfa862b54daa58a640bddf6eb4d50b291988e4a11dc2c270f
|
data/README.md
CHANGED
@@ -5,7 +5,9 @@ URL-safe strings. The strings can be simply decrypted later to be used by your
|
|
5
5
|
application.
|
6
6
|
|
7
7
|
Refcode was conceived as a way to attach referral data to tracking links. Encryption
|
8
|
-
makes it unlikely that the links
|
8
|
+
makes it unlikely that the links will be tampered with. Still, it is not recommended
|
9
|
+
for critically sensitive applications! It was designed to power a simple referral
|
10
|
+
link tracking system.
|
9
11
|
|
10
12
|
## Implementation
|
11
13
|
|
@@ -31,6 +33,8 @@ Or install it yourself as:
|
|
31
33
|
|
32
34
|
## Usage
|
33
35
|
|
36
|
+
Refcode can be used in two ways. Most simply, you can use the `Refcode::Encoder` class directly.
|
37
|
+
|
34
38
|
encoder = Refcode::Encoder.new do |e|
|
35
39
|
e.secret = "a long crunchy secret"
|
36
40
|
e.salt = "some salt"
|
@@ -39,11 +43,32 @@ Or install it yourself as:
|
|
39
43
|
something = OpenStruct.new(channel: 'facebook', action: 'message', user_id: 43765)
|
40
44
|
encoded_param = encoder.encode something
|
41
45
|
|
42
|
-
# in some future request
|
46
|
+
# in some future request:
|
43
47
|
|
44
48
|
something = encoder.decode params[:encoded_param]
|
45
49
|
puts something.channel # 'facebook'
|
46
50
|
|
51
|
+
For added convenience when using Refcode with an ORM such as ActiveRecord, a module called `Refcode::Encodable` is also provided.
|
52
|
+
|
53
|
+
class Job < ActiveRecord::Base
|
54
|
+
include Refcode::Encodable
|
55
|
+
has_refcode secret: 'a long crunchy secret', salt: :id
|
56
|
+
end
|
57
|
+
|
58
|
+
# the salt option of the has_refcode method can accept a proc/lambda, string
|
59
|
+
# or a symbol representing an existing method on the class (id in this case)
|
60
|
+
|
61
|
+
# in some controller code:
|
62
|
+
|
63
|
+
@job = Job.find params[:id]
|
64
|
+
referral_code = @job.generate_refcode channel: 'facebook', action: 'message', user_id: 43765
|
65
|
+
|
66
|
+
# and in another action:
|
67
|
+
|
68
|
+
@job = Job.find params[:id]
|
69
|
+
referral_data = @job.parse_refcode params[:encoded_param]
|
70
|
+
|
71
|
+
|
47
72
|
## Contributing
|
48
73
|
|
49
74
|
1. Fork it
|
data/lib/refcode.rb
CHANGED
@@ -3,39 +3,5 @@ require 'yaml'
|
|
3
3
|
require 'base64url'
|
4
4
|
require 'encryptor'
|
5
5
|
require 'refcode/version'
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
class Encoder
|
10
|
-
|
11
|
-
attr_accessor :secret, :salt
|
12
|
-
|
13
|
-
def initialize
|
14
|
-
yield self if block_given?
|
15
|
-
end
|
16
|
-
|
17
|
-
def encode val
|
18
|
-
Base64URL.encode(encrypt(YAML.dump(val)))
|
19
|
-
end
|
20
|
-
|
21
|
-
def decode val
|
22
|
-
YAML.load(decrypt(Base64URL.decode(val)))
|
23
|
-
end
|
24
|
-
|
25
|
-
def encrypt val
|
26
|
-
Encryptor.encrypt(:value => val, :key => @secret, :salt => @salt, :iv => iv)
|
27
|
-
end
|
28
|
-
|
29
|
-
def decrypt val
|
30
|
-
Encryptor.decrypt(:value => val, :key => @secret, :salt => @salt, :iv => iv)
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def iv
|
36
|
-
[@secret, @salt].join
|
37
|
-
end
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|
6
|
+
require 'refcode/encoder'
|
7
|
+
require 'refcode/encodable'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Refcode
|
2
|
+
|
3
|
+
module Encodable
|
4
|
+
|
5
|
+
def self.included base
|
6
|
+
base.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
def generate_refcode val
|
10
|
+
raise 'You must call the has_refcode method in your class definition' unless self.class.refcode_options
|
11
|
+
encoder.encode val
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse_refcode code
|
15
|
+
encoder.decode code
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
def has_refcode options
|
20
|
+
raise ArgumentError unless options[:secret] && options[:salt]
|
21
|
+
@refcode_options = options
|
22
|
+
end
|
23
|
+
def refcode_options
|
24
|
+
@refcode_options
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def encoder
|
31
|
+
Refcode::Encoder.new do |r|
|
32
|
+
r.secret = self.class.refcode_options[:secret]
|
33
|
+
salt_option = self.class.refcode_options[:salt]
|
34
|
+
if salt_option.respond_to? :call
|
35
|
+
r.salt = salt_option.call
|
36
|
+
elsif salt_option.is_a? Symbol
|
37
|
+
r.salt = self.send salt_option
|
38
|
+
else
|
39
|
+
r.salt = salt_option
|
40
|
+
end
|
41
|
+
raise "Value for salt is nil (#{salt_option} given)" if r.salt.nil?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Refcode
|
2
|
+
|
3
|
+
class Encoder
|
4
|
+
|
5
|
+
attr_accessor :secret, :salt
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
yield self if block_given?
|
9
|
+
end
|
10
|
+
def encode val
|
11
|
+
Base64URL.encode(encrypt(YAML.dump(val)))
|
12
|
+
end
|
13
|
+
def decode val
|
14
|
+
YAML.load(decrypt(Base64URL.decode(val)))
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def encrypt val
|
20
|
+
Encryptor.encrypt(:value => val, :key => @secret, :salt => @salt, :iv => iv)
|
21
|
+
end
|
22
|
+
def decrypt val
|
23
|
+
Encryptor.decrypt(:value => val, :key => @secret, :salt => @salt, :iv => iv)
|
24
|
+
end
|
25
|
+
def iv
|
26
|
+
[@secret, @salt].join
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/lib/refcode/version.rb
CHANGED
data/test/refcode_test.rb
CHANGED
@@ -7,6 +7,13 @@ class RefcodeTest < Test::Unit::TestCase
|
|
7
7
|
SECRET = "686cb0461769ebed556f56bc88c956bfdf786646ab1920f58201723c557ccbaeb57112fd53302c06475e6e19a176ba617ee284b091d375858cb259e8578c620f"
|
8
8
|
SALT = "dcc74de5efdab0d80b5d763f8a63bef3d37c7453ee45446fbbccd26648af8ca6a112576dafbf5475f3cd7b968703cc9e0682ee45b8b622045cec287908f536e8"
|
9
9
|
|
10
|
+
class BareEncodableObject < OpenStruct
|
11
|
+
include Refcode::Encodable
|
12
|
+
end
|
13
|
+
class EncodableObject < BareEncodableObject
|
14
|
+
has_refcode :secret => SECRET, :salt => :record_id
|
15
|
+
end
|
16
|
+
|
10
17
|
def test_encoder_against_hash
|
11
18
|
test_hash = { :channel => 'email', :record_id => 49203, :action => 'forward' }
|
12
19
|
assert_equal test_hash, encode_and_decode(test_hash)
|
@@ -26,8 +33,40 @@ class RefcodeTest < Test::Unit::TestCase
|
|
26
33
|
assert_equal test_struct, encode_and_decode(test_struct)
|
27
34
|
end
|
28
35
|
|
36
|
+
def test_should_raise_argument_error_if_generate_refcode_called_without_val
|
37
|
+
assert_raise ArgumentError do
|
38
|
+
encodable_object.generate_refcode
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_should_raise_error_if_generate_refcode_called_before_has_refcode
|
43
|
+
assert_raise ArgumentError do
|
44
|
+
obj = BareEncodableObject.new :some => 'value'
|
45
|
+
obj.generate_refcode
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_should_raise_an_error_if_salt_is_empty
|
50
|
+
encodable_object.record_id = nil
|
51
|
+
assert_raise RuntimeError do
|
52
|
+
referral_code_content = { :channel => 'email', :action => 'forward', :user_id => 14325 }
|
53
|
+
code = encodable_object.generate_refcode referral_code_content
|
54
|
+
assert_equal referral_code_content, encodable_object.parse_refcode(code)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_encodable_against_hash
|
59
|
+
referral_code_content = { :channel => 'email', :action => 'forward', :user_id => 14325 }
|
60
|
+
code = encodable_object.generate_refcode referral_code_content
|
61
|
+
assert_equal referral_code_content, encodable_object.parse_refcode(code)
|
62
|
+
end
|
63
|
+
|
29
64
|
private
|
30
65
|
|
66
|
+
def encodable_object
|
67
|
+
@encodable_object ||= EncodableObject.new :name => 'Unimportant Value', :record_id => 381598
|
68
|
+
end
|
69
|
+
|
31
70
|
def encoder
|
32
71
|
@encoder ||= Refcode::Encoder.new do |e|
|
33
72
|
e.secret = SECRET
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: refcode
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jordan Sitkin
|
@@ -79,6 +79,8 @@ files:
|
|
79
79
|
- README.md
|
80
80
|
- Rakefile
|
81
81
|
- lib/refcode.rb
|
82
|
+
- lib/refcode/encodable.rb
|
83
|
+
- lib/refcode/encoder.rb
|
82
84
|
- lib/refcode/version.rb
|
83
85
|
- refcode.gemspec
|
84
86
|
- test/refcode_test.rb
|