sitehub 0.5.0.alpha3 → 0.5.0.alpha4
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/Gemfile.lock +1 -1
- data/lib/sitehub/collection.rb +1 -1
- data/lib/sitehub/collection/route_collection.rb +1 -1
- data/lib/sitehub/collection/split_route_collection.rb +11 -11
- data/lib/sitehub/identifier.rb +37 -0
- data/lib/sitehub/middleware/route.rb +1 -1
- data/lib/sitehub/middleware/routes.rb +3 -3
- data/lib/sitehub/route_builder.rb +66 -66
- data/lib/sitehub/version.rb +1 -1
- data/spec/sitehub/collection/split_route_collection_spec.rb +18 -2
- data/spec/sitehub/collection_spec.rb +1 -0
- data/spec/sitehub/core_spec.rb +1 -1
- data/spec/sitehub/identifier_spec.rb +89 -0
- data/spec/sitehub/integration_spec.rb +185 -7
- data/spec/sitehub/middleware/routes_spec.rb +14 -8
- data/spec/sitehub/route_builder_spec.rb +142 -153
- data/spec/support/patch/rack/response.rb +1 -1
- metadata +4 -3
- data/spec/support/shared_contexts/sitehub_context.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ede66ca937a51aa4de571db5f3080113198429c
|
4
|
+
data.tar.gz: 91540d0d051530439cd2979ba054e5478cb215b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d1c08d30bcf5f0c95b20a7a49433ee214c65afa6d917eb04c5ca869d29fe3bff2bd8afce684b77f9920de2ee2f7f633797ba49b202cf6809fc3a2c25d8b4596
|
7
|
+
data.tar.gz: 9f2ce2edfb124e6b09b835986dd96a224bc7e403afbb20a808f19a0c5b0be83a5b044272b72680db17522b841ac3bbfbe03d982578d2666fd93db1b4f125a6e9
|
data/Gemfile.lock
CHANGED
data/lib/sitehub/collection.rb
CHANGED
@@ -16,7 +16,7 @@ class SiteHub
|
|
16
16
|
alias_method :add_backup, :add
|
17
17
|
|
18
18
|
send(:define_method, :add) do |id, value, *args|
|
19
|
-
raise DuplicateVersionException, UNIQUE_LABELS_MSG if
|
19
|
+
raise DuplicateVersionException, UNIQUE_LABELS_MSG if key?(id)
|
20
20
|
add_backup id, value, *args
|
21
21
|
end
|
22
22
|
end
|
@@ -12,12 +12,6 @@ class SiteHub
|
|
12
12
|
MAX = 100
|
13
13
|
MIN = 0
|
14
14
|
|
15
|
-
def initialize(hash = {})
|
16
|
-
hash.each do |value, percentage|
|
17
|
-
add(value.id, value, percentage)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
15
|
def add(id, value, percentage)
|
22
16
|
raise InvalidSplitException, FIXNUM_ERR_MSG unless percentage.is_a?(Fixnum)
|
23
17
|
upper_bound = lower_bound + percentage
|
@@ -28,20 +22,26 @@ class SiteHub
|
|
28
22
|
|
29
23
|
def resolve(*args)
|
30
24
|
random = rand(MAX)
|
31
|
-
result =
|
25
|
+
result = _values.find { |split| random >= split.lower && random < split.upper }
|
32
26
|
result ? result.value.resolve(*args) : nil
|
33
27
|
end
|
34
28
|
|
35
29
|
def transform(&block)
|
36
|
-
|
30
|
+
_values.each { |split| split.update_value(&block) }
|
37
31
|
end
|
38
32
|
|
39
33
|
def [](key)
|
40
|
-
key?(key) ? super(key).value :
|
34
|
+
key?(key) ? super(key).value : super(key)
|
35
|
+
end
|
36
|
+
|
37
|
+
alias _values values
|
38
|
+
|
39
|
+
def values
|
40
|
+
super().collect(&:value)
|
41
41
|
end
|
42
42
|
|
43
43
|
def valid?
|
44
|
-
last =
|
44
|
+
last = _values.last
|
45
45
|
return true if last && last.upper == MAX
|
46
46
|
|
47
47
|
warn(INVALID_SPILTS_MSG)
|
@@ -51,7 +51,7 @@ class SiteHub
|
|
51
51
|
private
|
52
52
|
|
53
53
|
def lower_bound
|
54
|
-
|
54
|
+
_values.empty? ? MIN : _values.last.upper
|
55
55
|
end
|
56
56
|
end
|
57
57
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class SiteHub
|
2
|
+
class Identifier
|
3
|
+
attr_reader :id, :components
|
4
|
+
|
5
|
+
def initialize(id)
|
6
|
+
@components = id.to_s.split('|').collect(&:to_sym)
|
7
|
+
end
|
8
|
+
|
9
|
+
def child_label(label)
|
10
|
+
components.empty? ? label : "#{self}|#{label}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def root
|
14
|
+
components.first
|
15
|
+
end
|
16
|
+
|
17
|
+
def sub_id
|
18
|
+
Identifier.new(components[1..-1].join('|'))
|
19
|
+
end
|
20
|
+
|
21
|
+
def valid?
|
22
|
+
!@components.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
components.join('|')
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_sym
|
30
|
+
to_s.to_sym
|
31
|
+
end
|
32
|
+
|
33
|
+
def ==(other)
|
34
|
+
other.respond_to?(:to_sym) && to_sym == other.to_sym
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -23,9 +23,9 @@ class SiteHub
|
|
23
23
|
def call(env)
|
24
24
|
source_request = Rack::Request.new(env)
|
25
25
|
|
26
|
-
|
26
|
+
route = mapped_route(path: source_request.path, request: source_request)
|
27
27
|
|
28
|
-
|
28
|
+
route.call(env)
|
29
29
|
end
|
30
30
|
|
31
31
|
def init
|
@@ -46,7 +46,7 @@ class SiteHub
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
def
|
49
|
+
def mapped_route(path:, request:)
|
50
50
|
self[mapping(path)].resolve(id: request.cookies[sitehub_cookie_name], env: request.env)
|
51
51
|
end
|
52
52
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'uuid'
|
2
2
|
require 'sitehub/equality'
|
3
|
+
require 'sitehub/nil_route'
|
4
|
+
require 'sitehub/identifier'
|
3
5
|
require 'sitehub/getter_setter_methods'
|
4
6
|
require_relative 'collection/split_route_collection'
|
5
7
|
require_relative 'rules'
|
@@ -14,16 +16,18 @@ class SiteHub
|
|
14
16
|
class InvalidDefinitionException < Exception
|
15
17
|
end
|
16
18
|
|
17
|
-
INVALID_SPLIT_MSG = 'url must be defined if not supplying a block'.freeze
|
18
19
|
ROUTES_WITH_SPLITS_MSG = 'you cant register routes and splits at the same level'.freeze
|
19
|
-
|
20
|
-
|
20
|
+
INVALID_SPLIT_MSG = 'url must be defined if not supplying a block'.freeze
|
21
|
+
RULE_NOT_SPECIFIED_MSG = 'rule must be specified when supplying a block'.freeze
|
22
|
+
IGNORING_URL_MSG = 'Block supplied, ignoring URL parameter'.freeze
|
21
23
|
URL_REQUIRED_MSG = 'URL must be supplied for splits and routes'.freeze
|
22
24
|
|
23
25
|
class << self
|
24
26
|
# TODO: support nest splits and routes
|
25
27
|
def from_hash(hash, sitehub_cookie_name)
|
26
|
-
new(sitehub_cookie_name: sitehub_cookie_name,
|
28
|
+
new(sitehub_cookie_name: sitehub_cookie_name,
|
29
|
+
sitehub_cookie_path: hash[:sitehub_cookie_path],
|
30
|
+
mapped_path: hash[:path]) do
|
27
31
|
extend CollectionMethods
|
28
32
|
|
29
33
|
collection(hash, :splits).each do |split|
|
@@ -44,11 +48,11 @@ class SiteHub
|
|
44
48
|
|
45
49
|
transient :id
|
46
50
|
|
47
|
-
getter_setters :
|
51
|
+
getter_setters :sitehub_cookie_path, :sitehub_cookie_name
|
48
52
|
attr_reader :mapped_path, :id
|
49
53
|
|
50
|
-
def initialize(sitehub_cookie_name:, sitehub_cookie_path: nil, mapped_path:, rule: nil, &block)
|
51
|
-
@id =
|
54
|
+
def initialize(id: nil, sitehub_cookie_name:, sitehub_cookie_path: nil, mapped_path:, rule: nil, &block)
|
55
|
+
@id = Identifier.new(id)
|
52
56
|
@mapped_path = mapped_path
|
53
57
|
@sitehub_cookie_name = sitehub_cookie_name
|
54
58
|
@sitehub_cookie_path = sitehub_cookie_path
|
@@ -62,32 +66,42 @@ class SiteHub
|
|
62
66
|
raise InvalidDefinitionException unless valid?
|
63
67
|
end
|
64
68
|
|
65
|
-
def
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
69
|
+
def add_route(label:, rule: nil, percentage: nil, url: nil, &block)
|
70
|
+
label = id.child_label(label)
|
71
|
+
route = if block
|
72
|
+
raise InvalidDefinitionException, RULE_NOT_SPECIFIED_MSG unless percentage || rule
|
73
|
+
warn(IGNORING_URL_MSG) if url
|
74
|
+
new(rule: rule, id: label, &block).build
|
75
|
+
else
|
76
|
+
raise InvalidDefinitionException, RULE_NOT_SPECIFIED_MSG unless url
|
77
|
+
forward_proxy(url: url, label: label, rule: rule)
|
78
|
+
end
|
70
79
|
|
71
|
-
|
80
|
+
routes.add(label, route, percentage)
|
72
81
|
end
|
73
82
|
|
74
|
-
def
|
75
|
-
|
83
|
+
def default_route
|
84
|
+
routes.default
|
76
85
|
end
|
77
86
|
|
78
|
-
def
|
79
|
-
|
87
|
+
def default_route?
|
88
|
+
!default_route.nil?
|
89
|
+
end
|
80
90
|
|
81
|
-
|
82
|
-
|
91
|
+
def build
|
92
|
+
build_with_middleware if middleware?
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
def default(url:)
|
97
|
+
routes.default = forward_proxy(label: :default, url: url)
|
83
98
|
end
|
84
99
|
|
85
100
|
def forward_proxy(label:, url:, rule: nil)
|
86
101
|
proxy = ForwardProxy.new(mapped_url: url, mapped_path: mapped_path)
|
87
102
|
|
88
|
-
id = (label || UUID.generate(:compact)).to_sym
|
89
103
|
Route.new(proxy,
|
90
|
-
id:
|
104
|
+
id: label,
|
91
105
|
sitehub_cookie_path: sitehub_cookie_path,
|
92
106
|
sitehub_cookie_name: sitehub_cookie_name).tap do |wrapper|
|
93
107
|
wrapper.rule(rule)
|
@@ -95,38 +109,34 @@ class SiteHub
|
|
95
109
|
end
|
96
110
|
|
97
111
|
def resolve(id: nil, env:)
|
98
|
-
id = id
|
99
|
-
|
112
|
+
id = Identifier.new(id)
|
113
|
+
if id.valid? && (route = routes[id.root])
|
114
|
+
route.resolve(id: id.sub_id, env: env)
|
115
|
+
else
|
116
|
+
routes.resolve(env: env) || default_route
|
117
|
+
end
|
100
118
|
end
|
101
119
|
|
102
|
-
def route(url: nil, label
|
103
|
-
|
104
|
-
|
105
|
-
warn(IGNORING_URL_LABEL_MSG) if url || label
|
106
|
-
new(rule: rule, &block).build
|
107
|
-
else
|
108
|
-
forward_proxy(url: url, label: label, rule: rule)
|
109
|
-
end
|
110
|
-
|
111
|
-
routes.add(endpoint.id, endpoint)
|
120
|
+
def route(url: nil, label:, rule: nil, &block)
|
121
|
+
routes(@routes)
|
122
|
+
add_route(label: label, rule: rule, url: url, &block)
|
112
123
|
end
|
113
124
|
|
114
|
-
def
|
115
|
-
|
125
|
+
def routes(collection = nil)
|
126
|
+
return @endpoints ||= Collection::RouteCollection.new unless collection
|
116
127
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
else
|
121
|
-
forward_proxy(label: label, url: url)
|
122
|
-
end
|
128
|
+
raise InvalidDefinitionException, ROUTES_WITH_SPLITS_MSG if @endpoints && !@endpoints.equal?(collection)
|
129
|
+
@endpoints = collection
|
130
|
+
end
|
123
131
|
|
124
|
-
|
132
|
+
def split(percentage:, url: nil, label:, &block)
|
133
|
+
routes(@splits)
|
134
|
+
add_route(label: label, percentage: percentage, url: url, &block)
|
125
135
|
end
|
126
136
|
|
127
137
|
def valid?
|
128
|
-
return true if
|
129
|
-
|
138
|
+
return true if default_route?
|
139
|
+
routes.valid?
|
130
140
|
end
|
131
141
|
|
132
142
|
private
|
@@ -138,34 +148,24 @@ class SiteHub
|
|
138
148
|
end
|
139
149
|
end
|
140
150
|
|
141
|
-
def build_default_with_middleware
|
142
|
-
add_middleware_to_proxy(default_proxy)
|
143
|
-
default_proxy.init
|
144
|
-
end
|
145
|
-
|
146
151
|
def build_with_middleware
|
147
|
-
|
148
|
-
|
149
|
-
|
152
|
+
routes = routes().values.find_all { |route| route.is_a?(Route) }
|
153
|
+
|
154
|
+
routes << default_route if default_route?
|
155
|
+
|
156
|
+
routes.each do |route|
|
157
|
+
add_middleware_to_proxy(route)
|
158
|
+
route.init
|
150
159
|
end
|
151
160
|
end
|
152
161
|
|
153
|
-
def new(rule: nil, &block)
|
154
|
-
self.class.new(
|
162
|
+
def new(id:, rule: nil, &block)
|
163
|
+
self.class.new(id: id,
|
164
|
+
sitehub_cookie_name: sitehub_cookie_name,
|
165
|
+
sitehub_cookie_path: sitehub_cookie_path,
|
155
166
|
mapped_path: mapped_path,
|
156
167
|
rule: rule,
|
157
|
-
&block)
|
158
|
-
builder.sitehub_cookie_name sitehub_cookie_name
|
159
|
-
builder.sitehub_cookie_path sitehub_cookie_path
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
def splits
|
164
|
-
endpoints(@splits)
|
165
|
-
end
|
166
|
-
|
167
|
-
def routes
|
168
|
-
endpoints(@routes)
|
168
|
+
&block)
|
169
169
|
end
|
170
170
|
end
|
171
171
|
end
|
data/lib/sitehub/version.rb
CHANGED
@@ -22,6 +22,14 @@ class SiteHub
|
|
22
22
|
expect(subject).to be_a(Collection)
|
23
23
|
end
|
24
24
|
|
25
|
+
describe '#[]' do
|
26
|
+
context 'key exists' do
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'key does not exist' do
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
25
33
|
describe '#add' do
|
26
34
|
before do
|
27
35
|
subject.add route_1.id, route_1, 50
|
@@ -32,8 +40,8 @@ class SiteHub
|
|
32
40
|
|
33
41
|
it 'sets the selection boundary which is used to choose routes' do
|
34
42
|
subject.add route_2.id, route_2, 50
|
35
|
-
first = subject.
|
36
|
-
second = subject.
|
43
|
+
first = subject._values.first
|
44
|
+
second = subject._values.last
|
37
45
|
|
38
46
|
expect(first.lower).to eq(0)
|
39
47
|
expect(first.upper).to eq(50)
|
@@ -117,5 +125,13 @@ class SiteHub
|
|
117
125
|
end
|
118
126
|
end
|
119
127
|
end
|
128
|
+
|
129
|
+
describe '#values' do
|
130
|
+
it 'returns values contained inside splits' do
|
131
|
+
subject.add route_1.id, route_1, 50
|
132
|
+
subject.add route_2.id, route_2, 50
|
133
|
+
expect(subject.values).to eq([route_1, route_2])
|
134
|
+
end
|
135
|
+
end
|
120
136
|
end
|
121
137
|
end
|
data/spec/sitehub/core_spec.rb
CHANGED
@@ -123,7 +123,7 @@ class SiteHub
|
|
123
123
|
|
124
124
|
it 'passes the block to the route constructor' do
|
125
125
|
expected_route.split url: :endpoint, percentage: 100, label: :label
|
126
|
-
expect(subject.routes['/app'].
|
126
|
+
expect(subject.routes['/app'].routes).to eq(expected_route.routes)
|
127
127
|
end
|
128
128
|
end
|
129
129
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
class SiteHub
|
2
|
+
describe Identifier do
|
3
|
+
subject(:valid_identifer) do
|
4
|
+
described_class.new(:root_id)
|
5
|
+
end
|
6
|
+
|
7
|
+
subject(:blank) do
|
8
|
+
described_class.new(nil)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#initialize' do
|
12
|
+
context 'id supplied' do
|
13
|
+
it 'it returns a concatenated id' do
|
14
|
+
expect(valid_identifer.child_label(:label)).to eq('root_id|label')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'id not set' do
|
19
|
+
it 'it returns the given value' do
|
20
|
+
expect(blank.child_label(:label)).to eq(:label)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#valid?' do
|
26
|
+
context 'has at least one component' do
|
27
|
+
it 'returns true' do
|
28
|
+
expect(valid_identifer).to be_valid
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'does not have at least one component' do
|
33
|
+
it 'returns false' do
|
34
|
+
expect(blank).to_not be_valid
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#sub_id' do
|
40
|
+
context 'id contains more than one pipe seperated value' do
|
41
|
+
subject do
|
42
|
+
described_class.new('root|first|second')
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'removes the first value in the list' do
|
46
|
+
expect(subject.sub_id).to eq(:'first|second')
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'returns an object of type Identifier' do
|
50
|
+
expect(subject.sub_id).to be_a(described_class)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '#to_s' do
|
56
|
+
subject do
|
57
|
+
described_class.new(:root_id)
|
58
|
+
end
|
59
|
+
it 'returns the id as a string' do
|
60
|
+
expect(valid_identifer.to_s).to eq(:root_id.to_s)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#to_sym' do
|
65
|
+
it 'returns the id as a string' do
|
66
|
+
expect(valid_identifer.to_sym).to eq(:root_id)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#==' do
|
71
|
+
subject do
|
72
|
+
described_class.new(:root_id)
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'other can be converted to a matching symbol' do
|
76
|
+
it 'returns true' do
|
77
|
+
expect(subject).to eq(:root_id)
|
78
|
+
expect(subject).to eq('root_id')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'other can not be converted to a matching symbol' do
|
83
|
+
it 'returns true' do
|
84
|
+
expect(subject).to_not eq('different')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|