determinator 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +4 -0
  7. data/Guardfile +6 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +34 -0
  10. data/Rakefile +6 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/determinator.gemspec +31 -0
  14. data/docs/img/determinator.jpg +0 -0
  15. data/examples/determinator-rails/.env +7 -0
  16. data/examples/determinator-rails/.gitignore +15 -0
  17. data/examples/determinator-rails/Gemfile +16 -0
  18. data/examples/determinator-rails/Gemfile.lock +163 -0
  19. data/examples/determinator-rails/Procfile +2 -0
  20. data/examples/determinator-rails/README.md +35 -0
  21. data/examples/determinator-rails/Rakefile +3 -0
  22. data/examples/determinator-rails/app/controllers/application_controller.rb +20 -0
  23. data/examples/determinator-rails/app/controllers/index_controller.rb +9 -0
  24. data/examples/determinator-rails/app/jobs/application_job.rb +2 -0
  25. data/examples/determinator-rails/bin/bundle +3 -0
  26. data/examples/determinator-rails/bin/rails +4 -0
  27. data/examples/determinator-rails/bin/rake +4 -0
  28. data/examples/determinator-rails/config.ru +3 -0
  29. data/examples/determinator-rails/config/application.rb +18 -0
  30. data/examples/determinator-rails/config/boot.rb +3 -0
  31. data/examples/determinator-rails/config/environment.rb +2 -0
  32. data/examples/determinator-rails/config/environments/development.rb +18 -0
  33. data/examples/determinator-rails/config/environments/production.rb +17 -0
  34. data/examples/determinator-rails/config/environments/test.rb +13 -0
  35. data/examples/determinator-rails/config/initializers/determinator.rb +7 -0
  36. data/examples/determinator-rails/config/initializers/filter_parameter_logging.rb +1 -0
  37. data/examples/determinator-rails/config/initializers/new_framework_defaults.rb +3 -0
  38. data/examples/determinator-rails/config/initializers/wrap_parameters.rb +3 -0
  39. data/examples/determinator-rails/config/puma.rb +5 -0
  40. data/examples/determinator-rails/config/routes.rb +6 -0
  41. data/examples/determinator-rails/config/secrets.yml +8 -0
  42. data/examples/determinator-rails/config/sidekiq.yml +2 -0
  43. data/examples/determinator-rails/public/favicon.ico +0 -0
  44. data/examples/determinator-rails/public/robots.txt +5 -0
  45. data/lib/determinator.rb +16 -0
  46. data/lib/determinator/actor_control.rb +41 -0
  47. data/lib/determinator/control.rb +119 -0
  48. data/lib/determinator/feature.rb +48 -0
  49. data/lib/determinator/retrieve/routemaster.rb +70 -0
  50. data/lib/determinator/retrieve/routemaster_indexing_middleware.rb +28 -0
  51. data/lib/determinator/target_group.rb +28 -0
  52. data/lib/determinator/version.rb +3 -0
  53. metadata +193 -0
