featureflow 0.6.0 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e44302ab7573a1fe36b0fac9deaac9958ab96d01b73a865e3c98d00bd71ebb3
4
- data.tar.gz: 8b641fa3bd07be005e7cf6f722a441da2544d2fa1141b40c35e1d3abb4df6db6
3
+ metadata.gz: 2865ebc3fed1dfffb613a9a459ce6e0ac8d6e5cd05d758b4e93944cbc8d8d372
4
+ data.tar.gz: 1b5a7d4b01a2ff98501ca2818e3d00a4d4523525e9e71cd4cda1a847ef009867
5
5
  SHA512:
6
- metadata.gz: dd22e33787e281e6a61ca62464f771c3e4d1778d6893f6689ff189c57dbdf3c433e7b49b6a881f73e113f5b5d21216c7b3a6dd67e313c8aeaf4e317ed4c858b9
7
- data.tar.gz: 679d1c6f0efbefca2497da6467109c22f93bdde4a6bc165dd5e6b0d03b35daaa02888c5aeec0296dec6f962fd0aeea5a257b701e26798b15f2d394fe324aa544
6
+ metadata.gz: 8c1fb252dcd6beac50457f7d8328198b7512d4bad761824d665490dc0a8fa59026aa55901adf0555dbfce12599ecbf1b611695801d150b29a76ee747abb1aadd
7
+ data.tar.gz: 712e342d59b58327b4ec1bf98770946ef255f39dbecc2093dc50fa786252b82f6aed33b06a1faacbd5c7e9f60aa65f750a37e454c4ec6f03f8a8cb9f96bb87c2
data/CHANGELOG.md CHANGED
@@ -1,4 +1,26 @@
1
1
  # Change log
2
+ ## [0.7.0] - 2026-08-11
3
+ ### Fixed
4
+ - Condition operators fail closed instead of raising into the host application: an
5
+ invalid `matches` regex, a non-string attribute against a string operator, or a
6
+ string/number type mismatch on a comparison operator is now a no-match, never an
7
+ exception.
8
+ - `before`/`after` compare parsed instants rather than strings, so rule values with
9
+ arbitrary UTC offsets compare correctly; date-only values are read as UTC midnight
10
+ on every host, not local midnight.
11
+ - Rollout bucketing hashed every user to the same bucket (`user['id']` vs
12
+ `user[:id]`); percentage splits now distribute correctly.
13
+ - `RailsClient#evaluate` and `UserBuilder#with_attributes` were broken; both work
14
+ again, and the Rails generator accepts current `sdk-srv-env-` keys.
15
+ ### Added
16
+ - `Featureflow::Client.new(api_key: '...')` accepts a configuration Hash; unknown
17
+ options raise rather than being silently dropped.
18
+ - Implicit `featureflow.date` / `featureflow.hourofday` attributes are injected at
19
+ evaluation time, matching the other server SDKs.
20
+ ### Changed
21
+ - Requires Ruby >= 3.1 (excon ~> 1.5).
22
+ - The packaged gem now contains only `lib/`, README, CHANGELOG and LICENSE.
23
+
2
24
  ## [0.6.0] - 2018-12-17
3
25
  ### Fixed
4
26
  - Use User over Context matching all other SDKs
data/README.md CHANGED
@@ -181,4 +181,14 @@ rvm install 2.5.1
181
181
  rvm use --default 2.5.1
182
182
  bundle install
183
183
  ruby test.rb
184
- ```
184
+ ```
185
+
186
+ ## Manual test harness (example server)
187
+
188
+ To try the SDK against a real Featureflow environment without writing any code, run the small local harness in [examples/harness](examples/harness):
189
+
190
+ ```
191
+ FEATUREFLOW_SERVER_KEY=sdk-srv-env-<your_key> bundle exec ruby examples/harness/server.rb
192
+ ```
193
+
194
+ Then open `http://127.0.0.1:3456/` to evaluate features in the browser, or hit `GET /api/evaluate?feature=<key>&userId=<id>` directly. See [examples/harness/README.md](examples/harness/README.md) for all endpoints and environment variables (including pointing at staging via `FEATUREFLOW_BASE_URL`).
@@ -1,3 +1,4 @@
1
+ require 'time'
1
2
  require 'featureflow/evaluate'
