tochtli 0.5.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.
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
+