@@ -0,0 +1,70 @@
1
+ require 'uri'
2
+ require 'routemaster/drain/caching'
3
+ require 'routemaster/responses/hateoas_response'
4
+ require 'determinator/retrieve/routemaster_indexing_middleware'
5
+
6
+ module Determinator
7
+ module Retrieve
8
+ # A storage and retrieval engine for Determinator using routemaster-drain.
9
+ #
10
+ # To use this correctly you will need the following environment variables set to appropriate values
11
+ # for your instance of Routemaster:
12
+ #
13
+ # ROUTEMASTER_DRAIN_TOKENS
14
+ # ROUTEMASTER_DRAIN_REDIS
15
+ # ROUTEMASTER_CACHE_REDIS
16
+ # ROUTEMASTER_CACHE_AUTH
17
+ # ROUTEMASTER_QUEUE_NAME
18
+ # ROUTEMASTER_CALLBACK_URL
19
+ class Routemaster
20
+ attr_reader :routemaster_app
21
+
22
+ CALLBACK_PATH = (URI.parse(ENV['ROUTEMASTER_CALLBACK_URL']).path rescue '/events').freeze
23
+
24
+ # @param :discovery_url [String] The bootstrap URL of the instance of Florence which defines Features.
25
+ def initialize(discovery_url:)
26
+ client = ::Routemaster::APIClient.new(
27
+ response_class: ::Routemaster::Responses::HateoasResponse,
28
+ middlewares: [RoutemasterIndexingMiddleware]
29
+ )
30
+ @routemaster = client.discover(discovery_url)
31
+ @routemaster_app = ::Routemaster::Drain::Caching.new
32
+ end
33
+
34
+ def retrieve(feature_name)
35
+ key = self.class.index_cache_key(feature_name)
36
+ feature_id = ::Routemaster::Config.cache_redis.get(key)
37
+ return unless feature_id
38
+
39
+ obj = @routemaster.feature.show(feature_id)
40
+
41
+ Feature.new(
42
+ name: obj.body.name,
43
+ identifier: obj.body.identifier,
44
+ bucket_type: obj.body.bucket_type,
45
+ target_groups: obj.body.target_groups.map { |tg|
46
+ TargetGroup.new(
47
+ rollout: tg.rollout,
48
+ constraints: tg.constraints.to_hash
49
+ )
50
+ },
51
+ variants: obj.body.variants.to_hash,
52
+ overrides: obj.body.overrides.to_hash
53
+ )
54
+ rescue ::Routemaster::Errors::ResourceNotFound
55
+ nil
56
+ end
57
+
58
+ # Automatically configures the rails router to listen for Features with routemaster
59
+ #
60
+ # @param route_mapper [ActionDispatch::Routing::Mapper] The rails mapper, 'self' within the `routes.draw` block
61
+ def configure_rails_router(route_mapper)
62
+ route_mapper.mount routemaster_app, at: CALLBACK_PATH
63
+ end
64
+
65
+ def self.index_cache_key(feature_name)
66
+ "determinator_index:#{feature_name}"
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,28 @@
1
+ module Determinator
2
+ module Retrieve
3
+ # Middleware which indexes features by their name, so we can look up a feature by name
4
+ # and find the details (which are only accessible by ID)
5
+ class RoutemasterIndexingMiddleware
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ content = JSON.parse(env.body)
12
+
13
+ if content_describes_feature?(content)
14
+ key = Routemaster.index_cache_key(content['name'])
15
+ ::Routemaster::Config.cache_redis.set(key, content['id'])
16
+ end
17
+
18
+ @app.call(env)
19
+ end
20
+
21
+ private
22
+
23
+ def content_describes_feature?(content)
24
+ content['id'] && content['name'] && content['bucket_type']
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ module Determinator
2
+ class TargetGroup
3
+ attr_reader :rollout, :constraints
4
+
5
+ def initialize(rollout:, constraints: {})
6
+ @rollout = rollout
7
+ @constraints = constraints
8
+ end
9
+
10
+ def rollout_percent
11
+ # Rollout is out of 65536 because the highest rollout indicator
12
+ # (which is a 16 bit integer) can be is 65,535. 100% rollout
13
+ # needs to include the highest indicator, and 0% needs to not include
14
+ # the lowest indicator.
15
+ Rational(rollout, 65_536)
16
+ end
17
+
18
+ def inspect
19
+ pc = (rollout_percent * 100).to_f.round(1)
20
+ "<#{pc}% of those matching: #{constraints}>"
21
+ end
22
+
23
+ def ==(other)
24
+ return false unless other.is_a?(self.class)
25
+ other.rollout == rollout && other.constraints == constraints
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module Determinator
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,193 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: determinator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - JP Hastings-Spital
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-04-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: routemaster-drain
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.13'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.13'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.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: rspec-its
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.2'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.2'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '4.7'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '4.7'
97
+ - !ruby/object:Gem::Dependency
98
+ name: factory_girl
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '4.8'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '4.8'
111
+ description:
112
+ email:
113
+ - jp@deliveroo.co.uk
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - ".rspec"
120
+ - ".travis.yml"
121
+ - CODE_OF_CONDUCT.md
122
+ - Gemfile
123
+ - Guardfile
124
+ - LICENSE.txt
125
+ - README.md
126
+ - Rakefile
127
+ - bin/console
128
+ - bin/setup
129
+ - determinator.gemspec
130
+ - docs/img/determinator.jpg
131
+ - examples/determinator-rails/.env
132
+ - examples/determinator-rails/.gitignore
133
+ - examples/determinator-rails/Gemfile
134
+ - examples/determinator-rails/Gemfile.lock
135
+ - examples/determinator-rails/Procfile
136
+ - examples/determinator-rails/README.md
137
+ - examples/determinator-rails/Rakefile
138
+ - examples/determinator-rails/app/controllers/application_controller.rb
139
+ - examples/determinator-rails/app/controllers/index_controller.rb
140
+ - examples/determinator-rails/app/jobs/application_job.rb
141
+ - examples/determinator-rails/bin/bundle
142
+ - examples/determinator-rails/bin/rails
143
+ - examples/determinator-rails/bin/rake
144
+ - examples/determinator-rails/config.ru
145
+ - examples/determinator-rails/config/application.rb
146
+ - examples/determinator-rails/config/boot.rb
147
+ - examples/determinator-rails/config/environment.rb
148
+ - examples/determinator-rails/config/environments/development.rb
149
+ - examples/determinator-rails/config/environments/production.rb
150
+ - examples/determinator-rails/config/environments/test.rb
151
+ - examples/determinator-rails/config/initializers/determinator.rb
152
+ - examples/determinator-rails/config/initializers/filter_parameter_logging.rb
153
+ - examples/determinator-rails/config/initializers/new_framework_defaults.rb
154
+ - examples/determinator-rails/config/initializers/wrap_parameters.rb
155
+ - examples/determinator-rails/config/puma.rb
156
+ - examples/determinator-rails/config/routes.rb
157
+ - examples/determinator-rails/config/secrets.yml
158
+ - examples/determinator-rails/config/sidekiq.yml
159
+ - examples/determinator-rails/public/favicon.ico
160
+ - examples/determinator-rails/public/robots.txt
161
+ - lib/determinator.rb
162
+ - lib/determinator/actor_control.rb
163
+ - lib/determinator/control.rb
164
+ - lib/determinator/feature.rb
165
+ - lib/determinator/retrieve/routemaster.rb
166
+ - lib/determinator/retrieve/routemaster_indexing_middleware.rb
167
+ - lib/determinator/target_group.rb
168
+ - lib/determinator/version.rb
169
+ homepage: https://github.com/deliveroo/determinator
170
+ licenses:
171
+ - MIT
172
+ metadata: {}
173
+ post_install_message:
174
+ rdoc_options: []
175
+ require_paths:
176
+ - lib
177
+ required_ruby_version: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ required_rubygems_version: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - ">="
185
+ - !ruby/object:Gem::Version
186
+ version: '0'
187
+ requirements: []
188
+ rubyforge_project:
189
+ rubygems_version: 2.5.1
190
+ signing_key:
191
+ specification_version: 4
192
+ summary: Determine which experiments and features a specific actor should see.
193
+ test_files: []