2
3
  require 'featureflow/polling_client'
3
4
  require 'featureflow/events_client'
@@ -5,7 +6,7 @@ require 'featureflow/events_client'
5
6
  module Featureflow
6
7
  class Client
7
8
  def initialize(configuration = nil)
8
- @configuration = configuration || Featureflow.configuration
9
+ @configuration = build_configuration(configuration)
9
10
  @configuration.validate!
10
11
  @features = {}
11
12
 
@@ -61,7 +62,16 @@ module Featureflow
61
62
  user = UserBuilder.new(user).build if user.is_a?(String)
62
63
 
63
64
  user = user.dup
64
- user[:attributes] = user[:attributes].merge('featureflow.user.id' => user[:id])
65
+ # Implicit attributes injected at evaluation time (matching the Node SDK's
66
+ # EvaluateHelpers): featureflow.date/hourofday let rules target the current
67
+ # moment; sdk-server's partial evaluation defers these to the JS client, but
68
+ # server SDKs evaluate them locally against "now".
69
+ now = Time.now
70
+ user[:attributes] = user[:attributes].merge(
71
+ 'featureflow.user.id' => user[:id],
72
+ 'featureflow.date' => now.utc.iso8601,
73
+ 'featureflow.hourofday' => now.hour
74
+ )
65
75
 
66
76
  Evaluate.new(
67
77
  feature_key: key,
@@ -72,5 +82,26 @@ module Featureflow
72
82
  events_client: @events_client
73
83
  )
74
84
  end
85
+
86
+ # Accepts either a Featureflow::Configuration, or a Hash of configuration
87
+ # options — `Featureflow::Client.new(api_key: '...')` is the form the README
88
+ # leads with and the natural Ruby idiom. Passing nothing keeps using the
89
+ # process-wide Featureflow.configuration (which reads FEATUREFLOW_SERVER_KEY).
90
+ private def build_configuration(configuration)
91
+ return Featureflow.configuration if configuration.nil?
92
+ return configuration unless configuration.is_a?(Hash)
93
+
94
+ config = Configuration.new
95
+ configuration.each do |option, value|
96
+ setter = "#{option}="
97
+ # Fail loudly rather than silently dropping a mistyped option — a
98
+ # misspelt `endpoint:` would otherwise quietly poll production.
99
+ unless config.respond_to?(setter)
100
+ raise ArgumentError, "Unknown Featureflow configuration option: #{option}"
101
+ end
102
+ config.public_send(setter, value)
103
+ end
104
+ config
105
+ end
75
106
  end
76
107
  end
@@ -1,33 +1,104 @@
1
+ require 'date'
2
+ require 'time'
3
+
1
4
  module Featureflow
2
5
  class Conditions
6
+ # Mirrors the Node SDK's dateParse: dates compare by instant, not by string,
7
+ # so ISO values with different UTC offsets (e.g. "+04:00" vs "Z") compare correctly.
8
+ #
9
+ # A value carrying no timezone information is read as UTC, per the SDK contract
10
+ # ("A date-only value denotes UTC midnight"): the dashboard's date picker emits
11
+ # date-only values like "2026-07-03", and reading those as *local* midnight would
12
+ # make a scheduled rollout fire at a different instant on every host in a fleet.
13
+ # Values with an explicit offset or "Z" keep their instant, unshifted.
14
+ def self.to_epoch(value)
15
+ case value
16
+ when Time then value.to_f
17
+ when Numeric then value.to_f
18
+ when String
19
+ begin
20
+ parsed = Time.parse(value)
21
+ # Time.parse silently falls back to the process's timezone when the value
22
+ # carries no zone; adding back the offset it applied re-reads the same
23
+ # wall-clock components as UTC. Date._parse only reports :offset when the
24
+ # value actually stated a zone.
25
+ parsed += parsed.utc_offset unless Date._parse(value).key?(:offset)
26
+ parsed.to_f
27
+ rescue ArgumentError, TypeError, RangeError
28
+ nil
29
+ end
30
+ end
31
+ end
32
+
33
+ # Both operands must be strings for a string operator to mean anything.
34
+ def self.both_strings?(a, b)
35
+ a.is_a?(String) && b.is_a?(String)
36
+ end
37
+
38
+ # Numeric-to-numeric or string-to-string only. Mixing the two raises in Ruby, and
39
+ # coercing would answer version comparisons wrongly.
40
+ def self.comparable?(a, b)
41
+ (a.is_a?(Numeric) && b.is_a?(Numeric)) || (a.is_a?(String) && b.is_a?(String))
42
+ end
43
+
3
44
  def self.test(op, a, b)
