diff --git a/.octopolo.yml b/.octopolo.yml new file mode 100644 index 0000000..9d27a80 --- /dev/null +++ b/.octopolo.yml @@ -0,0 +1,4 @@ +github_repo: sportngin/breaker-rails-cache-repo +semantic_versioning: true +branches_to_keep: + - master diff --git a/.soyuz.yml b/.soyuz.yml new file mode 100644 index 0000000..e03cc47 --- /dev/null +++ b/.soyuz.yml @@ -0,0 +1,14 @@ +defaults: + deploy_cmds: + - gem build breaker.gemspec + - fury push *.gem + before_deploy_cmds: + - /usr/local/bin/op tag-release + - sed -i "" -e "s/\".*/\"$(git tag| sort -n -t. -k1,1 -k2,2 -k3,3 | tail -1 | sed s/v//)\"/" lib/breaker/rails_cache/version.rb + - git add lib/breaker/rails_cache/version.rb + - git commit -m "Version Bump" && git push + after_deploy_cmds: + - rm *.gem +environments: + - + gemfury: {} diff --git a/.travis.yml b/.travis.yml index 3af9c1a..e588969 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,10 @@ +sudo: false +branches: + only: + - master language: ruby rvm: + - 1.9.3 - 2.1 - 2.2 - - 1.9.3 +script: bundle exec rspec diff --git a/Gemfile b/Gemfile index 16c2aa8..f05ce6d 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,5 @@ source 'https://rubygems.org' -# Specify your gem's dependencies in breaker-dalli-repo.gemspec +# Specify your gem's dependencies in breaker-rails-cache-repo.gemspec +gem 'breaker', :git => 'https://github.com/sportngin/breaker.git' gemspec diff --git a/README.md b/README.md index afbddac..97d19e3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Breaker::Dalli::Repo +# Breaker::RailsCache::Repo -Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/breaker/dalli/repo`. To experiment with that code, run `bin/console` for an interactive prompt. +Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/breaker/rails_cache/repo`. To experiment with that code, run `bin/console` for an interactive prompt. TODO: Delete this and the text above, and describe your gem @@ -9,7 +9,7 @@ TODO: Delete this and the text above, and describe your gem Add this line to your application's Gemfile: ```ruby -gem 'breaker-dalli-repo' +gem 'breaker-rails-cache-repo' ``` And then execute: @@ -18,7 +18,7 @@ And then execute: Or install it yourself as: - $ gem install breaker-dalli-repo + $ gem install breaker-rails-cache-repo ## Usage @@ -32,7 +32,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To ## Contributing -1. Fork it ( https://github.com/[my-github-username]/breaker-dalli-repo/fork ) +1. Fork it ( https://github.com/[my-github-username]/breaker-rails-cache-repo/fork ) 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) diff --git a/bin/console b/bin/console index 6c5425b..34fdf5f 100755 --- a/bin/console +++ b/bin/console @@ -1,7 +1,7 @@ #!/usr/bin/env ruby require "bundler/setup" -require "breaker/dalli/repo" +require "breaker/rails_cache/repo" # You can add fixtures and/or initialization code here to make experimenting # with your gem easier. You can also use a different console, if you like. diff --git a/breaker-dalli-repo.gemspec b/breaker-rails-cache-repo.gemspec similarity index 69% rename from breaker-dalli-repo.gemspec rename to breaker-rails-cache-repo.gemspec index 2069614..cab8673 100644 --- a/breaker-dalli-repo.gemspec +++ b/breaker-rails-cache-repo.gemspec @@ -1,11 +1,11 @@ # coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require 'breaker/dalli/repo/version' +require 'breaker/rails_cache/version' Gem::Specification.new do |spec| - spec.name = "breaker-dalli-repo" - spec.version = Breaker::Dalli::Repo::VERSION + spec.name = "breaker-rails-cache-repo" + spec.version = Breaker::RailsCache::VERSION spec.authors = ["Andy Fleener"] spec.email = ["andrew.fleener@sportngin.com"] @@ -14,16 +14,16 @@ Gem::Specification.new do |spec| end spec.summary = %q{This gem is a memcached implementation of the repo pattern used by the breaker gem} - spec.description = %q{Dalli based repo for the breaker gem} + spec.description = %q{Rails Cache backed repo for the breaker gem} spec.license = "MIT" spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] - spec.add_dependency "dalli" - spec.add_dependency "breaker" + spec.add_dependency "breaker", "~>0.2" + spec.add_dependency "activesupport" - spec.add_development_dependency "bundler", "~> 1.8" - spec.add_development_dependency "rake", "~> 10.0" + spec.add_development_dependency "bundler" + spec.add_development_dependency "rake" spec.add_development_dependency "rspec" end diff --git a/lib/breaker/dalli/repo.rb b/lib/breaker/dalli/repo.rb deleted file mode 100644 index 23e84b7..0000000 --- a/lib/breaker/dalli/repo.rb +++ /dev/null @@ -1,9 +0,0 @@ -require "breaker/dalli/repo/version" - -module Breaker - module Dalli - module Repo - # Your code goes here... - end - end -end diff --git a/lib/breaker/dalli/repo/version.rb b/lib/breaker/dalli/repo/version.rb deleted file mode 100644 index 3ea1f92..0000000 --- a/lib/breaker/dalli/repo/version.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Breaker - module Dalli - module Repo - VERSION = "0.1.0" - end - end -end diff --git a/lib/breaker/rails_cache/fuse.rb b/lib/breaker/rails_cache/fuse.rb new file mode 100644 index 0000000..d39c6f4 --- /dev/null +++ b/lib/breaker/rails_cache/fuse.rb @@ -0,0 +1,85 @@ +module Breaker + module RailsCache + class Fuse + attr_accessor :name, :failure_threshold, :retry_threshold, :retry_timeout, :timeout, :breaker_error_class, :failure_count_ttl + + def initialize(name, options={}) + self.name = name + options = defaults.dup.merge(options) + self.failure_threshold = options[:failure_threshold] + self.retry_timeout = options[:retry_timeout] + self.timeout = options[:timeout] + self.breaker_error_class = options[:breaker_error_class] + self.failure_count_ttl = options[:failure_count_ttl] + self.state || set_value(:state, options[:state]) + end + + def defaults + Repo.config + end + + def update(attributes) + attributes.each do |attr, value| + send("#{attr}=",value) + end + end + + def ==(other) + other.instance_of?(self.class) && name == other.name + end + + def eql?(other) + self == other + end + + def hash + [self.class, self.name].hash + end + + def set_value(key, value, options={}) + Rails.cache.write(key_name(key), value, options) + end + + def get_value(key) + Rails.cache.read(key_name(key)) + end + def inc_value(key, value) + Rails.cache.increment(key_name(key), value, expires_in: failure_count_ttl, initial: 1) + end + + def key_name(key) + "BREAKER_#{self.name}_#{key}" + end + + def state + @state ||= get_value(:state) + end + + def state=(state) + set_value(:state, state) + @state = state + end + + def retry_threshold + @retry_threshold ||= get_value(:retry_threshold) + end + + def retry_threshold=(retry_threshold) + set_value(:retry_threshold, retry_threshold) + @retry_threshold = retry_threshold + end + + def failure_count + @failure_count ||= get_value(:failure_count).to_i + end + + def failure_count=(value) + if @failure_count.nil? || @failure_count.zero? || value.zero? + Rails.cache.write(key_name(:failure_count), 0, raw: true) + end + @failure_count = inc_value(:failure_count, 1) + @failure_count + end + end + end +end diff --git a/lib/breaker/rails_cache/repo.rb b/lib/breaker/rails_cache/repo.rb new file mode 100644 index 0000000..f5b9557 --- /dev/null +++ b/lib/breaker/rails_cache/repo.rb @@ -0,0 +1,34 @@ +require "breaker/rails_cache/version" +require "breaker/rails_cache/fuse" +require "breaker/rails_cache/store" + +module Breaker + module RailsCache + class Repo + + attr_reader :store + + def initialize + @store = Store.new + end + + def self.config + { + failure_threshold: 10, + retry_timeout: 60, + timeout: 5, + breaker_error_class: Timeout::Error, + failure_count: 0, + failure_count_ttl: 300, + state: :closed, + } + end + + def upsert(attributes) + fuse = store.find attributes.fetch(:name) + fuse.update attributes + fuse + end + end + end +end diff --git a/lib/breaker/rails_cache/store.rb b/lib/breaker/rails_cache/store.rb new file mode 100644 index 0000000..977cf6c --- /dev/null +++ b/lib/breaker/rails_cache/store.rb @@ -0,0 +1,16 @@ +module Breaker + module RailsCache + class Store + def initialize + @store = Set.new + end + + def find(name) + Fuse.new(name) + end + end + end +end + + + diff --git a/lib/breaker/rails_cache/version.rb b/lib/breaker/rails_cache/version.rb new file mode 100644 index 0000000..26fab63 --- /dev/null +++ b/lib/breaker/rails_cache/version.rb @@ -0,0 +1,5 @@ +module Breaker + module RailsCache + VERSION = "0.1.0" + end +end diff --git a/spec/breaker/dalli/repo_spec.rb b/spec/breaker/dalli/repo_spec.rb deleted file mode 100644 index ea2e264..0000000 --- a/spec/breaker/dalli/repo_spec.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'spec_helper' - -describe Breaker::Dalli::Repo do - it 'has a version number' do - expect(Breaker::Dalli::Repo::VERSION).not_to be nil - end - -end diff --git a/spec/breaker/rails_cache/fuse_spec.rb b/spec/breaker/rails_cache/fuse_spec.rb new file mode 100644 index 0000000..a5b6a8f --- /dev/null +++ b/spec/breaker/rails_cache/fuse_spec.rb @@ -0,0 +1,63 @@ +require 'spec_helper' + +describe Breaker::RailsCache::Fuse do + subject { Breaker::RailsCache::Fuse.new(:test) } + + it 'uses the Repo config has defaults' do + expect(subject.defaults).to eq(Breaker::RailsCache::Repo.config) + end + + it 'sets default values' do + expect(subject.state).to eq(:closed) + expect(subject.failure_threshold).to eq(10) + expect(subject.timeout).to eq(5) + expect(subject.failure_count).to eq(0) + expect(subject.retry_timeout).to eq(60) + end + + it 'can be updated' do + subject.update(state: :open) + expect(subject.state).to eq(:open) + subject.state = :closed + end + + it 'can detect equality based on fuse name' do + expect(subject).to eq(Breaker::RailsCache::Fuse.new(:test)) + end + + it 'sets a value using rails cache' do + subject + expect(Rails.cache).to receive(:write).with("BREAKER_test_state", :closed, {}) + subject.set_value(:state, :closed) + end + + it 'gets a value using rails cache' do + subject + expect(Rails.cache).to receive(:read).with("BREAKER_test_state") + subject.get_value(:state) + end + + it 'increment a value in rails cache' do + subject + expect(Rails.cache).to receive(:increment).with("BREAKER_test_failure_count", 1, { :expires_in => 300, :initial => 1}) + subject.inc_value(:failure_count, 1) + end + + it 'generates a key name' do + expect(subject.key_name(:test)).to eq("BREAKER_test_test") + end + + it 'can set reset the failure count to the initial value' do + subject.failure_count = 1 + subject.failure_count = 1 + expect(subject.failure_count).to eq(2) + subject.failure_count = 0 + expect(subject.failure_count).to eq(1) + end + + it 'can increment the failure count' do + subject.failure_count = 2 + expect(subject.failure_count).to eq(1) + subject.failure_count = 0 + end +end diff --git a/spec/breaker/rails_cache/repo_spec.rb b/spec/breaker/rails_cache/repo_spec.rb new file mode 100644 index 0000000..eba8466 --- /dev/null +++ b/spec/breaker/rails_cache/repo_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe Breaker::RailsCache::Repo do + let(:fuse) { double(:fuse) } + + it 'has a version number' do + expect(Breaker::RailsCache::VERSION).not_to be nil + end + + it 'has a store' do + expect(subject.store).to be_a Breaker::RailsCache::Store + end + + it 'can create/update a fuse' do + attrs = double(:attrs) + allow(attrs).to receive(:fetch).with(:name) { fuse } + expect(subject.store).to receive(:find) { fuse } + expect(fuse).to receive(:update).with(attrs) + subject.upsert(attrs) + end +end + +describe Breaker::RailsCache::Repo do + subject { Breaker::RailsCache::Repo } + + it 'has a config' do + expect(subject.config).to be_a Hash + end +end + +describe Breaker do + it 'has a rails cache repo' do + expect(subject.repo).to be_a Breaker::RailsCache::Repo + end +end diff --git a/spec/breaker/rails_cache/store_spec.rb b/spec/breaker/rails_cache/store_spec.rb new file mode 100644 index 0000000..2b27b61 --- /dev/null +++ b/spec/breaker/rails_cache/store_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' + +describe Breaker::RailsCache::Store do + it "find creates or finds fuses" do + expect(subject.find(:test)).to be_a Breaker::RailsCache::Fuse + expect(subject.find(:test)).to eq(subject.find(:test)) + end + + it "Running find multiple times only creates one Fuse" do + expect(subject.find(:test)).to eq(subject.find(:test)) + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4442739..f140a7e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,2 +1,11 @@ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) -require 'breaker/dalli/repo' +require 'breaker/rails_cache/repo' +require 'active_support' +require 'breaker' + +module Rails + def self.cache + @cache ||= ActiveSupport::Cache::MemoryStore.new + end +end +Breaker.repo = Breaker::RailsCache::Repo.new