configcat 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f09ffb7f9d1bba3ecd5da0f306df08dead784a95f2a87e4998d7842ebfdf20b8
4
+ data.tar.gz: fbe49ce6b2cd1e643829c6092aa8d3f8b17393d2f273e5f1d23fee5ebf889b64
5
+ SHA512:
6
+ metadata.gz: ddeb7ec223e24bdd30d2f436fd9c23817b2e77424f1c15ea99e5d7b9c548fe26d2c568307230a42ad8d29541d3b9b09fb09a8440deae4f3014d58bd09298b674
7
+ data.tar.gz: e826984124b63d61b54ecc862caeabf870161fdb712424916d6cc2e7d793dd2770305ab714af97e9a2644048d8b8e520bfb03e2fe4f379b2ec2e3f1999dbeaf5
@@ -0,0 +1,83 @@
1
+ require 'configcat/interfaces'
2
+ require 'concurrent'
3
+
4
+ module ConfigCat
5
+ class AutoPollingCachePolicy < CachePolicy
6
+ def initialize(config_fetcher, config_cache, poll_interval_seconds=60, max_init_wait_time_seconds=5, on_configuration_changed_callback=nil)
7
+ if poll_interval_seconds < 1
8
+ poll_interval_seconds = 1
9
+ end
10
+ if max_init_wait_time_seconds < 0
11
+ max_init_wait_time_seconds = 0
12
+ end
13
+ @_config_fetcher = config_fetcher
14
+ @_config_cache = config_cache
15
+ @_poll_interval_seconds = poll_interval_seconds
16
+ @_max_init_wait_time_seconds = max_init_wait_time_seconds
17
+ @_on_configuration_changed_callback = on_configuration_changed_callback
18
+ @_initialized = false
19
+ @_is_running = false
20
+ @_start_time = Time.now.utc
21
+ @_lock = Concurrent::ReadWriteLock.new()
22
+ @_is_started = Concurrent::Event.new()
23
+ @thread = Thread.new{_run()}
24
+ @_is_started.wait()
25
+ end
26
+
27
+ def _run()
28
+ @_is_running = true
29
+ @_is_started.set()
30
+ loop do
31
+ force_refresh()
32
+ sleep(@_poll_interval_seconds)
33
+ break if !@_is_running
34
+ end
35
+ end
36
+
37
+ def get()
38
+ while !@_initialized && (Time.now.utc < @_start_time + @_max_init_wait_time_seconds)
39
+ sleep(0.5)
40
+ end
41
+ begin
42
+ @_lock.acquire_read_lock()
43
+ return @_config_cache.get()
44
+ ensure
45
+ @_lock.release_read_lock()
46
+ end
47
+ end
48
+
49
+ def force_refresh()
50
+ begin
51
+ @_lock.acquire_read_lock()
52
+ old_configuration = @_config_cache.get()
53
+ ensure
54
+ @_lock.release_read_lock()
55
+ end
56
+ begin
57
+ configuration = @_config_fetcher.get_configuration_json()
58
+ begin
59
+ @_lock.acquire_write_lock()
60
+ @_config_cache.set(configuration)
61
+ @_initialized = true
62
+ ensure
63
+ @_lock.release_write_lock()
64
+ end
65
+ begin
66
+ if !@_on_configuration_changed_callback.equal?(nil) && configuration != old_configuration
67
+ @_on_configuration_changed_callback.call()
68
+ end
69
+ rescue Exception => e
70
+ ConfigCat.logger.error "threw exception #{e.class}:'#{e}'"
71
+ ConfigCat.logger.error "stacktrace: #{e.backtrace}"
72
+ end
73
+ rescue StandardError => e
74
+ ConfigCat.logger.error "threw exception #{e.class}:'#{e}'"
75
+ ConfigCat.logger.error "stacktrace: #{e.backtrace}"
76
+ end
77
+ end
78
+
79
+ def stop()
80
+ @_is_running = false
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,17 @@
1
+ require 'configcat/interfaces'
2
+
3
+ module ConfigCat
4
+ class InMemoryConfigCache < ConfigCache
5
+ def initialize()
6
+ @_value = nil
7
+ end
8
+
9
+ def get()
10
+ return @_value
11
+ end
12
+
13
+ def set(value)
14
+ @_value = value
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,69 @@
1
+ require 'configcat/interfaces'
2
+ require 'configcat/configcache'
3
+ require 'configcat/configfetcher'
4
+ require 'configcat/autopollingcachepolicy'
5
+ require 'configcat/manualpollingcachepolicy'
6
+ require 'configcat/lazyloadingcachepolicy'
7
+ require 'configcat/rolloutevaluator'
8
+
9
+ module ConfigCat
10
+ class ConfigCatClient
11
+ def initialize(api_key,
12
+ poll_interval_seconds=60,
13
+ max_init_wait_time_seconds=5,
14
+ on_configuration_changed_callback=nil,
15
+ cache_time_to_live_seconds=60,
16
+ config_cache_class=nil,
17
+ base_url=nil)
18
+ if api_key === nil
19
+ raise ConfigCatClientException, "API Key is required."
20
+ end
21
+ @_api_key = api_key
22
+
23
+ if config_cache_class
24
+ @_config_cache = config_cache_class.new()
25
+ else
26
+ @_config_cache = InMemoryConfigCache.new()
27
+ end
28
+
29
+ if poll_interval_seconds > 0
30
+ @_config_fetcher = CacheControlConfigFetcher.new(api_key, "p", base_url)
31
+ @_cache_policy = AutoPollingCachePolicy.new(@_config_fetcher, @_config_cache, poll_interval_seconds, max_init_wait_time_seconds, on_configuration_changed_callback)
32
+ else
33
+ if cache_time_to_live_seconds > 0
34
+ @_config_fetcher = CacheControlConfigFetcher.new(api_key, "l", base_url)
35
+ @_cache_policy = LazyLoadingCachePolicy.new(@_config_fetcher, @_config_cache, cache_time_to_live_seconds)
36
+ else
37
+ @_config_fetcher = CacheControlConfigFetcher.new(api_key, "m", base_url)
38
+ @_cache_policy = ManualPollingCachePolicy.new(@_config_fetcher, @_config_cache)
39
+ end
40
+ end
41
+ end
42
+
43
+ def get_value(key, default_value, user=nil)
44
+ config = @_cache_policy.get()
45
+ if config === nil
46
+ return default_value
47
+ end
48
+ return RolloutEvaluator.evaluate(key, user, default_value, config)
49
+ end
50
+
51
+ def get_all_keys()
52
+ config = @_cache_policy.get()
53
+ if config === nil
54
+ return []
55
+ end
56
+ return config.keys
57
+ end
58
+
59
+ def force_refresh()
60
+ @_cache_policy.force_refresh()
61
+ end
62
+
63
+ def stop()
64
+ @_cache_policy.stop()
65
+ @_config_fetcher.close()
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,45 @@
1
+ require 'configcat/interfaces'
2
+ require 'configcat/version'
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'json'
6
+
7
+ module ConfigCat
8
+ BASE_URL = "https://cdn.configcat.com"
9
+ BASE_PATH = "configuration-files/"
10
+ BASE_EXTENSION = "/config_v2.json"
11
+
12
+ class CacheControlConfigFetcher < ConfigFetcher
13
+ def initialize(api_key, mode, base_url=nil)
14
+ @_api_key = api_key
15
+ @_headers = {"User-Agent" => ((("ConfigCat-Ruby/") + mode) + ("-")) + VERSION, "X-ConfigCat-UserAgent" => ((("ConfigCat-Ruby/") + mode) + ("-")) + VERSION, "Content-Type" => "application/json"}
16
+ if !base_url.equal?(nil)
17
+ @_base_url = base_url.chomp("/")
18
+ else
19
+ @_base_url = BASE_URL
20
+ end
21
+ uri = URI.parse(@_base_url)
22
+ @_http = Net::HTTP.new(uri.host, uri.port)
23
+ @_http.use_ssl = true if uri.scheme == 'https'
24
+ @_http.open_timeout = 10 # in seconds
25
+ @_http.read_timeout = 30 # in seconds
26
+ end
27
+
28
+ def get_configuration_json()
29
+ ConfigCat.logger.debug "Fetching configuration from ConfigCat"
30
+ uri = URI.parse((((@_base_url + ("/")) + BASE_PATH) + @_api_key) + BASE_EXTENSION)
31
+ request = Net::HTTP::Get.new(uri.request_uri, @_headers)
32
+ response = @_http.request(request)
33
+ json = JSON.parse(response.body)
34
+ # TODO: check cached entry
35
+ ConfigCat.logger.debug "ConfigCat configuration json fetch response code:#{response.code} Cached:#{response['ETag']}"
36
+ return json
37
+ end
38
+
39
+ def close()
40
+ if @_http
41
+ @_http = nil
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,71 @@
1
+ module ConfigCat
2
+
3
+ class ConfigFetcher
4
+ #
5
+ # Config fetcher interface
6
+ #
7
+
8
+ def get_configuration_json()
9
+ #
10
+ # :return: Returns the configuration json Dictionary
11
+ #
12
+ end
13
+
14
+ def close()
15
+ #
16
+ # Closes the ConfigFetcher's resources
17
+ #
18
+ end
19
+ end
20
+
21
+ class ConfigCache
22
+ #
23
+ # Config cache interface
24
+ #
25
+
26
+ def get()
27
+ #
28
+ # :returns the config json object from the cache
29
+ #
30
+ end
31
+
32
+ def set(value)
33
+ #
34
+ # Sets the config json cache.
35
+ #
36
+ end
37
+ end
38
+
39
+ class CachePolicy
40
+ #
41
+ # Config cache interface
42
+ #
43
+
44
+ def get()
45
+ #
46
+ # :returns the config json object from the cache
47
+ #
48
+ end
49
+
50
+ def force_refresh()
51
+ #
52
+ #
53
+ # :return:
54
+ #
55
+ end
56
+
57
+ def stop()
58
+ #
59
+ #
60
+ # :return:
61
+ #
62
+ end
63
+ end
64
+
65
+ class ConfigCatClientException < Exception
66
+ #
67
+ # Generic ConfigCatClientException
68
+ #
69
+ end
70
+
71
+ end
@@ -0,0 +1,62 @@
1
+ require 'configcat/interfaces'
2
+ require 'concurrent'
3
+
4
+ module ConfigCat
5
+ class LazyLoadingCachePolicy < CachePolicy
6
+
7
+ def initialize(config_fetcher, config_cache, cache_time_to_live_seconds=60)
8
+ if cache_time_to_live_seconds < 1
9
+ cache_time_to_live_seconds = 1
10
+ end
11
+ @_config_fetcher = config_fetcher
12
+ @_config_cache = config_cache
13
+ @_cache_time_to_live = cache_time_to_live_seconds
14
+ @_lock = Concurrent::ReadWriteLock.new()
15
+ @_last_updated = nil
16
+ end
17
+
18
+ def get()
19
+ begin
20
+ @_lock.acquire_read_lock()
21
+ utc_now = Time.now.utc
22
+ if !@_last_updated.equal?(nil) && (@_last_updated + @_cache_time_to_live > utc_now)
23
+ config = @_config_cache.get()
24
+ if !config.equal?(nil)
25
+ return config
26
+ end
27
+ end
28
+ ensure
29
+ @_lock.release_read_lock()
30
+ end
31
+ force_refresh()
32
+ begin
33
+ @_lock.acquire_read_lock()
34
+ config = @_config_cache.get()
35
+ return config
36
+ ensure
37
+ @_lock.release_read_lock()
38
+ end
39
+ end
40
+
41
+ def force_refresh()
42
+ begin
43
+ configuration = @_config_fetcher.get_configuration_json()
44
+ begin
45
+ @_lock.acquire_write_lock()
46
+ @_config_cache.set(configuration)
47
+ @_last_updated = Time.now.utc
48
+ ensure
49
+ @_lock.release_write_lock()
50
+ end
51
+ rescue StandardError => e
52
+ ConfigCat.logger.error "threw exception #{e.class}:'#{e}'"
53
+ ConfigCat.logger.error "stacktrace: #{e.backtrace}"
54
+ end
55
+ end
56
+
57
+ def stop()
58
+ end
59
+ end
60
+
61
+ end
62
+
@@ -0,0 +1,41 @@
1
+ require 'configcat/interfaces'
2
+ require 'concurrent'
3
+
4
+ module ConfigCat
5
+ class ManualPollingCachePolicy < CachePolicy
6
+ def initialize(config_fetcher, config_cache)
7
+ @_config_fetcher = config_fetcher
8
+ @_config_cache = config_cache
9
+ @_lock = Concurrent::ReadWriteLock.new()
10
+ end
11
+
12
+ def get()
13
+ begin
14
+ @_lock.acquire_read_lock()
15
+ config = @_config_cache.get()
16
+ return config
17
+ ensure
18
+ @_lock.release_read_lock()
19
+ end
20
+ end
21
+
22
+ def force_refresh()
23
+ begin
24
+ configuration = @_config_fetcher.get_configuration_json()
25
+ begin
26
+ @_lock.acquire_write_lock()
27
+ @_config_cache.set(configuration)
28
+ ensure
29
+ @_lock.release_write_lock()
30
+ end
31
+ rescue StandardError => e
32
+ ConfigCat.logger.error "threw exception #{e.class}:'#{e}'"
33
+ ConfigCat.logger.error "stacktrace: #{e.backtrace}"
34
+ end
35
+ end
36
+
37
+ def stop()
38
+ # pass
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,72 @@
1
+ require 'configcat/user'
2
+ require 'digest'
3
+
4
+ module ConfigCat
5
+ class RolloutEvaluator
6
+
7
+ def self.evaluate(key, user, default_value, config)
8
+ if !user.equal?(nil) && !user.class.equal?(User)
9
+ ConfigCat.logger.warn "User parameter is not an instance of User type."
10
+ user = nil
11
+ end
12
+ setting_descriptor = config.fetch(key, nil)
13
+ if setting_descriptor === nil
14
+ ConfigCat.logger.warn "Could not find setting by key, returning default value. Key: #{key}"
15
+ return default_value
16
+ end
17
+ if user === nil
18
+ return setting_descriptor.fetch("Value", default_value)
19
+ end
20
+ rollout_rules = setting_descriptor.fetch("RolloutRules", [])
21
+ for rollout_rule in rollout_rules
22
+ comparison_attribute = rollout_rule.fetch("ComparisonAttribute")
23
+ user_value = user.get_attribute(comparison_attribute)
24
+ if user_value === nil || !user_value
25
+ next
26
+ end
27
+ comparison_value = rollout_rule.fetch("ComparisonValue", nil)
28
+ comparator = rollout_rule.fetch("Comparator", nil)
29
+ value = rollout_rule.fetch("Value", nil)
30
+ if comparator == 0
31
+ if comparison_value.to_s.split(",").map { |x| x.strip() }.include?(user_value.to_s)
32
+ return value
33
+ end
34
+ else
35
+ if comparator == 1
36
+ if !comparison_value.to_s.split(",").map { |x| x.strip() }.include?(user_value.to_s)
37
+ return value
38
+ end
39
+ else
40
+ if comparator == 2
41
+ if user_value.to_s.include?(comparison_value.to_s)
42
+ return value
43
+ end
44
+ else
45
+ if comparator == 3
46
+ if !user_value.to_s.include?(comparison_value.to_s)
47
+ return value
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ rollout_percentage_items = setting_descriptor.fetch("RolloutPercentageItems", [])
55
+ if rollout_percentage_items.size > 0
56
+ user_key = user.get_identifier()
57
+ hash_candidate = ("%s%s" % [key, user_key]).encode("utf-8")
58
+ hash_val = Digest::SHA1.hexdigest(hash_candidate)[0...7].to_i(base=16) % 100
59
+ bucket = 0
60
+ for rollout_percentage_item in rollout_percentage_items || []
61
+ bucket += rollout_percentage_item.fetch("Percentage", 0)
62
+ if hash_val < bucket
63
+ return rollout_percentage_item.fetch("Value", nil)
64
+ end
65
+ end
66
+ end
67
+ return setting_descriptor.fetch("Value", default_value)
68
+ end
69
+ end
70
+
71
+ end
72
+
@@ -0,0 +1,37 @@
1
+ module ConfigCat
2
+
3
+ class User
4
+ #
5
+ # The user object for variation evaluation
6
+ #
7
+
8
+ PREDEFINED = ["identifier", "email", "country"]
9
+
10
+ def initialize(identifier, email: nil, country: nil, custom: nil)
11
+ @__identifier = identifier
12
+ @__data = {"identifier" => identifier, "email" => email, "country" => country}
13
+ @__custom = custom
14
+ end
15
+
16
+ def get_identifier()
17
+ return @__identifier
18
+ end
19
+
20
+ def get_attribute(attribute)
21
+ attribute = attribute.to_s.downcase()
22
+ if PREDEFINED.include?(attribute)
23
+ return @__data[attribute]
24
+ end
25
+
26
+ if !@__custom.equal?(nil)
27
+ @__custom.each do |customField, customValue|
28
+ if customField.to_s.downcase() == attribute
29
+ return customValue
30
+ end
31
+ end
32
+ end
33
+ return nil
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,3 @@
1
+ module ConfigCat
2
+ VERSION = "0.0.1"
3
+ end
data/lib/configcat.rb ADDED
@@ -0,0 +1,80 @@
1
+ require 'configcat/interfaces'
2
+ require 'configcat/configcatclient'
3
+ require 'configcat/user'
4
+ require 'logger'
5
+
6
+ module ConfigCat
7
+
8
+ @logger = Logger.new(STDOUT, level: Logger::INFO)
9
+ class << self
10
+ attr_accessor :logger
11
+ end
12
+
13
+ def ConfigCat.create_client(api_key)
14
+ #
15
+ # Create an instance of ConfigCatClient and setup Auto Poll mode with default options
16
+ #
17
+ # :param api_key: ConfigCat ApiKey to access your configuration.
18
+ #
19
+ return create_client_with_auto_poll(api_key)
20
+ end
21
+
22
+ def ConfigCat.create_client_with_auto_poll(api_key, poll_interval_seconds: 60, max_init_wait_time_seconds: 5, on_configuration_changed_callback: nil, config_cache_class: nil, base_url: nil)
23
+ #
24
+ # Create an instance of ConfigCatClient and setup Auto Poll mode with custom options
25
+ #
26
+ # :param api_key: ConfigCat ApiKey to access your configuration.
27
+ # :param poll_interval_seconds: The client's poll interval in seconds. Default: 60 seconds.
28
+ # :param on_configuration_changed_callback: You can subscribe to configuration changes with this callback
29
+ # :param max_init_wait_time_seconds: maximum waiting time for first configuration fetch in polling mode.
30
+ # :param config_cache_class: If you want to use custom caching instead of the client's default InMemoryConfigCache,
31
+ # You can provide an implementation of ConfigCache.
32
+ # :param base_url: You can set a base_url if you want to use a proxy server between your application and ConfigCat
33
+ #
34
+ if api_key === nil
35
+ raise ConfigCatClientException, "API Key is required."
36
+ end
37
+ if poll_interval_seconds < 1
38
+ poll_interval_seconds = 1
39
+ end
40
+ if max_init_wait_time_seconds < 0
41
+ max_init_wait_time_seconds = 0
42
+ end
43
+ return ConfigCatClient.new(api_key, poll_interval_seconds, max_init_wait_time_seconds, on_configuration_changed_callback, 0, config_cache_class, base_url)
44
+ end
45
+
46
+ def ConfigCat.create_client_with_lazy_load(api_key, cache_time_to_live_seconds: 60, config_cache_class: nil, base_url: nil)
47
+ #
48
+ # Create an instance of ConfigCatClient and setup Lazy Load mode with custom options
49
+ #
50
+ # :param api_key: ConfigCat ApiKey to access your configuration.
51
+ # :param cache_time_to_live_seconds: The cache TTL.
52
+ # :param config_cache_class: If you want to use custom caching instead of the client's default InMemoryConfigCache,
53
+ # You can provide an implementation of ConfigCache.
54
+ # :param base_url: You can set a base_url if you want to use a proxy server between your application and ConfigCat
55
+ #
56
+ if api_key === nil
57
+ raise ConfigCatClientException, "API Key is required."
58
+ end
59
+ if cache_time_to_live_seconds < 1
60
+ cache_time_to_live_seconds = 1
61
+ end
62
+ return ConfigCatClient.new(api_key, 0, 0, nil, cache_time_to_live_seconds, config_cache_class, base_url)
63
+ end
64
+
65
+ def ConfigCat.create_client_with_manual_poll(api_key, config_cache_class: nil, base_url: nil)
66
+ #
67
+ # Create an instance of ConfigCatClient and setup Manual Poll mode with custom options
68
+ #
69
+ # :param api_key: ConfigCat ApiKey to access your configuration.
70
+ # :param config_cache_class: If you want to use custom caching instead of the client's default InMemoryConfigCache,
71
+ # You can provide an implementation of ConfigCache.
72
+ # :param base_url: You can set a base_url if you want to use a proxy server between your application and ConfigCat
73
+ #
74
+ if api_key === nil
75
+ raise ConfigCatClientException, "API Key is required."
76
+ end
77
+ return ConfigCatClient.new(api_key, 0, 0, nil, 0, config_cache_class, base_url)
78
+ end
79
+
80
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: configcat
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - ConfigCat
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-10-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: concurrent-ruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: ConfigCat is a configuration as a service that lets you manage your features
56
+ and configurations without actually deploying new code.
57
+ email:
58
+ - developer@configcat.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - lib/configcat.rb
64
+ - lib/configcat/autopollingcachepolicy.rb
65
+ - lib/configcat/configcache.rb
66
+ - lib/configcat/configcatclient.rb
67
+ - lib/configcat/configfetcher.rb
68
+ - lib/configcat/interfaces.rb
69
+ - lib/configcat/lazyloadingcachepolicy.rb
70
+ - lib/configcat/manualpollingcachepolicy.rb
71
+ - lib/configcat/rolloutevaluator.rb
72
+ - lib/configcat/user.rb
73
+ - lib/configcat/version.rb
74
+ homepage: https://configcat.com
75
+ licenses:
76
+ - MIT
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - "~>"
85
+ - !ruby/object:Gem::Version
86
+ version: '2.2'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 2.7.3
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: ConfigCat SDK for Ruby.
98
+ test_files: []