4
45
  case op
5
46
  when 'equals'
6
47
  a.eql? b[0]
48
+ # The string operators assume both sides are strings. A numeric attribute against
49
+ # `contains` raised NoMethodError straight into the host application — the same
50
+ # fail-closed violation as the invalid regex below, and reachable the same way,
51
+ # from a rule somebody typed into the dashboard. Python already guards these with
52
+ # an explicit type check; this brings Ruby into line. A type mismatch is a
53
+ # no-match, not an exception.
7
54
  when 'contains'
8
- a.include? b[0]
55
+ both_strings?(a, b[0]) && a.include?(b[0])
9
56
  when 'startsWith'
10
- a.start_with? b[0]
57
+ both_strings?(a, b[0]) && a.start_with?(b[0])
11
58
  when 'endsWith'
12
- a.end_with? b[0]
59
+ both_strings?(a, b[0]) && a.end_with?(b[0])
13
60
  when 'matches'
14
- a.match? Regexp.new(b[0])
61
+ # The pattern is untrusted configuration, not code: it comes from a targeting
62
+ # rule somebody typed into the dashboard, so a malformed one like "[unclosed"
63
+ # is a user's mistake rather than a bug in the SDK. The contract
64
+ # (featureflow-client-sdk-testbed/CONTRACT.md, "Operators") therefore requires
65
+ # an invalid regex to return false — a flag SDK must degrade rather than take
66
+ # the host application down with it.
67
+ #
68
+ # Only the regex failure is rescued, never everything: RegexpError for a
69
+ # pattern that will not compile, TypeError for a value that is not a string at
70
+ # all. A genuine bug anywhere else still surfaces.
71
+ begin
72
+ a.match? Regexp.new(b[0])
73
+ rescue RegexpError, TypeError
74
+ false
75
+ end
15
76
  when 'in'
16
77
  b.include? a
17
78
  when 'notIn'
18
79
  !b.include? a
80
+ # Comparing a String with a Numeric raises ArgumentError in Ruby, so an attribute
81
+ # whose type does not match the rule's took the host application down. The
82
+ # contract requires a numeric operator to return false on non-numeric input
83
+ # rather than coercing — "1.10.0" > "1.9.0" must not silently succeed — so a
84
+ # mismatch is simply a no-match. String-to-string comparison is kept, since Ruby
85
+ # orders strings meaningfully and the server behaves the same way.
19
86
  when 'greaterThan'
20
- a > b[0]
87
+ comparable?(a, b[0]) && a > b[0]
21
88
  when 'greaterThanOrEqual'
22
- a >= b[0]
89
+ comparable?(a, b[0]) && a >= b[0]
23
90
  when 'lessThan'
24
- a < b[0]
91
+ comparable?(a, b[0]) && a < b[0]
25
92
  when 'lessThanOrEqual'
26
- a <= b[0]
93
+ comparable?(a, b[0]) && a <= b[0]
27
94
  when 'before'
28
- a < b[0]
95
+ a_time = to_epoch(a)
96
+ b_time = to_epoch(b[0])
97
+ !!(a_time && b_time && a_time < b_time)
29
98
  when 'after'
30
- a > b[0]
99
+ a_time = to_epoch(a)
100
+ b_time = to_epoch(b[0])
101
+ !!(a_time && b_time && a_time > b_time)
31
102
  else
32
103
  false
33
104
  end
@@ -48,7 +48,7 @@ module Featureflow
48
48
 
49
49
  @feature['rules'].each do |rule|
50
50
  next unless EvaluateHelpers.rule_matches(rule, @user)
