grapeape 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d68ce46191052f09bdbbf499ebae1fd5bb9e48d9
4
+ data.tar.gz: 38e7d9103f7f8c389c7db49bbf1653cc6de502f6
5
+ SHA512:
6
+ metadata.gz: edb0b9d3ac677a0145999073dee62348ecd80a21692ad64a4949609d2fa0018a186384983b1bbcefd1fc996d8a5b6c77f0a49ea717433393e628f0ea5daad6b3
7
+ data.tar.gz: 555485bfe9218645b428328ecd0d3eeab1806683ecb8192b8cd1e4a99e4af11ed6f10536bc0212e83cd0085a342b5ce3ae1885f79e1bc8b93def986cb0b54da2
@@ -0,0 +1,26 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ lib/bundler/man
9
+ pkg
10
+ rdoc
11
+ spec/reports
12
+ test/tmp
13
+ test/version_tmp
14
+ tmp
15
+
16
+ # YARD artifacts
17
+ .yardoc
18
+ _yardoc
19
+ doc/
20
+
21
+ # Rubymine artifacts
22
+ .idea/
23
+
24
+ # RVM
25
+ .ruby-version
26
+ .ruby-gemset
@@ -0,0 +1,4 @@
1
+ rvm:
2
+ - 2.0.0
3
+ services:
4
+ - rabbitmq
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+ ruby '2.0.0'
3
+
4
+ # Specify your gem's dependencies in grapeape.gemspec
5
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 David Justice
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,38 @@
1
+ # GrapeApe
2
+
3
+ [![Code Climate](https://codeclimate.com/github/devigned/grapeape.png)](https://codeclimate.com/github/devigned/grapeape)
4
+ [![Build Status](https://travis-ci.org/devigned/grapeape.png)](https://travis-ci.org/devigned/grapeape)
5
+ [![Coverage Status](https://coveralls.io/repos/devigned/grapeape/badge.png?branch=master)](https://coveralls.io/r/devigned/grapeape?branch=master)
6
+
7
+ Eventmachine driven distributed web API in ruby. The project stands up an event driven web API
8
+ ([goliath](http://postrank-labs.github.io/goliath/) / [grape](http://intridea.github.io/grape/)) backed by
9
+ [AMQP](http://www.amqp.org/). AMQP is used to route messages from the web to a collection of event driven worker
10
+ processes in an RPC flow.
11
+
12
+ Note: this is not ready for primetime
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ gem 'grapeape'
19
+
20
+ And then execute:
21
+
22
+ $ bundle
23
+
24
+ Or install it yourself as:
25
+
26
+ $ gem install grapeape
27
+
28
+ ## Usage
29
+
30
+ TODO: Write usage instructions here
31
+
32
+ ## Contributing
33
+
34
+ 1. Fork it
35
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
36
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
37
+ 4. Push to the branch (`git push origin my-new-feature`)
38
+ 5. Create new Pull Request
@@ -0,0 +1,19 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rubygems'
3
+ require 'rake'
4
+
5
+ def safe_load
6
+ begin
7
+ yield
8
+ rescue LoadError => ex
9
+ puts 'Error loading rake tasks, but will continue...'
10
+ puts ex.message
11
+ end
12
+ end
13
+
14
+ safe_load do
15
+ require 'rspec/core/rake_task'
16
+ RSpec::Core::RakeTask.new(:spec)
17
+ end
18
+
19
+ task :default => [:spec]
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+ ruby '2.0.0'
3
+
4
+ gem 'grapeape', path: '../../'
@@ -0,0 +1,2 @@
1
+ web: bundle exec ruby api.rb -sv
2
+ worker: bundle exec ruby worker.rb
@@ -0,0 +1,7 @@
1
+ require 'grape_ape'
2
+
3
+ class Api < GrapeApe::API
4
+ format :json
5
+
6
+ get '/', routing_key: 'foo', method: 'hello_world'
7
+ end
@@ -0,0 +1,9 @@
1
+ require 'grape_ape'
2
+
3
+ class Worker < GrapeApe::Worker
4
+ routing_key :foo
5
+
6
+ def hello_world(request)
7
+ 'Hello World!!'
8
+ end
9
+ end
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'grape_ape/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.platform = Gem::Platform::RUBY
8
+ s.name = 'grapeape'
9
+ s.version = GrapeApe::VERSION
10
+ s.authors = ['David Justice']
11
+ s.email = %w(david@devigned.com)
12
+ s.description = 'Message based, event driven web dsl in ruby'
13
+ s.summary = <<-MSG
14
+ Message based, event driven web dsl in ruby. The project stands up an event driven web (goliath / grape) dsl backed by
15
+ AMQP. AMQP is used to route messages from the web to a collection of event driven worker processes in an RPC flow.
16
+
17
+ This could be used to set up a quick [CQRS](http://martinfowler.com/bliki/CQRS.html) architecture.
18
+ MSG
19
+ s.homepage = ''
20
+ s.license = 'MIT'
21
+ s.required_ruby_version = '>= 2.0.0'
22
+
23
+ s.files = `git ls-files`.split($/)
24
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
25
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
26
+ s.require_paths = %w(lib)
27
+
28
+ s.add_development_dependency 'bundler', '~> 1.3'
29
+ s.add_development_dependency 'rake'
30
+ s.add_development_dependency 'rspec'
31
+ s.add_development_dependency 'rack-test'
32
+ s.add_development_dependency 'em-http-request'
33
+ s.add_development_dependency 'coveralls'
34
+
35
+ s.add_dependency 'goliath'
36
+ s.add_dependency 'grape'
37
+ s.add_dependency 'amqp'
38
+ s.add_dependency 'activesupport'
39
+ end
@@ -0,0 +1,15 @@
1
+ require 'hashie'
2
+ require 'active_support/json'
3
+ require 'active_support/core_ext/hash/indifferent_access'
4
+ require 'grape'
5
+ require 'grape_ape/version'
6
+ require 'amqp'
7
+
8
+ module GrapeApe
9
+ autoload :API, 'grape_ape/api'
10
+ autoload :Dispatcher, 'grape_ape/dispatcher'
11
+ autoload :Endpoint, 'grape_ape/endpoint'
12
+ autoload :Worker, 'grape_ape/worker'
13
+ autoload :Server, 'grape_ape/server'
14
+ end
15
+
@@ -0,0 +1,48 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+ require 'grape_ape/goliath/application_patch'
3
+
4
+ module GrapeApe
5
+ class API < Grape::API
6
+ REQUIRED_ROUTE_KEYS = [:routing_key, :method]
7
+
8
+ cattr_accessor :app_class
9
+
10
+ class << self
11
+ def inherited(subclass)
12
+ super
13
+ GrapeApe::API.app_class = subclass.name if defined?(GrapeApe::API)
14
+ end
15
+
16
+ def route(methods, paths = %w(/), route_options = {}, &block)
17
+ if ape_route?(route_options)
18
+ endpoint_options = {
19
+ :method => methods,
20
+ :path => paths,
21
+ :route_options => (@namespace_description || {}).deep_merge(@last_description || {}).deep_merge(route_options || {})
22
+ }
23
+
24
+ endpoints << GrapeApe::Endpoint.new(settings.clone, endpoint_options) do
25
+ message = rpc(env, route_options[:routing_key], {method: route_options[:method], params: params})
26
+ if block
27
+ block.call(message)
28
+ else
29
+ message
30
+ end
31
+ end
32
+
33
+ @last_description = nil
34
+ reset_validations!
35
+ else
36
+ super
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def ape_route?(route_options)
43
+ keys = Set.new(route_options.keys)
44
+ Set[*REQUIRED_ROUTE_KEYS].subset?(keys)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,66 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+ require 'eventmachine'
3
+ require 'em-synchrony'
4
+ require 'amqp'
5
+
6
+ module GrapeApe
7
+ module Consumer
8
+ module Application
9
+
10
+ module_function
11
+
12
+ def amqp_channel
13
+ @channel
14
+ end
15
+
16
+ def register(routing_key, klass)
17
+ @keys_and_klasses ||= {}
18
+ @keys_and_klasses[routing_key] = klass
19
+ end
20
+
21
+ def run!
22
+ start do
23
+ connect
24
+ end
25
+ end
26
+
27
+ def connect
28
+ AMQP.connect do |connection|
29
+ @channel = AMQP::Channel.new(connection)
30
+
31
+ @keys_and_klasses.each do |key, value|
32
+ q = @channel.queue(key, :auto_delete => true)
33
+ q.subscribe do |metadata, payload|
34
+ value.new.handle_message(metadata, payload)
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ # Stops the consumer running.
41
+ def stop
42
+ logger.info('Stopping the consumers...')
43
+ EM.stop
44
+ end
45
+
46
+ def start
47
+ EM.epoll
48
+ EM.synchrony do
49
+ trap('INT') { stop }
50
+ trap('TERM') { stop }
51
+ yield if block_given?
52
+ end
53
+ end
54
+
55
+ at_exit do
56
+ # Only run the application if ...
57
+ # - we want it to run
58
+ # - there has been no exception raised
59
+ # - the file that has been run, is the goliath application file
60
+ if $!.nil?
61
+ Application.run!
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,22 @@
1
+ module GrapeApe
2
+ module Dispatcher
3
+ def rpc(env, queue_name, message = {})
4
+ f = Fiber.current
5
+ response = nil
6
+ env['subscription'] = env.grape_amqp_em_channel.subscribe do |msg|
7
+ if msg[:meta].correlation_id == env['correlation_id']
8
+ response = msg[:data]
9
+ f.resume
10
+ end
11
+ end
12
+
13
+ env['correlation_id'] = SecureRandom.uuid
14
+ env.grape_amqp_exchange.publish(message.to_json,
15
+ routing_key: queue_name,
16
+ reply_to: env.grape_amqp_response_queue,
17
+ correlation_id: env['correlation_id'])
18
+ Fiber.yield
19
+ JSON.parse(response)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,5 @@
1
+ module GrapeApe
2
+ class Endpoint < Grape::Endpoint
3
+ include GrapeApe::Dispatcher
4
+ end
5
+ end
@@ -0,0 +1,47 @@
1
+ require 'goliath'
2
+ require 'goliath/websocket'
3
+ require 'grape_ape'
4
+ require 'grape_ape/goliath/server'
5
+ require 'grape_ape/goliath/runner'
6
+
7
+ Goliath::Application.module_eval do
8
+
9
+ Goliath::Application::CALLERS_TO_IGNORE << /\/grape_ape\/goliath_runner.rb$/
10
+ Goliath::Application::CALLERS_TO_IGNORE << /\/ruby-debug-ide-(.+)\//
11
+ Goliath::Application::CALLERS_TO_IGNORE << /\/debase\//
12
+
13
+ module_function
14
+
15
+ def camel_case(str)
16
+ return str if str !~ /_/ && str =~ /[A-Z]+.*/
17
+
18
+ str.split('_').map { |e| e.capitalize }.join
19
+ end
20
+
21
+ alias :super_run! :run!
22
+
23
+ # Execute the application
24
+ #
25
+ # @return [Nil]
26
+ def run!
27
+ if GrapeApe::API.app_class
28
+ begin
29
+ klass = Kernel
30
+ GrapeApe::API.app_class.split('::').each { |con| klass = klass.const_get(con) }
31
+ api = GrapeApe::Server.new(api: klass)
32
+ rescue NameError
33
+ raise NameError, "Class #{@app_class} not found."
34
+ end
35
+
36
+ runner = GrapeApe::Goliath::Runner.new(ARGV, api)
37
+ runner.app = Goliath::Rack::Builder.build(GrapeApe::Server, api)
38
+
39
+ runner.load_plugins(GrapeApe::Server.plugins)
40
+
41
+ runner.run
42
+ else
43
+ super_run!
44
+ end
45
+ end
46
+
47
+ end
@@ -0,0 +1,24 @@
1
+ require 'goliath/runner'
2
+ require 'grape_ape/goliath/server'
3
+
4
+ module GrapeApe
5
+ module Goliath
6
+ class Runner < ::Goliath::Runner
7
+
8
+ # Sets up the Goliath server
9
+ #
10
+ # @param log [Logger] The logger to configure the server to log to
11
+ # @return [Server] an instance of a Goliath server
12
+ def setup_server(log = setup_logger)
13
+ server = GrapeApe::Goliath::Server.new(@address, @port)
14
+ server.logger = log
15
+ server.app = @app
16
+ server.api = @api
17
+ server.plugins = @plugins || []
18
+ server.options = @server_options
19
+ server
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,67 @@
1
+ require 'eventmachine'
2
+
3
+ module GrapeApe
4
+ module Goliath
5
+ class Server < ::Goliath::Server
6
+ def start(&blk)
7
+ EM.epoll
8
+ EM.synchrony do
9
+ trap('INT') { stop }
10
+ trap('TERM') { stop }
11
+
12
+ if RUBY_PLATFORM !~ /mswin|mingw/
13
+ trap('HUP') { load_config(options[:config]) }
14
+ end
15
+
16
+ load_config(options[:config])
17
+ load_plugins
18
+ setup_amqp
19
+
20
+ EM.set_effective_user(options[:user]) if options[:user]
21
+
22
+ config[::Goliath::Constants::GOLIATH_SIGNATURE] = EM.start_server(address, port, ::Goliath::Connection) do |conn|
23
+ if options[:ssl]
24
+ conn.start_tls(
25
+ :private_key_file => options[:ssl_key],
26
+ :cert_chain_file => options[:ssl_cert],
27
+ :verify_peer => options[:ssl_verify]
28
+ )
29
+ end
30
+
31
+ conn.port = port
32
+ conn.app = app
33
+ conn.api = api
34
+ conn.logger = logger
35
+ conn.status = status
36
+ conn.config = config
37
+ conn.options = options
38
+ end
39
+
40
+ blk.call(self) if blk
41
+ end
42
+ end
43
+
44
+ def stop
45
+ logger.info('Stopping server...')
46
+ config['grape_amqp_conn'].close { EM.stop }
47
+ end
48
+
49
+ def setup_amqp
50
+ config['grape_amqp_conn'] = AMQP.connect(on_possible_authentication_failure: Proc.new { |settings|
51
+ logger.info "Authentication failed, as expected, settings are: #{settings.inspect}"
52
+ EM.stop
53
+ })
54
+ config['grape_amqp_channel'] = AMQP::Channel.new(config['grape_amqp_conn'])
55
+ config['grape_amqp_exchange'] = config['grape_amqp_channel'].default_exchange
56
+ config['grape_amqp_response_queue'] = SecureRandom.uuid
57
+ config['grape_amqp_em_channel'] = EM::Channel.new
58
+
59
+ q = config['grape_amqp_channel'].queue(config['grape_amqp_response_queue'], exclusive: true)
60
+
61
+ q.subscribe do |meta, payload|
62
+ config['grape_amqp_em_channel'].push({meta: meta, data: payload})
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,15 @@
1
+ require 'goliath'
2
+ require 'goliath/api'
3
+ require 'goliath/websocket'
4
+
5
+ module GrapeApe
6
+ class Server < ::Goliath::API
7
+ def initialize(opts = {})
8
+ @api = opts.delete(:api)
9
+ end
10
+
11
+ def response(env)
12
+ @api.call(env)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,41 @@
1
+ require 'goliath/test_helper'
2
+
3
+ module GrapeApe
4
+ module TestHelper
5
+ include ::Goliath::TestHelper
6
+
7
+ # Wrapper for launching API and executing given code block. This
8
+ # will start the EventMachine reactor running.
9
+ #
10
+ # @param api [Class] The GrapeApe::API class to launch
11
+ # @param options [Hash] The options to pass to the server
12
+ # @param blk [Proc] The code to execute after the server is launched.
13
+ # @note This will not return until stop is called.
14
+ def with_api(api, options = {}, &blk)
15
+ server(GrapeApe::Server.new(api: api), options.delete(:port) || 9900, options, &blk)
16
+ end
17
+
18
+ # Launches an instance of a given API server. The server
19
+ # will launch on the specified port.
20
+ #
21
+ # @param api [Class] The API class to launch
22
+ # @param port [Integer] The port to run the server on
23
+ # @param options [Hash] The options hash to provide to the server
24
+ # @return [Goliath::Server] The executed server
25
+ def server(api, port, options = {}, &blk)
26
+ op = OptionParser.new
27
+
28
+ s = GrapeApe::Goliath::Server.new
29
+ s.logger = setup_logger(options)
30
+ s.api = api
31
+ s.app = ::Goliath::Rack::Builder.build(api.class, s.api)
32
+ s.api.options_parser(op, options)
33
+ s.options = options
34
+ s.port = port
35
+ s.plugins = api.class.plugins
36
+ @test_server_port = s.port if blk
37
+ s.start(&blk)
38
+ s
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module GrapeApe
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,39 @@
1
+ require 'grape_ape/consumer/application'
2
+
3
+ module GrapeApe
4
+ class Worker
5
+ class << self
6
+
7
+ attr_reader :routing_key
8
+
9
+ def routing_key(key)
10
+ @routing_key = key
11
+ Consumer::Application.register(key, self)
12
+ end
13
+ end
14
+
15
+ def handle_message(metadata, payload)
16
+ payload = JSON.parse(payload).with_indifferent_access
17
+ response = if respond_to?(payload[:method].to_sym)
18
+ begin
19
+ ok(send(payload[:method].to_sym, payload[:params]))
20
+ rescue Exception => ex
21
+ error({message: ex.message, backtrace: ex.backtrace})
22
+ end
23
+ else
24
+ error("Worker listening to routing_key: #{self.routing_key} does not have method: #{payload[:method]}")
25
+ end
26
+ Consumer::Application.amqp_channel.default_exchange.publish(response.to_json,
27
+ routing_key: metadata.reply_to,
28
+ correlation_id: metadata.correlation_id)
29
+ end
30
+
31
+ def ok(response)
32
+ [:ok, response]
33
+ end
34
+
35
+ def error(response)
36
+ [:error, response]
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ describe GrapeApe::API do
4
+
5
+ before(:each) do
6
+ described_class.endpoints.clear
7
+ end
8
+
9
+ subject { Class.new(GrapeApe::API) }
10
+
11
+ def app;
12
+ subject
13
+ end
14
+
15
+ describe '.route' do
16
+ let(:ape_options) { {routing_key: 'blah', method: 'hello'} }
17
+
18
+ it 'should ask if the route is an ape route' do
19
+ described_class.should_receive(:ape_route?).with(hash_including(ape_options)).and_return(true)
20
+ described_class.route(:get, '/', ape_options)
21
+ end
22
+
23
+ it 'should call super route with no ape route options' do
24
+ described_class.superclass.should_receive(:route)
25
+ described_class.route(:get, '/')
26
+ end
27
+
28
+ it 'should add an endpoint to the endpoint collection' do
29
+ expect {
30
+ described_class.route(:get, '/', ape_options)
31
+ }.to change { described_class.endpoints.count }.from(0).to(1)
32
+ end
33
+
34
+ context 'api responses' do
35
+ it 'returns the message that rpc returns with out a block provided to the dsl method' do
36
+ subject.get '/', ape_options
37
+ GrapeApe::Endpoint.any_instance.should_receive(:rpc).and_return('hello_world')
38
+ get '/'
39
+ last_response.body.should eql 'hello_world'
40
+ end
41
+
42
+ it 'should pass the rpc message to the provided block for processing' do
43
+ subject.get '/', ape_options do |message|
44
+ 'hello ' + message
45
+ end
46
+ GrapeApe::Endpoint.any_instance.should_receive(:rpc).and_return('world')
47
+ get '/'
48
+ last_response.body.should eql 'hello world'
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe GrapeApe::Dispatcher do
4
+
5
+ end
@@ -0,0 +1,21 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'support'))
4
+
5
+ require 'coveralls'
6
+ Coveralls.wear!
7
+
8
+ require 'grape_ape'
9
+ require 'rubygems'
10
+ require 'bundler'
11
+ require 'rack/test'
12
+ Bundler.setup :default, :test
13
+
14
+ Dir["#{File.dirname(__FILE__)}/support/*.rb"].each do |file|
15
+ require file
16
+ end
17
+
18
+ RSpec.configure do |config|
19
+ config.order = 'random'
20
+ config.include Rack::Test::Methods
21
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+ require 'grape_ape/test_helper'
3
+
4
+ describe GrapeApe::TestHelper do
5
+ include GrapeApe::TestHelper
6
+
7
+ class TestApi < GrapeApe::API
8
+ format :json
9
+
10
+ get '/' do
11
+ {hello: 'world'}
12
+ end
13
+ end
14
+
15
+ context 'make request to root' do
16
+ it 'should get a 200' do
17
+ with_api(TestApi) do
18
+ em_http_client = get_request path: '/'
19
+ em_http_client.response_header.status.should eq(200)
20
+ end
21
+ end
22
+
23
+ it 'should get json' do
24
+ with_api(TestApi) do
25
+ em_http_client = get_request path: '/'
26
+ JSON.parse(em_http_client.response).should eq({'hello' => 'world'})
27
+ end
28
+ end
29
+ end
30
+
31
+ context 'make request to not routed path' do
32
+ it 'get a 404' do
33
+ with_api(TestApi) do
34
+ em_http_client = get_request path: '/something'
35
+ em_http_client.response_header.status.should eq(404)
36
+ end
37
+ end
38
+ end
39
+ end
metadata ADDED
@@ -0,0 +1,219 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: grapeape
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - David Justice
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
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
+ - !ruby/object:Gem::Dependency
56
+ name: rack-test
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: em-http-request
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: coveralls
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: goliath
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: grape
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: amqp
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: activesupport
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description: Message based, event driven web dsl in ruby
154
+ email:
155
+ - david@devigned.com
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - .gitignore
161
+ - .travis.yml
162
+ - Gemfile
163
+ - LICENSE
164
+ - README.md
165
+ - Rakefile
166
+ - examples/hello_world/Gemfile
167
+ - examples/hello_world/Procfile
168
+ - examples/hello_world/api.rb
169
+ - examples/hello_world/worker.rb
170
+ - grape_ape.gemspec
171
+ - lib/grape_ape.rb
172
+ - lib/grape_ape/api.rb
173
+ - lib/grape_ape/consumer/application.rb
174
+ - lib/grape_ape/dispatcher.rb
175
+ - lib/grape_ape/endpoint.rb
176
+ - lib/grape_ape/goliath/application_patch.rb
177
+ - lib/grape_ape/goliath/runner.rb
178
+ - lib/grape_ape/goliath/server.rb
179
+ - lib/grape_ape/server.rb
180
+ - lib/grape_ape/test_helper.rb
181
+ - lib/grape_ape/version.rb
182
+ - lib/grape_ape/worker.rb
183
+ - spec/api_spec.rb
184
+ - spec/dispatcher_spec.rb
185
+ - spec/spec_helper.rb
186
+ - spec/test_helper_spec.rb
187
+ homepage: ''
188
+ licenses:
189
+ - MIT
190
+ metadata: {}
191
+ post_install_message:
192
+ rdoc_options: []
193
+ require_paths:
194
+ - lib
195
+ required_ruby_version: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - '>='
198
+ - !ruby/object:Gem::Version
199
+ version: 2.0.0
200
+ required_rubygems_version: !ruby/object:Gem::Requirement
201
+ requirements:
202
+ - - '>='
203
+ - !ruby/object:Gem::Version
204
+ version: '0'
205
+ requirements: []
206
+ rubyforge_project:
207
+ rubygems_version: 2.0.3
208
+ signing_key:
209
+ specification_version: 4
210
+ summary: Message based, event driven web dsl in ruby. The project stands up an event
211
+ driven web (goliath / grape) dsl backed by AMQP. AMQP is used to route messages
212
+ from the web to a collection of event driven worker processes in an RPC flow. This
213
+ could be used to set up a quick [CQRS](http://martinfowler.com/bliki/CQRS.html)
214
+ architecture.
215
+ test_files:
216
+ - spec/api_spec.rb
217
+ - spec/dispatcher_spec.rb
218
+ - spec/spec_helper.rb
219
+ - spec/test_helper_spec.rb