tochtli 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +14 -0
  3. data/Gemfile +32 -0
  4. data/History.md +138 -0
  5. data/README.md +46 -0
  6. data/Rakefile +50 -0
  7. data/VERSION +1 -0
  8. data/assets/communication.png +0 -0
  9. data/assets/layers.png +0 -0
  10. data/examples/01-screencap-service/Gemfile +3 -0
  11. data/examples/01-screencap-service/README.md +5 -0
  12. data/examples/01-screencap-service/client.rb +15 -0
  13. data/examples/01-screencap-service/common.rb +15 -0
  14. data/examples/01-screencap-service/server.rb +26 -0
  15. data/examples/02-log-analyzer/Gemfile +3 -0
  16. data/examples/02-log-analyzer/README.md +5 -0
  17. data/examples/02-log-analyzer/client.rb +95 -0
  18. data/examples/02-log-analyzer/common.rb +33 -0
  19. data/examples/02-log-analyzer/sample.log +10001 -0
  20. data/examples/02-log-analyzer/server.rb +133 -0
  21. data/lib/tochtli.rb +177 -0
  22. data/lib/tochtli/active_record_connection_cleaner.rb +9 -0
  23. data/lib/tochtli/application.rb +135 -0
  24. data/lib/tochtli/base_client.rb +135 -0
  25. data/lib/tochtli/base_controller.rb +360 -0
  26. data/lib/tochtli/controller_manager.rb +99 -0
  27. data/lib/tochtli/engine.rb +15 -0
  28. data/lib/tochtli/message.rb +114 -0
  29. data/lib/tochtli/rabbit_client.rb +36 -0
  30. data/lib/tochtli/rabbit_connection.rb +249 -0
  31. data/lib/tochtli/reply_queue.rb +129 -0
  32. data/lib/tochtli/simple_validation.rb +23 -0
  33. data/lib/tochtli/test.rb +9 -0
  34. data/lib/tochtli/test/client.rb +28 -0
  35. data/lib/tochtli/test/controller.rb +66 -0
  36. data/lib/tochtli/test/integration.rb +78 -0
  37. data/lib/tochtli/test/memory_cache.rb +22 -0
  38. data/lib/tochtli/test/test_case.rb +191 -0
  39. data/lib/tochtli/test/test_unit.rb +22 -0
  40. data/lib/tochtli/version.rb +3 -0
  41. data/log_generator.rb +11 -0
  42. data/test/base_client_test.rb +68 -0
  43. data/test/controller_functional_test.rb +87 -0
  44. data/test/controller_integration_test.rb +274 -0
  45. data/test/controller_manager_test.rb +75 -0
  46. data/test/dummy/Rakefile +7 -0
  47. data/test/dummy/config/application.rb +36 -0
  48. data/test/dummy/config/boot.rb +4 -0
  49. data/test/dummy/config/database.yml +3 -0
  50. data/test/dummy/config/environment.rb +5 -0
  51. data/test/dummy/config/rabbit.yml +4 -0
  52. data/test/dummy/db/.gitkeep +0 -0
  53. data/test/dummy/log/.gitkeep +0 -0
  54. data/test/key_matcher_test.rb +100 -0
  55. data/test/log/.gitkeep +0 -0
  56. data/test/message_test.rb +80 -0
  57. data/test/rabbit_client_test.rb +71 -0
  58. data/test/rabbit_connection_test.rb +151 -0
  59. data/test/test_helper.rb +32 -0
  60. data/test/version_test.rb +8 -0
  61. data/tochtli.gemspec +129 -0
  62. metadata +259 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 56acd327a8d04ef8de6ca2ab8a2b677e27e276e4