51
- hash = EvaluateHelpers.calculate_hash(@salt, @feature['key'], @user['id'])
51
+ hash = EvaluateHelpers.calculate_hash(@salt, @feature['key'], @user[:id])
52
52
  variant_value = EvaluateHelpers.get_variant_value(hash)
53
53
  return EvaluateHelpers.get_variant_split_key(rule['variantSplits'], variant_value)
54
54
  end
@@ -1,4 +1,5 @@
1
1
  require 'featureflow/client'
2
+ require 'featureflow/user_builder'
2
3
 
3
4
  module Featureflow
4
5
  class RailsClient
@@ -6,13 +7,31 @@ module Featureflow
6
7
  @request = request
7
8
  end
8
9
 
9
- def evaluate(key, context)
10
- request_context_values = {
10
+ # `user` is whatever Client#evaluate accepts: a String user id, or a user
11
+ # Hash built with Featureflow::UserBuilder ({id:, attributes:}). The current
12
+ # request's ip and url are merged in as implicit attributes so targeting
13
+ # rules can match on them; anything the caller set explicitly wins.
14
+ def evaluate(key, user)
15
+ Featureflow.evaluate(key, with_request_attributes(user))
16
+ end
17
+
18
+ private
19
+
20
+ def with_request_attributes(user)
21
+ user = UserBuilder.new(user).build if user.is_a?(String)
22
+ # Anything else is left alone so Client#evaluate raises its own ArgumentError.
23
+ return user unless user.is_a?(Hash)
24
+
25
+ user.merge(attributes: request_attributes.merge(user[:attributes] || {}))
26
+ end
27
+
28
+ # Attribute keys are Strings throughout the evaluation path (UserBuilder
29
+ # normalises them), so the request attributes are keyed as Strings too.
30
+ def request_attributes
31
+ {
11
32
  'featureflow.ip' => @request.remote_ip,
12
33
  'featureflow.url' => @request.original_url
13
34
  }
14
- context = {key: context, values: {}} if context.is_a?(String)
15
- Featureflow.evaluate(key, key: context[:key], values: request_context_values.merge(context[:values]))
16
35
  end
17
36
  end
18
- end
37
+ end
@@ -9,12 +9,12 @@ module Featureflow
9
9
 
10
10
  def with_attributes(hash)
11
11
  raise ArgumentError, 'Parameter hash must be a Hash' unless hash.is_a?(Hash)
12
- hash = hash.dup
12
+ normalized = {}
13
13
  hash.each do |k, v|
14
- raise ArgumentError, "Value for #{k} must be a valid 'primitive' JSON datatype" unless valid_value?(v)
15
- hash[k.to_s] = h.delete(k) unless k.is_a?(String)
14
+ raise ArgumentError, "Value for #{k} must be a valid 'primitive' JSON datatype" unless valid_attribute?(v)
15
+ normalized[k.to_s] = v
16
16
  end
17
- @attributes = @attributes.merge(hash)
17
+ @attributes = @attributes.merge(normalized)
18
18
  self
19
19
  end
20
20
 
@@ -1,3 +1,3 @@
1
1
  module Featureflow
2
- VERSION = "0.6.0"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -9,7 +9,7 @@ class FeatureflowGenerator < Rails::Generators::Base
9
9
  desc "Configures the featureflow with your API key"
10
10
 
11
11
  def create_initializer_file
12
- unless /^srv-env-[a-f0-9]{32}$/ =~ api_key
12
+ unless /^sdk-srv-env-[a-f0-9]{32}$/ =~ api_key
13
13
  raise Thor::Error, "Invalid featureflow environment api key #{api_key.inspect}\nYou can find your environment api key on your featureflow dashboard at https://[APP-NAME].featureflow.io/"
14
14
  end
15
15
 
metadata CHANGED
@@ -1,85 +1,99 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: featureflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oliver Oldfield-Hodge
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-12-17 00:00:00.000000000 Z
11
+ date: 2026-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: bundler
14
+ name: rake
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.15'
19
+ version: '13.0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.15'
26
+ version: '13.0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '3.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: '3.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rspec
42
+ name: cucumber
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '3.0'
47
+ version: '11.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '3.0'
54
+ version: '11.0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: cucumber
56
+ name: webrick
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.8'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.8'
69
+ - !ruby/object:Gem::Dependency
70
+ name: debug
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - "~>"
60
74
  - !ruby/object:Gem::Version
61
- version: 2.4.0
75
+ version: '1.9'
62
76
  type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
- version: 2.4.0
82
+ version: '1.9'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: excon
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
87
  - - "~>"
74
88
  - !ruby/object:Gem::Version
75
- version: 0.57.0
89
+ version: '1.5'
76
90
  type: :runtime
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
94
  - - "~>"
81
95
  - !ruby/object:Gem::Version
82
- version: 0.57.0
96
+ version: '1.5'
83
97
  description: Connects your server with Featureflow. Create features at https://featureflow.io.
84
98
  email:
85
99
  - contact@featureflow.io
@@ -87,15 +101,9 @@ executables: []
87
101
  extensions: []
88
102
  extra_rdoc_files: []
89
103
  files:
90
- - ".gitignore"
91
- - ".rubocop.yml"
92
104
  - CHANGELOG.md
93
- - Gemfile
94
- - Gemfile.lock
95
105
  - LICENSE
96
106
  - README.md
97
- - Rakefile
98
- - featureflow.gemspec
99
107
  - lib/featureflow.rb
100
108
  - lib/featureflow/client.rb
101
109
  - lib/featureflow/conditions.rb
@@ -110,13 +118,15 @@ files:
110
118
  - lib/featureflow/user_builder.rb
111
119
  - lib/featureflow/version.rb
112
120
  - lib/generators/featureflow_generator.rb
113
- - publish-gem.sh
114
- - test.rb
115
121
  homepage: https://github.com/featureflow/featureflow-ruby-sdk
116
122
  licenses:
117
- - MIT
118
- metadata: {}
119
- post_install_message:
123
+ - Apache-2.0
124
+ metadata:
125
+ homepage_uri: https://github.com/featureflow/featureflow-ruby-sdk
126
+ source_code_uri: https://github.com/featureflow/featureflow-ruby-sdk
127
+ changelog_uri: https://github.com/featureflow/featureflow-ruby-sdk/blob/master/CHANGELOG.md
128
+ bug_tracker_uri: https://github.com/featureflow/featureflow-ruby-sdk/issues
129
+ post_install_message:
120
130
  rdoc_options: []
121
131
  require_paths:
122
132
  - lib
@@ -124,16 +134,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
124
134
  requirements:
125
135
  - - ">="
126
136
  - !ruby/object:Gem::Version
127
- version: '0'
137
+ version: '3.1'
128
138
  required_rubygems_version: !ruby/object:Gem::Requirement
129
139
  requirements:
130
140
  - - ">="
131
141
  - !ruby/object:Gem::Version
132
142
  version: '0'
133
143
  requirements: []
134
- rubyforge_project:
135
- rubygems_version: 2.7.8
136
- signing_key:
144
+ rubygems_version: 3.5.22
145
+ signing_key:
137
146
  specification_version: 4
138
147
  summary: Ruby SDK for Featureflow
139
148
  test_files: []
data/.gitignore DELETED
@@ -1,2 +0,0 @@
1
- pkg
2
- .idea
data/.rubocop.yml DELETED
@@ -1,5 +0,0 @@
1
- Metrics/LineLength:
2
- Max: 120
3
-
4
- Metrics/MethodLength:
5
- Max: 20
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- # Specify your gem's dependencies in featureflow.gemspec
4
- gemspec
data/Gemfile.lock DELETED
@@ -1,53 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- featureflow (0.6.0)
5
- excon (~> 0.57.0)
6
-
7
- GEM
8
- remote: https://rubygems.org/
9
- specs:
10
- builder (3.2.3)
11
- cucumber (2.4.0)
12
- builder (>= 2.1.2)
13
- cucumber-core (~> 1.5.0)
14
- cucumber-wire (~> 0.0.1)
15
- diff-lcs (>= 1.1.3)
16
- gherkin (~> 4.0)
17
- multi_json (>= 1.7.5, < 2.0)
18
- multi_test (>= 0.1.2)
19
- cucumber-core (1.5.0)
20
- gherkin (~> 4.0)
21
- cucumber-wire (0.0.1)
22
- diff-lcs (1.3)
23
- excon (0.57.1)
24
- gherkin (4.1.3)
25
- multi_json (1.12.1)
26
- multi_test (0.1.2)
27
- rake (10.5.0)
28
- rspec (3.6.0)
29
- rspec-core (~> 3.6.0)
30
- rspec-expectations (~> 3.6.0)
31
- rspec-mocks (~> 3.6.0)
32
- rspec-core (3.6.0)
33
- rspec-support (~> 3.6.0)
34
- rspec-expectations (3.6.0)
35
- diff-lcs (>= 1.2.0, < 2.0)
36
- rspec-support (~> 3.6.0)
37
- rspec-mocks (3.6.0)
38
- diff-lcs (>= 1.2.0, < 2.0)
39
- rspec-support (~> 3.6.0)
40
- rspec-support (3.6.0)
41
-
42
- PLATFORMS
43
- ruby
44
-
45
- DEPENDENCIES
46
- bundler (~> 1.15)
47
- cucumber (~> 2.4.0)
48
- featureflow!
49
- rake (~> 10.0)
50
- rspec (~> 3.0)
51
-
52
- BUNDLED WITH
53
- 1.15.3
data/Rakefile DELETED
@@ -1,6 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task :default => :spec
data/featureflow.gemspec DELETED
@@ -1,38 +0,0 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'featureflow/version'
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "featureflow"
8
- spec.version = Featureflow::VERSION
9
- spec.authors = ["Oliver Oldfield-Hodge"]
10
- spec.email = ["contact@featureflow.io"]
11
-
12
- spec.summary = "Ruby SDK for Featureflow"
13
- spec.description = "Connects your server with Featureflow. Create features at https://featureflow.io."
14
- spec.homepage = "https://github.com/featureflow/featureflow-ruby-sdk"
15
- spec.license = "MIT"
16
-
17
- # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
- # to allow pushing to a single host or delete this section to allow pushing to any host.
19
- # if spec.respond_to?(:metadata)
20
- # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
21
- # else
22
- # raise "RubyGems 2.0 or newer is required to protect against " \
23
- # "public gem pushes."
24
- # end
25
-
26
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
- f.match(%r{^(test|spec|features)/})
28
- end
29
- spec.bindir = "exe"
30
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
- spec.require_paths = ["lib"]
32
-
33
- spec.add_development_dependency "bundler", "~> 1.15"
34
- spec.add_development_dependency "rake", "~> 10.0"
35
- spec.add_development_dependency "rspec", "~> 3.0"
36
- spec.add_development_dependency "cucumber", "~> 2.4.0"
37
- spec.add_dependency "excon", "~> 0.57.0"
38
- end
data/publish-gem.sh DELETED
@@ -1 +0,0 @@
1
- gem push
data/test.rb DELETED
@@ -1,33 +0,0 @@
1
- $:.unshift(File.dirname(__FILE__) + "/lib/")
2
-
3
- require 'featureflow'
4
- with_features = [
5
- Featureflow::Feature.create('default', 'variant'),
6
- Featureflow::Feature.create('oli-f1', 'off')
7
- ]
8
-
9
-
10
-
11
- api_key = 'srv-env-'
12
- config = Featureflow::Configuration.new
13
- config.api_key = api_key
14
- #config.endpoint = 'http://localhost:8081'
15
- #config.event_endpoint = 'http://localhost:8081'
16
- config.with_features = with_features
17
-
18
- featureflow_client = Featureflow::Client.new(config)
19
- =begin
20
- featureflow_client = Featureflow::Client.new(api_key: api_key, with_features: with_features, url: 'http://localhost:8081')
21
- =end
22
- user = Featureflow::UserBuilder.new('user1').build
23
- puts('test-integration is on? ' + featureflow_client.evaluate('test-integration', user).on?.to_s)
24
- featureflow_client.evaluate('nooooo', user).on?
25
- featureflow_client.evaluate('default', user).on?
26
-
27
- #
28
- loop do
29
- sleep 10
30
- puts(featureflow_client.evaluate('oli-f1', user).value)
31
- puts(featureflow_client.evaluate('oli-f1', user).is?('extended'))
32
- puts(featureflow_client.evaluate('default', user).on?)
33
- end