From 0e7a39b819524105df296d766e95f41e741e0821 Mon Sep 17 00:00:00 2001 From: Benjamin ter Kuile Date: Thu, 6 Mar 2014 18:08:39 +0100 Subject: [PATCH] Add couchbase with modifications, formalize broadcaster and make testable and some other stuff --- Gemfile | 5 ++ Gemfile.lock | 26 +++++++- app/controllers/application_controller.rb | 15 +---- app/models/list.rb | 3 + app/models/order.rb | 9 +++ app/models/supplier.rb | 29 +++++++++ app/views/layouts/phone.html.slim | 4 +- .../suppliers/application/_head.html.erb | 2 +- config/application.rb | 3 + config/couchbase.yml | 15 +++++ config/initializers/couchbase.rb | 1 + config/initializers/model_broadcast.rb | 3 +- config/initializers/qwaiter_settings.rb | 2 + lib/couchbase-setting.rb | 44 ++++++++++++++ lib/couchbase_doc_store.rb | 3 + lib/couchbase_setting.rb | 3 + lib/couchbase_settings.rb | 3 + lib/qwaiter.rb | 14 +++++ lib/qwaiter/broadcaster.rb | 6 ++ lib/qwaiter/broadcaster/faye.rb | 10 ++++ lib/qwaiter/counter.rb | 27 +++++++++ spec/models/list_spec.rb | 32 ++++++++++ spec/models/order_spec.rb | 60 +++++++++++++++++++ spec/models/supplier_spec.rb | 39 ++++++------ spec/spec_helper.rb | 6 ++ spec/support/in_memory_q_counter.rb | 29 +++++++++ .../matchers/broadcast_to_supplier_matcher.rb | 49 +++++++++++++++ .../matchers/broadcast_to_user_matcher.rb | 49 +++++++++++++++ 28 files changed, 456 insertions(+), 35 deletions(-) create mode 100644 config/couchbase.yml create mode 100644 config/initializers/couchbase.rb create mode 100644 config/initializers/qwaiter_settings.rb create mode 100644 lib/couchbase-setting.rb create mode 100644 lib/couchbase_doc_store.rb create mode 100644 lib/couchbase_setting.rb create mode 100644 lib/couchbase_settings.rb create mode 100644 lib/qwaiter/broadcaster.rb create mode 100644 lib/qwaiter/broadcaster/faye.rb create mode 100644 lib/qwaiter/counter.rb create mode 100644 spec/support/in_memory_q_counter.rb create mode 100644 spec/support/matchers/broadcast_to_supplier_matcher.rb create mode 100644 spec/support/matchers/broadcast_to_user_matcher.rb diff --git a/Gemfile b/Gemfile index 143e2a15..f40a9d22 100644 --- a/Gemfile +++ b/Gemfile @@ -34,6 +34,11 @@ group :assets do end #gem 'less-rails' +# COUCHBASE GEMS +gem 'couchbase' +gem 'map' +gem 'couchbase-docstore' +gem 'couchbase-structures', github: 'bterkuile/couchbase-structures' gem 'couch_potato' , github: 'bterkuile/couch_potato' gem 'simply_stored', path: './../components/simply_stored' #, github: 'bterkuile/simply_stored' diff --git a/Gemfile.lock b/Gemfile.lock index 1053eb85..34dd55c6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,6 +7,15 @@ GIT couchrest (>= 1.0.1) json (~> 1.6) +GIT + remote: git://github.com/bterkuile/couchbase-structures.git + revision: f6f7da685fd8bacd2bd05b55eac73e8b82a8078d + specs: + couchbase-structures (0.1.0) + couchbase (>= 1.2.0.z.beta3) + couchbase-docstore (>= 0.1.2) + couchbase-settings (>= 0.1.0) + GIT remote: git://github.com/bterkuile/devise_simply_stored.git revision: 8fdaed396a7e9566fe6cb59b64d950953c8506a6 @@ -120,7 +129,16 @@ GEM sass (~> 3.1) compass-rails (1.1.2) compass (>= 0.12.2) + connection_pool (1.2.0) cookiejar (0.3.0) + couchbase (1.3.6) + connection_pool (~> 1.0, >= 1.0.0) + multi_json (~> 1.0) + yaji (~> 0.3, >= 0.3.2) + couchbase-docstore (0.3.0) + couchbase (>= 1.2) + couchbase-settings (>= 0.2.7) + couchbase-settings (0.3.4) couchrest (1.2.0) mime-types (~> 1.15) multi_json (~> 1.0) @@ -221,13 +239,14 @@ GEM mail (2.5.4) mime-types (~> 1.16) treetop (~> 1.4.8) + map (6.5.3) method_source (0.8.2) mime-types (1.25.1) mini_magick (3.7.0) subexec (~> 0.2.1) mini_portile (0.5.2) minitest (4.7.5) - multi_json (1.8.2) + multi_json (1.9.0) multipart-post (1.2.0) nokogiri (1.6.1) mini_portile (~> 0.5.0) @@ -353,6 +372,7 @@ GEM websocket-driver (0.3.1) xpath (2.0.0) nokogiri (~> 1.3) + yaji (0.3.5) PLATFORMS ruby @@ -367,6 +387,9 @@ DEPENDENCIES coffee-rails compass-rails couch_potato! + couchbase + couchbase-docstore + couchbase-structures! database_cleaner devise devise_simply_stored! @@ -382,6 +405,7 @@ DEPENDENCIES kaminari launchy letter_opener + map mini_magick omniauth-facebook orm_adapter! diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index a69b4d8e..fcca8d55 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -12,15 +12,11 @@ private end def broadcast_user(uid, event, data = {}) - message = {channel: "/user/#{uid}", data: {event: event, data: data}} - uri = URI.parse(event_host) - Net::HTTP.post_form(uri, :message => message.to_json) + Qwaiter.broadcast_user uid, event, data end def broadcast_supplier(sid, event, data = {}) - message = {channel: "/supplier/#{sid}", data: {event: event, data: data}} - uri = URI.parse(event_host) - Net::HTTP.post_form(uri, :message => message.to_json) + Qwaiter.broadcast_supplier sid, event, data end def set_locale @@ -64,13 +60,6 @@ private end alias json_notice js_notice - # Return the hostname of the event server - def event_host - #TODO: do not do and environment check, its ugly! - "http://#{Rails.env.production? ? 'events.qwaiter.com' : 'localhost'}:9296/faye" - end - helper_method :event_host - def show_404 respond_to do |format| format.html { render 'dashboard/404', layout: true, status: 404} diff --git a/app/models/list.rb b/app/models/list.rb index ee40654e..a69b0616 100644 --- a/app/models/list.rb +++ b/app/models/list.rb @@ -270,6 +270,7 @@ class List return false unless user order = Order.create list: self, supplier: supplier, user: user, section_id: section_id return unless order.id + orders_in_process_count = supplier.increment_orders_in_process_count! loaded_products = self.class.database.load_document products.keys products.each do |product_id, quantity| quantity = quantity.to_i @@ -280,9 +281,11 @@ class List save for user_id in user_ids broadcast_user user_id, 'new_order', order: order.with_products_as_json, total_amount: price + broadcast_user user_id, 'orders_in_process_count', count: orders_in_process_count end broadcast_supplier supplier.id, 'list_update', active_model_serializer.new(self).as_json broadcast_supplier supplier.id, 'new_order', OrderSerializer.new(order) + broadcast_supplier supplier.id, 'orders_in_process_count', count: orders_in_process_count order end diff --git a/app/models/order.rb b/app/models/order.rb index a0948e7c..7cc6e6c8 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -77,20 +77,29 @@ class Order def is_being_processed! self.state = 'active' if save + orders_in_process_count = supplier.decrement_orders_in_process_count! + orders_delivered_count = supplier.increment_orders_delivered_count! for user_id in list.user_ids broadcast_user user_id, 'order_being_processed', id: id, list_id: list_id + broadcast_user user_id, 'orders_in_process_count', count: orders_in_process_count + broadcast_user user_id, 'orders_delivered_count', count: orders_delivered_count end broadcast_supplier supplier_id, 'order_being_processed', id: id, list_id: list_id + broadcast_supplier supplier_id, 'orders_in_process_count', count: orders_in_process_count + broadcast_supplier supplier_id, 'orders_delivered_count', count: orders_delivered_count end end def is_delivered! self.state = 'delivered' if save + orders_delivered_count = supplier.decrement_orders_delivered_count! for user_id in list.user_ids broadcast_user user_id, 'order_being_delivered', id: id, list_id: list_id + broadcast_user user_id, 'orders_delivered_count', count: orders_delivered_count end broadcast_supplier supplier_id, 'order_being_delivered', id: id, list_id: list_id + broadcast_supplier supplier_id, 'orders_delivered_count', count: orders_delivered_count end end diff --git a/app/models/supplier.rb b/app/models/supplier.rb index 307443f6..57683f7d 100644 --- a/app/models/supplier.rb +++ b/app/models/supplier.rb @@ -137,6 +137,35 @@ class Supplier def week_starts_on_monday? true end + + def increment_orders_in_process_count! + Qwaiter::Counter.incr "supplier:#{id}:orders_in_process" + end + + def increment_orders_delivered_count! + Qwaiter::Counter.incr "supplier:#{id}:orders_delivered" + end + + def decrement_orders_in_process_count! + Qwaiter::Counter.decr "supplier:#{id}:orders_in_process" + end + + def decrement_orders_delivered_count! + Qwaiter::Counter.decr "supplier:#{id}:orders_delivered" + end + + def orders_in_process_count + Qwaiter::Counter.get "supplier:#{id}:orders_in_process" + end + + def orders_delivered_count + Qwaiter::Counter.get "supplier:#{id}:orders_delivered" + end + + def active_order_count + orders_in_process_count + orders_delivered_count + end + private def add_section_on_create diff --git a/app/views/layouts/phone.html.slim b/app/views/layouts/phone.html.slim index 8878bc9e..398d5a13 100644 --- a/app/views/layouts/phone.html.slim +++ b/app/views/layouts/phone.html.slim @@ -20,14 +20,14 @@ html lang="en" javascript: var QMobile, Qwaiter, Quser; var data_host = 'http://data.qwaiter.com'; - var event_host = '#{event_host}'; + var event_host = '#{Qwaiter.event_host}'; var $asset_path = '##assets_path##'; var Qstorage = localStorage; - else javascript: var QMobile, Qwaiter, Quser; var data_host = 'http://data.qwaiter.com'; - var event_host = '#{event_host}'; + var event_host = '#{Qwaiter.event_host}'; var $asset_path = '/assets/'; var Qstorage = localStorage; #{user_dynamic_data_host} diff --git a/app/views/suppliers/application/_head.html.erb b/app/views/suppliers/application/_head.html.erb index f3a30546..bbc5da85 100644 --- a/app/views/suppliers/application/_head.html.erb +++ b/app/views/suppliers/application/_head.html.erb @@ -2,6 +2,6 @@ var $locale = '<%= I18n.locale %>'; var supplier_id = '<%= current_supplier.id %>'; var data_host = ''; -var event_host = '<%= event_host %>'; +var event_host = '<%= Qwaiter.event_host %>'; var datepicker_options = {dateFormat: 'yy-mm-dd', firstDay: <%= current_supplier.week_starts_on_monday? ? 1 : 0 %>}; diff --git a/config/application.rb b/config/application.rb index b980e179..98a40a0f 100644 --- a/config/application.rb +++ b/config/application.rb @@ -9,6 +9,9 @@ require 'action_mailer/railtie' require 'rails/test_unit/railtie' require 'sprockets/railtie' +# custom override hack for the couchbase-setting gem, needs to be loaded before other gems, is settings only without dependencies +require File.expand_path('./../../lib/couchbase-setting', __FILE__) + if defined?(Bundler) # If you precompile assets before deploying to production, use this line Bundler.require(*Rails.groups(:assets => %w(development test))) diff --git a/config/couchbase.yml b/config/couchbase.yml new file mode 100644 index 00000000..df1073e0 --- /dev/null +++ b/config/couchbase.yml @@ -0,0 +1,15 @@ +defaults: &defaults + server: 127.0.0.1 + +development: + <<: *defaults + bucket: qwaiter_development + + +test: + <<: *defaults + bucket: qwaiter_test + +production: + <<: *defaults + bucket: qwaiter diff --git a/config/initializers/couchbase.rb b/config/initializers/couchbase.rb new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/config/initializers/couchbase.rb @@ -0,0 +1 @@ + diff --git a/config/initializers/model_broadcast.rb b/config/initializers/model_broadcast.rb index 8d10d000..68301256 100644 --- a/config/initializers/model_broadcast.rb +++ b/config/initializers/model_broadcast.rb @@ -1,3 +1,4 @@ +#TODO: this is really ugly, can cause memory leaks and much more bad stuff. We need a new broadcaster.... require 'simply_stored/couch' module ModelBroadcast def broadcast_supplier(*args) @@ -8,4 +9,4 @@ module ModelBroadcast end end SimplyStored::Couch.send(:include, ModelBroadcast) -SimplyStored::Couch.send(:extend, ModelBroadcast) +#SimplyStored::Couch.send(:extend, ModelBroadcast) # this should never happen!!! diff --git a/config/initializers/qwaiter_settings.rb b/config/initializers/qwaiter_settings.rb new file mode 100644 index 00000000..ea56e7c2 --- /dev/null +++ b/config/initializers/qwaiter_settings.rb @@ -0,0 +1,2 @@ +Qwaiter.event_host = "http://#{Rails.env.production? ? 'events.qwaiter.com' : 'localhost'}:9296/faye" +Qwaiter.broadcaster = Qwaiter::Broadcaster::Faye.new diff --git a/lib/couchbase-setting.rb b/lib/couchbase-setting.rb new file mode 100644 index 00000000..7ee8ba00 --- /dev/null +++ b/lib/couchbase-setting.rb @@ -0,0 +1,44 @@ +# This is a replacement of the couchbase settings gem. The quality of this gem is not sufficient to +# work with. Since couchbase structures/docstore are using CouchbaseSetting we supply the right settings +# here + +module CouchbaseSetting + mattr_accessor :config_type + mattr_accessor :configs + + def self.server + config :server, default: '127.0.0.1' + end + + def self.bucket + config :bucket, default: 'qwaiter' + end + + def self.config(setting, default: nil) + configs[config_type][Rails.env.to_sym][setting] || default + end +end + +CouchbaseSetting.configs = { + supplier_counters: { + development: { + bucket: 'qwaiter_development' + }, + test:{ + bucket: 'qwaiter_development' + }, + production: {} + }, + queue: { + development: { + bucket: 'qwaiter_development' + }, + test:{ + bucket: 'qwaiter_development' + }, + production: {} + } +} + +# only one in use at the moment +CouchbaseSetting.config_type = :supplier_counters diff --git a/lib/couchbase_doc_store.rb b/lib/couchbase_doc_store.rb new file mode 100644 index 00000000..45fdffc2 --- /dev/null +++ b/lib/couchbase_doc_store.rb @@ -0,0 +1,3 @@ +# this file is for backwards compatibility of the couchbase-structures gem since version 0.1.0 requires this file +# while couchbase-docstore is already over to the new require 'couchbase-docstore' syntax (kinda) +require 'couchbase-docstore' diff --git a/lib/couchbase_setting.rb b/lib/couchbase_setting.rb new file mode 100644 index 00000000..035755a2 --- /dev/null +++ b/lib/couchbase_setting.rb @@ -0,0 +1,3 @@ +# this file is for backwards compatibility of the couchbase-structures gem since version 0.1.0 requires this file +# while couchbase-settings is custom for now +require 'couchbase-setting' diff --git a/lib/couchbase_settings.rb b/lib/couchbase_settings.rb new file mode 100644 index 00000000..035755a2 --- /dev/null +++ b/lib/couchbase_settings.rb @@ -0,0 +1,3 @@ +# this file is for backwards compatibility of the couchbase-structures gem since version 0.1.0 requires this file +# while couchbase-settings is custom for now +require 'couchbase-setting' diff --git a/lib/qwaiter.rb b/lib/qwaiter.rb index f7338758..e9602876 100644 --- a/lib/qwaiter.rb +++ b/lib/qwaiter.rb @@ -1,6 +1,20 @@ module Qwaiter + mattr_accessor :event_host + mattr_accessor :broadcaster extend ActiveSupport::Autoload autoload :Distribution autoload :Serializer + autoload :Counter + autoload :Broadcaster + + def self.broadcast_user(uid, event, data) + message = {channel: "/user/#{uid}", data: {event: event, data: data}} + broadcaster.broadcast message + end + + def self.broadcast_supplier(sid, event, data) + message = {channel: "/supplier/#{sid}", data: {event: event, data: data}} + broadcaster.broadcast message + end end diff --git a/lib/qwaiter/broadcaster.rb b/lib/qwaiter/broadcaster.rb new file mode 100644 index 00000000..2a0a1e13 --- /dev/null +++ b/lib/qwaiter/broadcaster.rb @@ -0,0 +1,6 @@ +module Qwaiter + module Broadcaster + extend ActiveSupport::Autoload + autoload :Faye + end +end diff --git a/lib/qwaiter/broadcaster/faye.rb b/lib/qwaiter/broadcaster/faye.rb new file mode 100644 index 00000000..e69b32e0 --- /dev/null +++ b/lib/qwaiter/broadcaster/faye.rb @@ -0,0 +1,10 @@ +module Qwaiter + module Broadcaster + class Faye + def broadcast(message) + @uri ||= URI.parse(Qwaiter.event_host) + Net::HTTP.post_form(@uri, :message => message.to_json) + end + end + end +end diff --git a/lib/qwaiter/counter.rb b/lib/qwaiter/counter.rb new file mode 100644 index 00000000..894d9631 --- /dev/null +++ b/lib/qwaiter/counter.rb @@ -0,0 +1,27 @@ +module Qwaiter + module Counter + mattr_accessor :connection + + # mainly for testing purposes + def self.set(key, value) + connection.set(key, value) + end + + def self.get(key) + connection.get(key).to_i + end + + def self.incr(*args) + connection.incr(*args) + end + + def self.decr(*args) + connection.decr(*args) + end + end +end + +# use the connection from couchbase-structures/documents +# will be overwritten in the specs since flushing the real +# thing is difficult +Qwaiter::Counter.connection = $cb diff --git a/spec/models/list_spec.rb b/spec/models/list_spec.rb index 8dac2652..c4c6db88 100644 --- a/spec/models/list_spec.rb +++ b/spec/models/list_spec.rb @@ -122,4 +122,36 @@ describe List do end end + describe '#place_order' do + + it 'returns an order object' do + list.place_order(user, product.id => 7).should be_a Order + end + + it 'creates an order' do + expect{ list.place_order(user, product.id => 7) }.to change{ Order.count }.by(1) + end + + describe 'broadcasting' do + it 'broadcasts to the user and the supplier the active order counter' do + list.place_order(user, product.id => 7) + + expect{ + list.place_order(user, product.id => 3) + }.to broadcast_to_user(user.id).message('orders_in_process_count').with(count: 2) + + expect{ + list.place_order(user, product.id => 5) + }.to broadcast_to_supplier(supplier.id).message('orders_in_process_count').with(count: 3) + end + end + + it 'sets the list price as kind of caching' do + list.place_order(user, product.id => 7) + list.reload + list.price.should == 3 + end + + describe 'product order creation' + end end diff --git a/spec/models/order_spec.rb b/spec/models/order_spec.rb index b8a33746..aae251ab 100644 --- a/spec/models/order_spec.rb +++ b/spec/models/order_spec.rb @@ -37,4 +37,64 @@ describe Order do it 'paginates' end + describe '#is_being_processed!' do + describe 'broadcasting' do + + it 'broadcasts order info to the user' do + expect{ order.is_being_processed! }.to broadcast_to_user(user.id).message( 'order_being_processed' ).with(id: order.id, list_id: list.id) + end + + describe 'counters' do + before do + # hack some initial values + Qwaiter::Counter.set "supplier:#{supplier.id}:orders_in_process", 7 + Qwaiter::Counter.set "supplier:#{supplier.id}:orders_delivered", 9 + end + + it 'reduces the orders_in_process count and communicates it to user' do + expect{ order.is_being_processed! }.to broadcast_to_user(user.id).message( 'orders_in_process_count' ).with(count: 6) + end + + it 'increases the orders_delivered count and communicates it to user' do + expect{ order.is_being_processed! }.to broadcast_to_user(user.id).message( 'orders_delivered_count' ).with(count: 10) + end + + it 'reduces the orders_in_process count and communicates it to supplier' do + expect{ order.is_being_processed! }.to broadcast_to_supplier(supplier.id).message( 'orders_in_process_count' ).with(count: 6) + end + + it 'increases the orders_in_process count and communicates it to supplier' do + expect{ order.is_being_processed! }.to broadcast_to_supplier(supplier.id).message( 'orders_delivered_count' ).with(count: 10) + end + + end + + it 'broadcasts order info to the supplier' do + expect{ order.is_being_processed! }.to broadcast_to_supplier(supplier.id).message( 'order_being_processed' ).with(id: order.id, list_id: list.id) + end + + end + + end + + describe '#is_delivered!' do + describe 'broadcasting' do + describe 'counters' do + before do + # hack some initial values + Qwaiter::Counter.set "supplier:#{supplier.id}:orders_in_process", 7 + Qwaiter::Counter.set "supplier:#{supplier.id}:orders_delivered", 9 + end + + it 'decreases the orders_delivered count and communicates it to user' do + expect{ order.is_delivered! }.to broadcast_to_user(user.id).message( 'orders_delivered_count' ).with(count: 8) + end + + it 'decreases the orders_in_process count and communicates it to supplier' do + expect{ order.is_delivered! }.to broadcast_to_supplier(supplier.id).message( 'orders_delivered_count' ).with(count: 8) + end + end + end + end + end diff --git a/spec/models/supplier_spec.rb b/spec/models/supplier_spec.rb index 976e4e77..e02c8ff4 100644 --- a/spec/models/supplier_spec.rb +++ b/spec/models/supplier_spec.rb @@ -1,62 +1,67 @@ require 'spec_helper' describe Supplier do - before :each do - @supplier = build :supplier - end + let(:supplier){ build :supplier } # property open describe :open do it 'should be false by default' do - @supplier.open.should == false + supplier.open.should == false end it 'should not be open? by default' do - @supplier.open?.should == false + supplier.open?.should == false end it 'should be closed? by default' do - @supplier.closed?.should == true + supplier.closed?.should == true end describe :mark_as_open! do before :each do - @supplier.mark_as_open! + supplier.mark_as_open! end it 'should be persisted in the database' do - @supplier.reload - @supplier.open.should == true + supplier.reload + supplier.open.should == true end it 'should be open?' do - @supplier.open?.should == true + supplier.open?.should == true end it 'should not be closed?' do - @supplier.closed?.should == false + supplier.closed?.should == false end end describe :mark_as_closed! do before :each do - @supplier.mark_as_open! - @supplier.mark_as_closed! + supplier.mark_as_open! + supplier.mark_as_closed! end it 'should be persisted in the database' do - @supplier.reload - @supplier.open.should == false + supplier.reload + supplier.open.should == false end it 'should be open?' do - @supplier.open?.should == false + supplier.open?.should == false end it 'should not be closed?' do - @supplier.closed?.should == true + supplier.closed?.should == true end end end + describe '#decrement_orders_delivered_count!' do + it 'decreases orders_delivered' do + Qwaiter::Counter.set "supplier:#{supplier.id}:orders_delivered", 9 + supplier.decrement_orders_delivered_count!.should == 8 + end + end + end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 63b3b419..892741a3 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -43,6 +43,10 @@ module SpecSelectorHelpers %x(launchy http://localhost:3000/capybara.html) end end + +# NOT THREADSAFE!!!!!! but good enough for testing since the real couchbase flush is slowwwwww.... +Qwaiter::Counter.connection = InMemoryQCounter.new + RSpec.configure do |config| # == Mock Framework # @@ -56,6 +60,7 @@ RSpec.configure do |config| config.include FactoryAttributesFor config.include Devise::TestHelpers, type: :controller config.include EndWithMatcher + config.include Matchers config.include Features::BasicHelpers, type: :feature config.include SpecRouteHelpers, type: :feature #config.use_transactional_fixtures = true @@ -98,6 +103,7 @@ RSpec.configure do |config| config.before :each do CouchPotato.couchrest_database.recreate! + Qwaiter::Counter.connection.flush end config.before :each, type: :feature do diff --git a/spec/support/in_memory_q_counter.rb b/spec/support/in_memory_q_counter.rb new file mode 100644 index 00000000..7ff5f560 --- /dev/null +++ b/spec/support/in_memory_q_counter.rb @@ -0,0 +1,29 @@ +# This is a non thread safe replacement for the +# couchbase counter mechanism since every test needs +# a clean start and Hash#clear is soooo much faster than +# a couchbase bucket flush +class InMemoryQCounter + STORE = {} + + def get(key) + STORE[key] + end + + def set(key, value) + STORE[key] = value + end + + def incr(key, options = {}) + STORE[key] ||= options[:initial].to_i + STORE[key] += 1 + end + + def decr(key, options = {}) + STORE[key] ||= options[:initial].to_i + STORE[key] -= 1 + end + + def flush + STORE.clear + end +end diff --git a/spec/support/matchers/broadcast_to_supplier_matcher.rb b/spec/support/matchers/broadcast_to_supplier_matcher.rb new file mode 100644 index 00000000..18b8dbdd --- /dev/null +++ b/spec/support/matchers/broadcast_to_supplier_matcher.rb @@ -0,0 +1,49 @@ +module Matchers + class BroadcastToSupplier + class TestBroadcaster + attr_reader :broadcasts + + def initialize + @broadcasts = [] + end + + def broadcast(object) + self.broadcasts << object + end + end + + def initialize(supplier_id) + @supplier_id = supplier_id + end + + def matches?(block) + old_broadcaster = Qwaiter.broadcaster + test_broadcaster = TestBroadcaster.new + Qwaiter.broadcaster = test_broadcaster + block.call + Qwaiter.broadcaster = old_broadcaster + + relevant_broadcasts = test_broadcaster.broadcasts.select{|b| b[:channel] =~ /^\/supplier\/#{@supplier_id}/ && b[:data][:event] == @message} + @failure_debug_content = "was #{relevant_broadcasts.map{|b| b[:data][:data].inspect}.join(" and ")}" + relevant_broadcasts.any?{|b| b[:data][:data] == @target_object} + end + + def message(msg) + @message = msg + self + end + + def with(target_object) + @target_object = target_object + self + end + + def failure_message + "supplier #{@supplier_id} did not receive broadcast #{@message} with #{@target_object.inspect} #{@failure_debug_content}" + end + end + + def broadcast_to_supplier(*args, &block) + BroadcastToSupplier.new(*args, &block) + end +end diff --git a/spec/support/matchers/broadcast_to_user_matcher.rb b/spec/support/matchers/broadcast_to_user_matcher.rb new file mode 100644 index 00000000..4f5bea7a --- /dev/null +++ b/spec/support/matchers/broadcast_to_user_matcher.rb @@ -0,0 +1,49 @@ +module Matchers + class BroadcastToUser + class TestBroadcaster + attr_reader :broadcasts + + def initialize + @broadcasts = [] + end + + def broadcast(object) + self.broadcasts << object + end + end + + def initialize(user_id) + @user_id = user_id + end + + def matches?(block) + old_broadcaster = Qwaiter.broadcaster + test_broadcaster = TestBroadcaster.new + Qwaiter.broadcaster = test_broadcaster + block.call + Qwaiter.broadcaster = old_broadcaster + + relevant_broadcasts = test_broadcaster.broadcasts.select{|b| b[:channel] =~ /^\/user\/#{@user_id}/ && b[:data][:event] == @message} + @failure_debug_content = "was #{relevant_broadcasts.map{|b| b[:data][:data].inspect}.join(" and ")}" + relevant_broadcasts.any?{|b| b[:data][:data] == @target_object} + end + + def message(msg) + @message = msg + self + end + + def with(target_object) + @target_object = target_object + self + end + + def failure_message + "user #{@user_id} did not receive broadcast #{@message} with #{@target_object.inspect} #{@failure_debug_content}" + end + end + + def broadcast_to_user(*args, &block) + BroadcastToUser.new(*args, &block) + end +end