fare 0.1.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.
@@ -0,0 +1,28 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ begin
4
+ require 'cucumber'
5
+ require 'cucumber/rake/task'
6
+
7
+ Cucumber::Rake::Task.new(:features)
8
+
9
+ rescue LoadError
10
+ task :features do
11
+ puts "Cucumber not installed."
12
+ exit 1
13
+ end
14
+ end
15
+
16
+ begin
17
+ require 'rspec/core/rake_task'
18
+ RSpec::Core::RakeTask.new(:spec) do |t|
19
+ t.pattern = "spec"
20
+ end
21
+ rescue LoadError
22
+ task :spec do
23
+ puts "RSpec not installed"
24
+ exit 1
25
+ end
26
+ end
27
+
28
+ task :default => [:spec, :features]
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path("../../lib", __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require "fare"
7
+ require "optparse"
8
+
9
+ command = ARGV.shift
10
+
11
+ def show_help
12
+ puts <<-HELP.gsub(/^ +/, '')
13
+ Usage: fare COMMAND [OPTIONS]
14
+
15
+ Available commands:
16
+
17
+ fare update Parses your configuration file, creates topics, queus, subscriptions and caches it.
18
+ fare subscriber Controls the subscribers.
19
+
20
+ You can run any command with the --help option.
21
+
22
+ Example: fare subscriber --help
23
+ HELP
24
+ end
25
+
26
+ case command
27
+ when "update"
28
+ require "fare/update_cli"
29
+ Fare::UpdateCLI.new(ARGV).call
30
+ when "subscriber"
31
+ require "fare/subscriber_cli"
32
+ Fare::SubscriberCLI.new(ARGV).call
33
+ when "-v", "--version"
34
+ puts "Fare version #{Fare::VERSION}"
35
+ exit 0
36
+ when "help", "--help", "-h"
37
+ show_help
38
+ exit 0
39
+ when "", nil
40
+ show_help
41
+ exit 1
42
+ else
43
+ puts "Unkown command: #{command}"
44
+ puts
45
+ show_help
46
+ exit 1
47
+ end
48
+
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'fare/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "fare"
8
+ spec.version = Fare::VERSION
9
+ spec.authors = ["iain"]
10
+ spec.email = ["iain@iain.nl"]
11
+ spec.description = %q{An event system built on Amazon SNS and SQS}
12
+ spec.summary = %q{An event system built on Amazon SNS and SQS}
13
+ spec.homepage = "https://github.com/yourkarma/fare"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency "aws-sdk"
21
+ spec.add_dependency "daemonic", "~> 0.1.2"
22
+ spec.add_dependency "virtus"
23
+
24
+ spec.add_development_dependency "bundler"
25
+ spec.add_development_dependency "thin"
26
+ spec.add_development_dependency "rake"
27
+ spec.add_development_dependency "rspec"
28
+ spec.add_development_dependency "fake_sqs", "~> 0.0.10"
29
+ spec.add_development_dependency "fake_sns", "~> 0.0.2"
30
+
31
+ spec.add_development_dependency "cucumber"
32
+ spec.add_development_dependency "aruba"
33
+ spec.add_development_dependency "json_expressions"
34
+
35
+ end
@@ -0,0 +1,60 @@
1
+ Feature: Multiqueue
2
+
3
+ Scenario: Different subscribers for different queues
4
+
5
+ Given the Fare config:
6
+ """
7
+ app_name "my_app"
8
+
9
+ publishes subject: "user", action: "login", version: "0.1"
10
+ publishes subject: "user", action: "signup", version: "0.1"
11
+
12
+ subscriber do
13
+
14
+ setup do
15
+ require File.expand_path("../../middleware", __FILE__)
16
+ end
17
+
18
+ stack do
19
+ listen_to subject: "user", action: "login"
20
+ run do
21
+ use RegisterEvent, name: "default-queue"
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ subscriber :other_queue do
28
+
29
+ setup do
30
+ require File.expand_path("../../middleware", __FILE__)
31
+ end
32
+
33
+ stack do
34
+ listen_to subject: "user", action: "login"
35
+ run do
36
+ use RegisterEvent, name: "other-queue"
37
+ end
38
+ end
39
+
40
+ end
41
+ """
42
+
43
+ And a file named "middleware.rb" with:
44
+ """
45
+ class RegisterEvent
46
+ def initialize(app, options = {})
47
+ @app = app
48
+ @options = options
49
+ end
50
+ def call(env)
51
+ puts "Registered event by #{@options[:name]}"
52
+ exit
53
+ end
54
+ end
55
+ """
56
+
57
+ When I publish an event with the subject "user" and action "login"
58
+ And I run `fare subscriber start --name other_queue`
59
+ Then the output should contain "Registered event by other-queue"
60
+ But the output should not contain "Registered event by default-queue"
@@ -0,0 +1,65 @@
1
+ Feature: Multistack
2
+
3
+ Scenario: Different stacks for different messages
4
+
5
+ Given the Fare config:
6
+ """
7
+ app_name "my_app"
8
+
9
+ publishes subject: "user", action: "login", version: "0.1"
10
+ publishes subject: "user", action: "signup", version: "0.1"
11
+
12
+ subscriber do
13
+
14
+ setup do
15
+ require File.expand_path("../../middleware", __FILE__)
16
+ end
17
+
18
+ stack do
19
+ listen_to subject: "user", action: "login"
20
+ run do
21
+ use TestForLogin
22
+ end
23
+ end
24
+
25
+ stack do
26
+ listen_to subject: "user", action: "signup"
27
+ run do
28
+ use TestForSignup
29
+ end
30
+ end
31
+
32
+ end
33
+ """
34
+
35
+ And a file named "middleware.rb" with:
36
+ """
37
+ class TestForLogin
38
+ def initialize(app)
39
+ @app = app
40
+ end
41
+ def call(env)
42
+ puts "Received Login"
43
+ exit
44
+ end
45
+ end
46
+
47
+ class TestForSignup
48
+ def initialize(app)
49
+ @app = app
50
+ end
51
+ def call(env)
52
+ puts "Received Signup"
53
+ exit
54
+ end
55
+ end
56
+ """
57
+
58
+ When I publish an event with the subject "user" and action "login"
59
+ And I run `fare subscriber start`
60
+ Then the output should contain "Received Login"
61
+ But the output should not contain "Received Signup"
62
+
63
+ When I publish an event with the subject "user" and action "signup"
64
+ And I run `fare subscriber start`
65
+ Then the output should contain "Received Signup"
@@ -0,0 +1 @@
1
+ require "aruba/cucumber"
@@ -0,0 +1,40 @@
1
+ Given(/^the Fare config:$/) do |string|
2
+
3
+ extra = %Q|require File.expand_path("../../helper.rb", __FILE__)|
4
+
5
+ write_file "config/fare.rb", "#{extra}\n#{string}"
6
+
7
+ write_file "helper.rb", <<-HELPER
8
+
9
+ AWS.config(
10
+ use_ssl: false,
11
+ sqs_endpoint: "localhost",
12
+ sqs_port: 4568,
13
+ sns_endpoint: "localhost",
14
+ sns_port: 9293,
15
+ access_key_id: "fake access key",
16
+ secret_access_key: "fake secret key",
17
+ )
18
+
19
+ HELPER
20
+
21
+ run_simple("fare update")
22
+ end
23
+
24
+ When(/^I give SNS time to propagate the messages$/) do
25
+ $fake_sns.drain
26
+ end
27
+
28
+ When(/^I publish an event with the subject "(.*?)" and action "(.*?)"$/) do |subject, action|
29
+ write_file "publisher.rb", <<-PUBLISHER
30
+ require "bundler/setup"
31
+ Bundler.require(:default, :test)
32
+ Fare.publish(subject: #{subject.inspect}, action: #{action.inspect}, payload: "payload")
33
+ PUBLISHER
34
+
35
+ run_simple "ruby publisher.rb"
36
+
37
+ $fake_sns.drain
38
+ $fake_sns.reset
39
+ run_simple("fare update --force")
40
+ end
@@ -0,0 +1,95 @@
1
+ Feature: Subscriber
2
+
3
+ Scenario: Running in the foreground
4
+
5
+ Given the Fare config:
6
+ """
7
+ app_name "my_app"
8
+
9
+ publishes subject: "user", action: "login", version: "0.1"
10
+
11
+ subscriber do
12
+
13
+ stack do
14
+ listen_to subject: "user", action: "login"
15
+
16
+ run do
17
+ use Class.new {
18
+
19
+ def initialize(app, options = {})
20
+ @app = app
21
+ end
22
+
23
+ def call(env)
24
+ event = env.fetch(:event)
25
+ puts "Event received: #{event.subject} - #{event.action}"
26
+ exit
27
+ end
28
+
29
+ }
30
+ end
31
+ end
32
+
33
+ end
34
+ """
35
+
36
+ When I publish an event with the subject "user" and action "login"
37
+
38
+ And I run `fare subscriber start`
39
+
40
+ Then the output should contain "Event received: user - login"
41
+
42
+ Scenario: Multiple Middleware
43
+
44
+ Given the Fare config:
45
+ """
46
+ app_name "my_app"
47
+
48
+ publishes subject: "user", action: "login", version: "0.1"
49
+
50
+ subscriber do
51
+
52
+ setup do
53
+ require File.expand_path("../../middleware", __FILE__)
54
+ end
55
+
56
+ stack do
57
+ listen_to subject: "user", action: "login"
58
+
59
+ run do
60
+ use TestMiddlewareOne
61
+ use TestMiddlewareTwo
62
+ end
63
+ end
64
+
65
+ end
66
+ """
67
+
68
+ And a file named "middleware.rb" with:
69
+ """
70
+ class TestMiddlewareOne
71
+ def initialize(app)
72
+ @app = app
73
+ end
74
+ def call(env)
75
+ env[:one] = "Extra Data"
76
+ @app.call(env)
77
+ end
78
+ end
79
+
80
+ class TestMiddlewareTwo
81
+ def initialize(app)
82
+ @app = app
83
+ end
84
+ def call(env)
85
+ puts "Received data: #{env[:one]}"
86
+ exit
87
+ end
88
+ end
89
+ """
90
+
91
+ When I publish an event with the subject "user" and action "login"
92
+
93
+ And I run `fare subscriber start`
94
+
95
+ Then the output should contain "Received data: Extra Data"
@@ -0,0 +1,34 @@
1
+ ENV["RACK_ENV"] = "test"
2
+ require "bundler/setup"
3
+ Bundler.require(:default, :test)
4
+
5
+ require "fare"
6
+
7
+ require "fake_sns/test_integration"
8
+ require "fake_sqs/test_integration"
9
+
10
+ AWS.config(
11
+ use_ssl: false,
12
+ sqs_endpoint: "localhost",
13
+ sqs_port: 4568,
14
+ sns_endpoint: "localhost",
15
+ sns_port: 9293,
16
+ access_key_id: "fake access key",
17
+ secret_access_key: "fake secret key",
18
+ )
19
+
20
+ $fake_sns = FakeSNS::TestIntegration.new(database: ":memory:")
21
+ $fake_sqs = FakeSQS::TestIntegration.new(database: ":memory:")
22
+
23
+ $fake_sns.start
24
+ $fake_sqs.start
25
+
26
+ at_exit {
27
+ $fake_sns.stop
28
+ $fake_sqs.stop
29
+ }
30
+
31
+ Before do
32
+ $fake_sns.reset
33
+ $fake_sqs.reset
34
+ end
@@ -0,0 +1,96 @@
1
+ require "aws-sdk"
2
+ require "verbose_hash_fetch"
3
+ require "base64"
4
+ require "json"
5
+ require "yaml"
6
+ require "digest/md5"
7
+ require "virtus"
8
+ require "forwardable"
9
+
10
+ require "fare/version"
11
+
12
+ require "fare/load_configuration_file"
13
+ require "fare/configuration_dsl"
14
+ require "fare/configuration"
15
+ require "fare/subscriber_stack"
16
+ require "fare/generate_lock_file"
17
+ require "fare/topic"
18
+
19
+ require "fare/queue_adapter"
20
+ require "fare/topic_adapter"
21
+
22
+ require "fare/configuration_when_locked"
23
+ require "fare/publisher"
24
+
25
+ require "fare/subscriber"
26
+ require "fare/event"
27
+
28
+ require "fare/middleware/logging"
29
+ require "fare/middleware/raven"
30
+
31
+ module Fare
32
+ ConfigurationNotFoundError = Class.new(RuntimeError)
33
+ LockFileNotFoundError = Class.new(RuntimeError)
34
+ ChecksumNotMatchingError = Class.new(RuntimeError)
35
+ NoEnvironmentFoundError = Class.new(RuntimeError)
36
+
37
+ class << self
38
+ attr_accessor :queue_adapter, :topic_adapter
39
+ end
40
+ self.queue_adapter = QueueAdapter
41
+ self.topic_adapter = TopicAdapter
42
+
43
+ def self.test_mode!
44
+ require "fare/test_mode"
45
+ @message_list = TestMode::MessageList.new
46
+ self.queue_adapter = @message_list.queue_adapter
47
+ self.topic_adapter = @message_list.topic_adapter
48
+ update(force: true)
49
+ @configuration = nil
50
+ end
51
+
52
+ def self.stubbed_messages
53
+ if @message_list
54
+ @message_list
55
+ else
56
+ fail "Not in test mode. Enable it with: Fare.test_mode!"
57
+ end
58
+ end
59
+
60
+ def self.given_event(*args)
61
+ stubbed_messages.given_event(*args)
62
+ end
63
+
64
+ def self.run(*args)
65
+ stubbed_messages.run(*args)
66
+ end
67
+
68
+ def self.default_config_file
69
+ File.expand_path("config/fare.rb", Dir.pwd)
70
+ end
71
+
72
+ def self.default_environment
73
+ env = ENV["RACK_ENV"] || ENV["RAILS_ENV"]
74
+ if env.nil? && defined?(::Rails)
75
+ env = Rails.env
76
+ end
77
+ env
78
+ end
79
+
80
+ def self.update(options = {})
81
+ GenerateLockFile.new(options).call
82
+ end
83
+
84
+ def self.publish(options)
85
+ Publisher.new(configuration, options).call
86
+ end
87
+
88
+ def self.configuration
89
+ @configuration ||= config({})
90
+ end
91
+
92
+ def self.config(options)
93
+ @configuration = ConfigurationWhenLocked.new(options)
94
+ end
95
+
96
+ end