4
+ data.tar.gz: c0f076e6906b0ef7a401585be0946ec1b8b0d1f3
5
+ SHA512:
6
+ metadata.gz: 4653fc71fcd32d2a9a21bb9929d9994564b2b3b5e1a680e04faf414705ad7bf9b9bcc8e12e274d653183f2fec2780adb3d872a93c004123c611820479d95cd97
7
+ data.tar.gz: 4e799d6d4888f1182772ca63dfdcd534fa2255f50926a98bb2a3df8ba1fd0b5b8b897361227e785dfdb5287269400ecac5cae7be9925f6cd2babf2350889fdb9
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.5
4
+ - 2.2.1
5
+ - rbx-2
6
+ env:
7
+ - RAILS_VER=3.2.19
8
+ - RAILS_VER=4.2.1
9
+ - NO_RAILS=1
10
+ services:
11
+ - rabbitmq
12
+ matrix:
13
+ allow_failures:
14
+ - rvm: rbx-2
data/Gemfile ADDED
@@ -0,0 +1,32 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'bunny', '~> 1.7.0'
4
+ gem 'uber'
5
+ gem 'virtus'
6
+ gem 'facets', require: false
7
+ gem 'hooks'
8
+
9
+ group :development do
10
+ gem 'dalli', '~> 2.6.4'
11
+ gem 'jeweler', '~> 2.0.1'
12
+
13
+ gem 'mini_cache'
14
+ gem 'yard', '~> 0.8'
15
+
16
+ if ENV['RAILS_VER']
17
+ gem 'sqlite3'
18
+ rails_ver = ENV['RAILS_VER'] # Setup rails version for tests
19
+ gem 'rails', rails_ver
20
+ if rails_ver.to_i < 4
21
+ gem 'test-unit', '>= 3.0.9'
22
+ end
23
+ gem 'tzinfo-data' if RUBY_PLATFORM =~ /mswin|mingw/ && rails_ver >= '4.0' # require tzinfo-data only for Rails 4 on windows
24
+ gem 'minitest', rails_ver < '4.0' ? '~> 4.7.5' : '~> 5.4.2'
25
+ gem 'minitest-rails', rails_ver < '4.0' ? '~> 1.0.1' : '~> 2.1.1'
26
+ else
27
+ #gem 'rails', '>= 3.2.15'
28
+ gem 'minitest', '>= 4.7.5'
29
+ gem 'minitest-reporters', '>= 0.5.0'
30
+ end
31
+ end
32
+
@@ -0,0 +1,138 @@
1
+ ## 0.5.0 / 2015-08-19
2
+
3
+ * Usage examples
4
+ * Key pattern support for a controller routing (ex. on Message, :action, routing_key: 'my.key.*')
5
+ * Tochtli::BaseController#rabbit_connection delegation
6
+ * Tochtli::BaseController callbacks: (before|after)_(setup|start|stop|restart) with `hooks` gem
7
+ * Tochtli::BaseController#restart supports multiple queues
8
+ * Tochtli::Message#routing_key as a block
9
+ * Default Tochtli.logger logs on STDERR with WARN level
10
+ * Tochtli.cache is not obligatory
11
+ * Get rid of Tochtli::Test cases - replace with concerns (ex. include Tochtli::Test::Client not inherit from)
12
+
13
+ ## 0.4.0 / 2015-07-10
14
+
15
+ * Remove ActiveSupport dependencies (use Facets if Rails not loaded)
16
+ * Replace ActiveSupport::Cache with MiniCache and fix validation tests
17
+ * Use RabbitConnection#on_return method with all exchanges
18
+ * YARD introduced
19
+ * Tochtli::SimpleValidation for validation error messages
20
+ * RabbitClient#rabbit_config removed
21
+ * Removed RabbitClient#publish_and_wait (use SyncMessageHandler
22
+ * Fix #2: Client error on dropped message
23
+ * No more automatic routing. All routes have to be defined in the controller. (got rid of the MessageMap)
24
+ * Message attributes rewritten with Virtus.
25
+ * Message#bind_topic renamed to route_to
26
+ * BaseController#subscribe renamed to bind
27
+ * BaseClient#instance (singleton support) removed
28
+ * Tochtli::Test::IntegrationHelpers module with methods extracted from Tochtli::Test::Integration
29
+ * setup and teardown -> before_setup and after_teardown (no need for def setup; super; ... in tests)
30
+
31
+ ## 0.3.0 / 2015-05-21
32
+
33
+ ### 4 major enhancements:
34
+
35
+ * Process each message in a separate controller instance (`BaseController::Dispatcher` introduced)
36
+ * Use separate channels and work pools per controller
37
+ * ControllerManager#start selectively starts controllers
38
+ * BaseController#start accepts queue name. Multiple queues are supported per single controller.
39
+
40
+ ### 8 minor enhancements:
41
+
42
+ * `queue` & `queue_exists?` methods for TestRabbitConnection
43
+ * Single reply queue per rabbit connection
44
+ * configuration_store removed from `RabbitClient` (should be used separately)
45
+ * `RabbitConnection.logger` default set to `Tochtli.logger`
46
+ * ActiveRecordConnectionCleaner - a middleware for active connection cleanup
47
+ * Tochtli.application with middlewares introduced
48
+ * Network failure recovery rewritten (using automatic bunny recovery with additional reply queue binding recovery)
49
+ * Bunny logger redirected to RabbitConnection logger with level dependent on Tochtli.debug_bunny (by default WARN)
50
+
51
+ ### 1 bug fix:
52
+
53
+ * `RabbitConnection` logger setup with configuration
54
+
55
+ ## 0.2.0 / 2015-05-08
56
+
57
+ ### 1 major enhancement:
58
+
59
+ * `RabbitConnection` connection cache. `RabbitConnection.open(configuration_name)` as a standard connection access method.
60
+
61
+ ### 11 minor enhancements:
62
+
63
+ * BaseController callbacks around :start and :setup
64
+ * Public BaseController#setup method (for manual routing control in tests)
65
+ * Tochtli::BaseClient tests, expect_published helper method
66
+ * Tochtli::Message required_attributes and optional_attributes declarations
67
+ * Tochtli::ServiceCache caches the store object with method: Tochtli::ServiceCache.store
68
+ * Tochtli::BaseClient singleton methods accept any number of arguments (passed to constructor)
69
+ * Get rid of ClientProxy. Error handling unified in BaseClient.
70
+ * Default logger for BaseClient (Tochtli.logger)
71
+ * Tochtli::Message validation callbacks
72
+ * Tochtli::Message#ignore_excess_attributes for open messages
73
+ * assert_published accepts block and yields message
74
+
75
+ ## 0.1.7 / 2015-04-01
76
+
77
+ ### 1 minor enhancement:
78
+
79
+ * Tochtli::BaseClient introduced (with MessageHandler and SyncMessageHandler)
80
+
81
+ ## 0.1.6 / 2015-03-02
82
+
83
+ ### 1 bug fix:
84
+
85
+ * Protect RabbitConnection from crash when Rails.root is not set
86
+
87
+ ## 0.1.5 / 2015-02-06
88
+
89
+ ### 2 minor enhancement:
90
+
91
+ * Test fix: timeout raised
92
+ * Rails 4.2 compatibility fix
93
+
94
+ ## 0.1.4 / 2014-11-17
95
+
96
+ ### 1 minor enhancement:
97
+
98
+ * Switched to minitest
99
+
100
+ ### 1 bug fix:
101
+
102
+ * Blocking client proxy should not block on immediate reply (refs #13829)
103
+
104
+ ## 0.1.3 / 2014-10-28
105
+
106
+ ### 1 minor enhancement:
107
+
108
+ * Remove Rails dependencies
109
+
110
+ ## 0.1.2 / 2014-10-10
111
+
112
+ ### 4 minor enhancements:
113
+
114
+ * Update corrupted gemspec
115
+ * Move add_engine_migrations method to hoe-puzzleflow
116
+ * User always PuzzleFlow gem server
117
+ * User always PuzzleFlow gem server
118
+ * rakefile cleaning
119
+
120
+ ## 0.1.1 / 2014-09-30
121
+
122
+ ### 1 major enhancement:
123
+
124
+ * hoe introduced with git and geminabox integration
125
+
126
+ ### 1 minor enhancements:
127
+
128
+ * do not show ruby warnings (reset RUBY_FLAGS)
129
+ * hoe-puzzleflow used to unify hoe spec with other PuzzleFlow gems
130
+
131
+ ## 0.1.0 / 2014-09-29
132
+
133
+ ### 1 major enhancement
134
+
135
+ * Birthday!
136
+
137
+
138
+
@@ -0,0 +1,46 @@
1
+ [![Build Status](https://travis-ci.org/PuzzleFlow/tochtli.svg?branch=master)](https://travis-ci.org/PuzzleFlow/tochtli)
2
+
3
+ # What's Tochtli?
4
+
5
+ Tochtli is a set of conventions and tools that simplifies the implementation of components communicating over the RabbitMQ broker.
6
+ Internally it depends on [bunny](https://github.com/ruby-amqp/bunny) gem.
7
+
8
+ ## Idea
9
+
10
+ ![Tochtli Diagram](assets/communication.png)
11
+
12
+ The communication between application and service looks like on the above picture.
13
+ The _application_ calls the _client_ (regular object) methods which expose the _service API_.
14
+ The _request_ (a message) is created and sent by the _client_ to the _service queue_ (dedicated queue per service controller).
15
+ _RabbitMQ_ is used as a message broker and delivers the _request_ to the _service controller_.
16
+ The _controller_ implements the actions that are performed on the _request_ and may result in the _response_.
17
+ The _response_ message is published on the _reply queue_ (private for a client connection).
18
+ The _client_ receives the _response_ and returns the expected result to the _application_.
19
+ The client methods can be implemented in the blocking and non-blocking (asynchronous) way.
20
+
21
+ ## Layers
22
+
23
+ ![Tochtli Layers](assets/layers.png)
24
+
25
+ The stable service interface really helps to reduce costs of a inevitable change that should only affect the internal implementation.
26
+ In the proposed above layered structure the application interacts only with the public client class methods.
27
+ It's not very hard to implement client methods in a such way that they will allow for future changes which won't cause compatibility issues.
28
+ The expected resistance on the service API changes is a reason way application has no access to the messages definition.
29
+ Only clients and service controllers operate on the message layer (the common layer).
30
+ The bottom layer contains tools that allow for communication between the client and the server.
31
+ There is an option for the message broker agnosticism in the future, because client and service implementation should not directly depend on the particular tool (ex. bunny).
32
+
33
+ # What's next?
34
+
35
+ Read more about Tochtli and go through the tutorial on [Tochtli homepage](http://puzzleflow.github.io/tochtli).
36
+
37
+ # Contributing
38
+
39
+ - Fork the project.
40
+ - Make your feature addition or bug fix.
41
+ - Add tests for it. This is important so I don't break it in a future version unintentionally.
42
+ - Send me a pull request.
43
+
44
+ # License
45
+
46
+ Released under the MIT license.
@@ -0,0 +1,50 @@
1
+ # encoding: utf-8
2
+ #!/usr/bin/env rake
3
+
4
+ require 'rubygems'
5
+ require 'bundler'
6
+ begin
7
+ Bundler.setup(:default, :development)
8
+ rescue Bundler::BundlerError => e
9
+ $stderr.puts e.message
10
+ $stderr.puts "Run `bundle install` to install missing gems"
11
+ exit e.status_code
12
+ end
13
+ require 'rake'
14
+
15
+ require 'rake/testtask'
16
+ require 'jeweler'
17
+ require 'yard'
18
+
19
+ Jeweler::Tasks.new do |gem|
20
+ # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
21
+ gem.name = "tochtli"
22
+ gem.homepage = "http://github.com/puzzleflow/tochtli"
23
+ gem.license = "MIT"
24
+ gem.summary = %Q{Tochtli a core components for SOA}
25
+ gem.description = %Q{Lightweight framework for service oriented applications based on bunny (RabbitMQ)}
26
+ gem.email = "rafal.bigaj@puzzleflow.com"
27
+ gem.authors = ["Rafal Bigaj"]
28
+ # dependencies defined in Gemfile
29
+ end
30
+ Jeweler::RubygemsDotOrgTasks.new
31
+
32
+ Rake::TestTask.new(:test) do |test|
33
+ test.libs << 'lib' << 'test'
34
+ test.pattern = 'test/**/*_test.rb'
35
+ test.verbose = true
36
+ end
37
+
38
+ desc "Code coverage detail"
39
+ task :simplecov do
40
+ ENV['COVERAGE'] = "true"
41
+ Rake::Task['test'].execute
42
+ end
43
+
44
+ task :default => :test
45
+
46
+ YARD::Rake::YardocTask.new do |t|
47
+ t.files = ['lib/**/*.rb', 'README*'] # optional
48
+ t.options = ['--any', '--extra', '--opts'] # optional
49
+ t.stats_options = ['--list-undoc'] # optional
50
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.5.0
Binary file
Binary file
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+ gem 'screencap'
3
+ gem 'tochtli', github: 'puzzleflow/tochtli'
@@ -0,0 +1,5 @@
1
+ # Getting started
2
+
3
+ The first example is a server that creates website snapshots using [screencap](https://github.com/maxwell/screencap) gem.
4
+
5
+ See [Getting started tutorial](http://puzzleflow.github.io/tochtli/tutorials/getting-started.html) for details.
@@ -0,0 +1,15 @@
1
+ require_relative 'common'
2
+
3
+ Tochtli.logger.progname = 'CLIENT'
4
+
5
+ class ScreenerClient < Tochtli::BaseClient
6
+ def create_screen(url, file_name)
7
+ handler = SyncMessageHandler.new
8
+ message = CreateScreenMessage.new(url: url, file: file_name)
9
+ rabbit_client.publish message, handler: handler
10
+ handler.wait!(20)
11
+ puts "Done in #{handler.reply.time} seconds"
12
+ end
13
+ end
14
+
15
+ ScreenerClient.new.create_screen(ARGV[0], ARGV[1])
@@ -0,0 +1,15 @@
1
+ require 'bundler'
2
+ Bundler.require
3
+
4
+ Tochtli.logger = Logger.new('tochtli.log')
5
+
6
+ class CreateScreenMessage < Tochtli::Message
7
+ route_to 'screener.create'
8
+
9
+ attribute :url, String
10
+ attribute :file, String
11
+ end
12
+
13
+ class CreateScreenReplyMessage < Tochtli::Message
14
+ attribute :time, Float
15
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'common'
2
+
3
+ Tochtli.logger.progname = 'SERVER'
4
+
5
+ class ScreenerController < Tochtli::BaseController
6
+ bind 'screener.*'
7
+
8
+ on CreateScreenMessage, :create
9
+
10
+ def create
11
+ start_time = Time.now
12
+ f = Screencap::Fetcher.new(message.url)
13
+ f.fetch output: File.join(__dir__, 'images', message.file)
14
+ total_time = Time.now - start_time
15
+ reply CreateScreenReplyMessage.new(time: total_time)
16
+ end
17
+ end
18
+
19
+ Tochtli::ControllerManager.setup
20
+ Tochtli::ControllerManager.start
21
+
22
+ trap('SIGINT') { exit }
23
+ at_exit { Tochtli::ControllerManager.stop }
24
+
25
+ puts 'Press Ctrl-C to stop worker...'
26
+ sleep
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+ gem 'screencap'
3
+ gem 'tochtli', path: '../..' # github: 'puzzleflow/tochtli'
@@ -0,0 +1,5 @@
1
+ # Log Analyzer
2
+
3
+ The second example is a log analyzer with status monitor and event handler.
4
+
5
+ See [Client notifications tutorial](http://puzzleflow.github.io/tochtli/tutorials/client-notifications.html) for details.
@@ -0,0 +1,95 @@
1
+ require_relative 'common'
2
+
3
+ Tochtli.logger.progname = 'CLIENT'
4
+
5
+ module LogAnalyzer
6
+ class Client < Tochtli::BaseClient
7
+ def send_new_log(path)
8
+ publish NewLog.new(path: path)
9
+ end
10
+
11
+ def react_on_events(client_id, severities, handler=nil, &block)
12
+ handler = block unless handler
13
+ severities = Array(severities)
14
+ routing_keys = severities.map {|severity| "log.events.#{severity}" }
15
+ Tochtli::ControllerManager.start EventsController,
16
+ queue_name: "log_analyzer/events/#{client_id}", # custom queue name
17
+ routing_keys: routing_keys, # make custom binding (only selected severities)
18
+ env: { handler: handler }
19
+ end
20
+
21
+ def monitor_status(monitor=nil, &block)
22
+ monitor = block unless monitor
23
+ Tochtli::ControllerManager.start MonitorController, env: { monitor: monitor }
24
+ end
25
+ end
26
+
27
+ protected
28
+
29
+ class EventsController < Tochtli::BaseController
30
+ on EventOccurred, :handle, routing_key: 'log.events.*'
31
+
32
+ def handle
33
+ handler.call(message.severity, message.timestamp, message.message)
34
+ end
35
+
36
+ protected
37
+
38
+ def handler
39
+ raise "Internal Error: handler not set for EventsController" unless env.has_key?(:handler)
40
+ env[:handler]
41
+ end
42
+ end
43
+
44
+ class MonitorController < Tochtli::BaseController
45
+ bind 'log.status'
46
+
47
+ self.queue_name = '' # auto generate
48
+ self.queue_durable = false
49
+ self.queue_exclusive = true
50
+ self.queue_auto_delete = true
51
+
52
+ on CurrentStatus do
53
+ monitor.call(message.to_hash)
54
+ end
55
+
56
+ protected
57
+
58
+ def monitor
59
+ raise "Internal Error: monitor not set for MonitorController" unless env.has_key?(:monitor)
60
+ env[:monitor]
61
+ end
62
+ end
63
+ end
64
+
65
+ client = LogAnalyzer::Client.new
66
+ command = ARGV[0]
67
+
68
+ def hold
69
+ puts 'Press Ctrl-C to stop...'
70
+ sleep
71
+ end
72
+
73
+
74
+ case command
75
+ when 's'
76
+ client.send_new_log ARGV[1]
77
+ when 'm'
78
+ client.monitor_status {|status| p status }
79
+ hold
80
+ when 'c'
81
+ client.react_on_events ARGV[1], [:fatal, :error], lambda {|severity, timestamp, message|
82
+ puts "[#{timestamp}] Got #{severity}: #{message}"
83
+ }
84
+ hold
85
+ else
86
+ puts "Unknown command: #{command.inspect}"
87
+ puts
88
+ puts "Usage: bundle exec ruby client [command] [params]"
89
+ puts
90
+ puts "Commands:"
91
+ puts " s [path] - send log from file to server"
92
+ puts " m - start status monitor"
93
+ puts " c [client ID] - catch fatal and error events"
94
+ end
95
+