require 'net/http' require 'time' require 'json' require "makitoo/feature_flag/version" module Makitoo module FeatureFlag class MemoryCache def initialize(ttl = 20.seconds) @cache = {} @ttl = ttl end def get(key) if @cache.has_key?(key) if @cache[key][:end] > Time.now() @cache[key][:value] else @cache.delete(key) nil end else nil end end def put(key, value) @cache[key] = { value: value, end: Time.now() + @ttl } end end class Client def initialize(application_id, options = {}) @application_id = application_id @server = if options.has_key?(:server) options[:server] elsif options[:ssl] 'https://features.makitoo.com/api/v1' else 'http://features.makitoo.com/api/v1' end @cache = if options.has_key?(:cache) options[:cache] else MemoryCache.new end @environment_name = if options.has_key?(:environment_name) options[:environment_name] else 'production' end end def is_active(feature_name, user_or_id, default_state = false) user = User.of(user_or_id) feature_configuration = get_feature_configuration(feature_name, user) is_visible = if feature_configuration.nil? default_state else feature_configuration[:state] == 'ON' end event = if is_visible create_seen_event(feature_name, user) else create_skip_event(feature_name, user) end send_event(event) is_visible end def success(feature_name, user_or_id) user = User.of(user_or_id) send_event(create_success_event(feature_name, user)) end def track(event_name, user_or_id, properties = {}) user = User.of(user_or_id) send_event(create_track_event(event_name, user, properties)) end def init_segment(segment_id, user_id) post_json(@server + '/associate-foreign-id', { foreignId: segment_id, source: 'segment.com', installationId: user_id }) end private def refresh_feature_configurations_cache(user) feature_configurations = send_event(create_refresh_event(user)) @cache.put('features.' + user.id, feature_configurations) feature_configurations end def get_feature_configuration(feature_name, user) feature_configurations = @cache.get('features.' + user.id) if feature_configurations.nil? feature_configurations = refresh_feature_configurations_cache(user) end if feature_configurations.nil? nil else feature_configurations[feature_name] end end def create_seen_event(feature_name, user) create_execution_event(feature_name, 'SEEN', user) end def create_skip_event(feature_name, user) create_execution_event(feature_name, 'SKIP', user) end def create_success_event(feature_name, user) create_execution_event(feature_name, 'SUCCESS', user) end def create_execution_event(feature_name, event_type, user) event = create_event(event_type, user) event[:featureName] = feature_name event end def create_track_event(event_name, user, properties) event = create_event('TRACK', user) event[:eventName] = event_name event[:properties] = properties event end def create_refresh_event(user) create_event('REFRESH', user) end def create_event(event_type, user) { eventType: event_type, date: Time.now().iso8601, count: 1, installationId: user.id, context: create_context(user), lib: { version: '0.0.1', type: 'ruby' } } end def post_json(url_str, data) puts url_str url = URI.parse(url_str) req = Net::HTTP::Post.new(url.to_s, 'Content-Type' => 'application/json') req.body = data.to_json res = Net::HTTP.start(url.host, url.port) {|http| http.request(req) } res end def send_event(event) begin res = post_json(@server + '/event?' + URI.encode_www_form({ application: @application_id, environmentName: @environment_name }), event) if !res.body.nil? result = JSON.parse(res.body); feature_configurations = {} result['features'].each{|feature| feature_configurations[feature['name']] = { name: feature['name'], state: feature['state'] } } feature_configurations end rescue end end def create_context(user) { user: user.as_json } end end class User attr_accessor :id def initialize(id, attributes = {}) @id = id @attributes = attributes end def as_json(options = {}) result = @attributes.clone result[:id] = @id result end def self.of(string_or_user) if string_or_user.is_a? String User.new(string_or_user) else string_or_user end end end end end