acfs 0.16.0 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.travis.yml +5 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +13 -5
- data/Guardfile +6 -16
- data/README.md +43 -3
- data/Rakefile +11 -1
- data/acfs.gemspec +7 -6
- data/gemfiles/Gemfile.rails-3-0 +8 -0
- data/gemfiles/Gemfile.rails-3-1 +8 -0
- data/lib/acfs.rb +7 -0
- data/lib/acfs/configuration.rb +52 -1
- data/lib/acfs/global.rb +14 -0
- data/lib/acfs/messaging/client.rb +39 -0
- data/lib/acfs/messaging/message.rb +7 -0
- data/lib/acfs/messaging/receiver.rb +119 -0
- data/lib/acfs/model.rb +3 -0
- data/lib/acfs/model/attributes.rb +97 -12
- data/lib/acfs/model/attributes/boolean.rb +11 -1
- data/lib/acfs/model/attributes/integer.rb +11 -1
- data/lib/acfs/model/attributes/string.rb +11 -1
- data/lib/acfs/model/dirty.rb +14 -4
- data/lib/acfs/model/initialization.rb +7 -2
- data/lib/acfs/model/loadable.rb +16 -0
- data/lib/acfs/model/locatable.rb +25 -4
- data/lib/acfs/model/operational.rb +4 -0
- data/lib/acfs/model/persistence.rb +73 -14
- data/lib/acfs/model/query_methods.rb +44 -7
- data/lib/acfs/model/service.rb +23 -11
- data/lib/acfs/operation.rb +2 -0
- data/lib/acfs/runner.rb +3 -0
- data/lib/acfs/service.rb +38 -5
- data/lib/acfs/service/middleware.rb +23 -5
- data/lib/acfs/version.rb +1 -1
- data/lib/acfs/yard.rb +5 -0
- data/rubydoc.png +0 -0
- data/spec/acfs/configuration_spec.rb +0 -1
- data/spec/acfs/messaging/receiver_spec.rb +55 -0
- data/spec/acfs/model/attributes_spec.rb +4 -4
- data/spec/acfs/stub_spec.rb +1 -1
- data/spec/acfs_messaging_spec.rb +5 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/support/service.rb +12 -1
- metadata +35 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 879fbc544d14669fd0db86a7705c1ff10e997a56
|
4
|
+
data.tar.gz: 6424e9a7fcc978e739e80a0ee009fa141ce2ad25
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 697f3c7d31e1aa7081eb0e471e1065dcb2a9ec0be7e57b0f9f130cea0c40bb96dba91bbbb9faaf30eaec0aac29de99ee5b27df8b278696d7d43a9c8d1d4e2826
|
7
|
+
data.tar.gz: da505f1e0fcf03d21f594d81f32527dbe0b991dea6b310a400c0d5182cd573a9f3bfabeb3fddd5438a69dee344ef0dcbd540bc8994ee61cadf92e04a3238f436
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
language: ruby
|
2
|
+
bundler_args: --without development
|
2
3
|
rvm:
|
3
4
|
- 2.0.0
|
4
5
|
- 1.9.3
|
@@ -6,5 +7,9 @@ rvm:
|
|
6
7
|
- rbx-19mode
|
7
8
|
|
8
9
|
gemfile:
|
10
|
+
- gemfiles/Gemfile.rails-3-1
|
9
11
|
- gemfiles/Gemfile.rails-3-2
|
10
12
|
- gemfiles/Gemfile.rails-4-0
|
13
|
+
|
14
|
+
services:
|
15
|
+
- rabbitmq
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
@@ -1,19 +1,27 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
# Specify your gem's dependencies in acfs.gemspec
|
4
|
-
gemroot = File.dirname File.absolute_path __FILE__
|
5
|
-
gemspec path: gemroot
|
6
|
-
|
7
3
|
# Development gems
|
8
4
|
#
|
9
5
|
gem 'webmock', '~> 1.7'
|
10
6
|
gem 'rake'
|
11
7
|
gem 'rspec'
|
12
|
-
gem 'guard-rspec'
|
13
8
|
gem 'coveralls'
|
9
|
+
gem 'json', '~> 1.7.7'
|
14
10
|
|
11
|
+
# Doc
|
12
|
+
group :development do
|
13
|
+
gem 'yard', '~> 0.8.6'
|
14
|
+
gem 'listen'
|
15
|
+
gem 'guard-yard'
|
16
|
+
gem 'guard-rspec'
|
17
|
+
gem 'redcarpet', platform: :ruby
|
18
|
+
end
|
15
19
|
|
16
20
|
# Platform specific development dependencies
|
17
21
|
#
|
18
22
|
gem 'msgpack', platform: :ruby
|
19
23
|
gem 'msgpack-jruby', require: 'msgpack', platform: :jruby
|
24
|
+
|
25
|
+
# Specify your gem's dependencies in acfs.gemspec
|
26
|
+
gemroot = File.dirname File.absolute_path __FILE__
|
27
|
+
gemspec path: gemroot
|
data/Guardfile
CHANGED
@@ -3,22 +3,12 @@
|
|
3
3
|
|
4
4
|
guard 'rspec' do
|
5
5
|
watch(%r{^spec/.+_spec\.rb$})
|
6
|
-
watch(%r{^lib/(.+)\.rb$})
|
7
|
-
watch('spec/spec_helper.rb') { "spec" }
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
8
7
|
|
9
|
-
|
10
|
-
watch(%r{^
|
11
|
-
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
12
|
-
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
13
|
-
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
14
|
-
watch('config/routes.rb') { "spec/routing" }
|
15
|
-
watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
16
|
-
|
17
|
-
# Capybara features specs
|
18
|
-
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
|
19
|
-
|
20
|
-
# Turnip features and steps
|
21
|
-
watch(%r{^spec/acceptance/(.+)\.feature$})
|
22
|
-
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
|
8
|
+
watch('spec/spec_helper.rb') { 'spec' }
|
9
|
+
watch(%r{^spec/support/(.+)\.rb$}) { 'spec' }
|
23
10
|
end
|
24
11
|
|
12
|
+
guard 'yard' do
|
13
|
+
watch(%r{lib/.+\.rb})
|
14
|
+
end
|
data/README.md
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
# Acfs - *API client for services*
|
2
2
|
|
3
|
-
[![Gem Version](https://badge.fury.io/rb/acfs.png)](http://badge.fury.io/rb/acfs)
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/acfs.png)](http://badge.fury.io/rb/acfs)
|
4
|
+
[![Build Status](https://travis-ci.org/jgraichen/acfs.png?branch=master)](https://travis-ci.org/jgraichen/acfs)
|
5
|
+
[![Coverage Status](https://coveralls.io/repos/jgraichen/acfs/badge.png?branch=master)](https://coveralls.io/r/jgraichen/acfs)
|
6
|
+
[![Code Climate](https://codeclimate.com/github/jgraichen/acfs.png)](https://codeclimate.com/github/jgraichen/acfs)
|
7
|
+
[![Dependency Status](https://gemnasium.com/jgraichen/acfs.png)](https://gemnasium.com/jgraichen/acfs)
|
8
|
+
[![RubyDoc Documentation](https://raw.github.com/jgraichen/acfs/master/rubydoc.png)](http://rubydoc.info/github/jgraichen/acfs/master/frames)
|
4
9
|
|
5
10
|
Acfs is a library to develop API client libraries for single services within a larger service oriented application.
|
6
11
|
|
@@ -16,11 +21,11 @@ Add this line to your application's Gemfile:
|
|
16
21
|
|
17
22
|
And then execute:
|
18
23
|
|
19
|
-
|
24
|
+
> bundle
|
20
25
|
|
21
26
|
Or install it yourself as:
|
22
27
|
|
23
|
-
|
28
|
+
> gem install acfs
|
24
29
|
|
25
30
|
## Usage
|
26
31
|
|
@@ -185,6 +190,38 @@ it 'should find user number one' do
|
|
185
190
|
end
|
186
191
|
```
|
187
192
|
|
193
|
+
## Messaging (Experimental)
|
194
|
+
|
195
|
+
Acfs 0.17.0 includes experimental messaging support using RabbitMQ. You can create receivers for messages as well as send custom messages. Messages can be high level structures (like hashes) and will be packed into MessagePack.
|
196
|
+
|
197
|
+
Create your custom receivers e.g. in `app/receivers`:
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
# app/receivers/my_receiver.rb
|
201
|
+
|
202
|
+
class MyReceiver
|
203
|
+
include Acfs::Messaging::Receiver
|
204
|
+
route "my.#" # Specify routing key for receiving messages
|
205
|
+
|
206
|
+
def receive(delivery_info, metadata, payload)
|
207
|
+
puts payload.inspect
|
208
|
+
# more foo...
|
209
|
+
end
|
210
|
+
end
|
211
|
+
```
|
212
|
+
|
213
|
+
Make sure the receivers get loaded by placing an initializer in you app that require all receivers. You need to do this manually but it will be automated in some future release.
|
214
|
+
|
215
|
+
You can now send messages by calling the `publish` method directly:
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
Acfs::Messaging::Client.instance.publish "my.message", { message: "Hi!" }
|
219
|
+
```
|
220
|
+
|
221
|
+
This will invoke `#receive` on an instance of your receiver class.
|
222
|
+
|
223
|
+
Be aware that messaging is still experimental and *will* change in future releases.
|
224
|
+
|
188
225
|
## Roadmap
|
189
226
|
|
190
227
|
* Update
|
@@ -201,6 +238,9 @@ end
|
|
201
238
|
Modified Headers, conflict detection, ...
|
202
239
|
* Pagination? Filtering? (If service API provides such features.)
|
203
240
|
* Messaging Queue support for services and models
|
241
|
+
* Allow triggering messages on resource events (CRUD)
|
242
|
+
* Abstract messages into objects
|
243
|
+
* Middleware stack for messages?
|
204
244
|
* Documentation
|
205
245
|
|
206
246
|
## Contributing
|
data/Rakefile
CHANGED
@@ -2,5 +2,15 @@ require 'bundler/gem_tasks'
|
|
2
2
|
require 'rspec/core/rake_task'
|
3
3
|
|
4
4
|
RSpec::Core::RakeTask.new(:spec)
|
5
|
-
|
6
5
|
task :default => :spec
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'yard'
|
9
|
+
require 'yard/rake/yardoc_task'
|
10
|
+
|
11
|
+
YARD::Rake::YardocTask.new do |t|
|
12
|
+
t.files = %w(lib/**/*.rb)
|
13
|
+
t.options = %w(--output-dir doc/)
|
14
|
+
end
|
15
|
+
rescue LoadError
|
16
|
+
end
|
data/acfs.gemspec
CHANGED
@@ -4,25 +4,26 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'acfs/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'acfs'
|
8
8
|
spec.version = Acfs::VERSION
|
9
9
|
spec.authors = ['Jan Graichen']
|
10
10
|
spec.email = %w(jg@altimos.de)
|
11
11
|
spec.description = %q{API Client For Services}
|
12
12
|
spec.summary = %q{An abstract API base client for service oriented application.}
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
13
|
+
spec.homepage = 'https://github.com/jgraichen/acfs'
|
14
|
+
spec.license = 'MIT'
|
15
15
|
|
16
16
|
spec.files = `git ls-files`.split($/)
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = %w(lib)
|
20
20
|
|
21
|
-
spec.add_runtime_dependency 'activesupport'
|
22
|
-
spec.add_runtime_dependency 'activemodel'
|
23
|
-
spec.add_runtime_dependency 'actionpack'
|
21
|
+
spec.add_runtime_dependency 'activesupport', '>= 3.1'
|
22
|
+
spec.add_runtime_dependency 'activemodel', '>= 3.1'
|
23
|
+
spec.add_runtime_dependency 'actionpack', '>= 3.1'
|
24
24
|
spec.add_runtime_dependency 'multi_json'
|
25
25
|
spec.add_runtime_dependency 'typhoeus'
|
26
|
+
spec.add_runtime_dependency 'bunny', '~> 0.9.0.pre12'
|
26
27
|
spec.add_runtime_dependency 'rack'
|
27
28
|
|
28
29
|
spec.add_development_dependency 'bundler', '~> 1.3'
|
data/lib/acfs.rb
CHANGED
data/lib/acfs/configuration.rb
CHANGED
@@ -3,15 +3,27 @@ require 'yaml'
|
|
3
3
|
|
4
4
|
module Acfs
|
5
5
|
|
6
|
-
#
|
6
|
+
# Acfs configuration is used to locate services and get their base URLs.
|
7
7
|
#
|
8
8
|
class Configuration
|
9
9
|
attr_reader :locations
|
10
10
|
|
11
|
+
# @api private
|
11
12
|
def initialize
|
12
13
|
@locations = {}
|
13
14
|
end
|
14
15
|
|
16
|
+
# @api public
|
17
|
+
#
|
18
|
+
# Configure using given block. If block accepts zero arguments
|
19
|
+
# bock will be evaluated in context of the configuration instance
|
20
|
+
# otherwise the configuration instance will be given as first arguments.
|
21
|
+
#
|
22
|
+
# @yield [configuration] Give configuration as arguments or evaluate block
|
23
|
+
# in context of configuration object.
|
24
|
+
# @yieldparam configuration [Configuration] Configuration object.
|
25
|
+
# @return [undefined]
|
26
|
+
#
|
15
27
|
def configure(&block)
|
16
28
|
if block.arity > 0
|
17
29
|
block.call self
|
@@ -20,6 +32,21 @@ module Acfs
|
|
20
32
|
end
|
21
33
|
end
|
22
34
|
|
35
|
+
# @api public
|
36
|
+
#
|
37
|
+
# @overload locate(service, uri)
|
38
|
+
# Configures URL where a service can be reached.
|
39
|
+
#
|
40
|
+
# @param [Symbol] service Service identity key for service that is reachable under given URL.
|
41
|
+
# @param [String] uri URL where service is reachable. Will be passed to {URI.parse}.
|
42
|
+
# @return [undefined]
|
43
|
+
#
|
44
|
+
# @overload locate(service)
|
45
|
+
# Return configured base URL for given service identity key.
|
46
|
+
#
|
47
|
+
# @param [Symbol] service Service identity key to lookup.
|
48
|
+
# @return [URI, NilClass] Configured base URL or nil.
|
49
|
+
#
|
23
50
|
def locate(service, uri = nil)
|
24
51
|
service = service.to_s.underscore.to_sym
|
25
52
|
if uri.nil?
|
@@ -29,6 +56,13 @@ module Acfs
|
|
29
56
|
end
|
30
57
|
end
|
31
58
|
|
59
|
+
# @api public
|
60
|
+
#
|
61
|
+
# Load configuration from given YAML file.
|
62
|
+
#
|
63
|
+
# @param [String] filename Path to YAML configuration file.
|
64
|
+
# @return [undefined]
|
65
|
+
#
|
32
66
|
def load(filename)
|
33
67
|
config = YAML::load File.read filename
|
34
68
|
env = ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
|
@@ -41,6 +75,10 @@ module Acfs
|
|
41
75
|
end
|
42
76
|
end
|
43
77
|
|
78
|
+
# @api private
|
79
|
+
#
|
80
|
+
# Load services from configuration YAML.
|
81
|
+
#
|
44
82
|
def load_services(services)
|
45
83
|
services.each do |service, data|
|
46
84
|
if (val = data).is_a?(String) || (val = data['locate'])
|
@@ -51,10 +89,23 @@ module Acfs
|
|
51
89
|
|
52
90
|
class << self
|
53
91
|
|
92
|
+
# @api private
|
93
|
+
#
|
94
|
+
# Return current configuration object.
|
95
|
+
#
|
96
|
+
# @return [Configuration]
|
97
|
+
#
|
54
98
|
def current
|
55
99
|
@configuration ||= new
|
56
100
|
end
|
57
101
|
|
102
|
+
# @api private
|
103
|
+
#
|
104
|
+
# Swap configuration object with given new one. Must be a {Configuration} object.
|
105
|
+
#
|
106
|
+
# @param [Configuration] configuration
|
107
|
+
# @return [undefined]
|
108
|
+
#
|
58
109
|
def set(configuration)
|
59
110
|
@configuration = configuration if configuration.is_a? Configuration
|
60
111
|
end
|
data/lib/acfs/global.rb
CHANGED
@@ -4,16 +4,30 @@ module Acfs
|
|
4
4
|
#
|
5
5
|
module Global
|
6
6
|
|
7
|
+
# @api private
|
8
|
+
# @return [Runner]
|
9
|
+
#
|
7
10
|
def runner
|
8
11
|
@runner ||= Runner.new Adapter::Typhoeus.new
|
9
12
|
end
|
10
13
|
|
14
|
+
# @api public
|
15
|
+
#
|
11
16
|
# Run all queued operations.
|
12
17
|
#
|
18
|
+
# @return [undefined]
|
19
|
+
#
|
13
20
|
def run
|
14
21
|
runner.start
|
15
22
|
end
|
16
23
|
|
24
|
+
# @api public
|
25
|
+
#
|
26
|
+
# Configure acfs using given block.
|
27
|
+
#
|
28
|
+
# @return [undefined]
|
29
|
+
# @see Configuration#configure
|
30
|
+
#
|
17
31
|
def configure(&block)
|
18
32
|
Configuration.current.configure &block
|
19
33
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'bunny'
|
2
|
+
|
3
|
+
module Acfs::Messaging
|
4
|
+
|
5
|
+
# @macro experimental
|
6
|
+
#
|
7
|
+
class Client
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@bunny ||= Bunny.new.tap do |bunny|
|
11
|
+
bunny.start
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def channel
|
16
|
+
@channel ||= @bunny.create_channel
|
17
|
+
end
|
18
|
+
|
19
|
+
def exchange
|
20
|
+
@exchange ||= @channel.topic 'acfs-0.17.0-2', auto_delete: true
|
21
|
+
end
|
22
|
+
|
23
|
+
def publish(routing_key, message)
|
24
|
+
exchange.publish MessagePack.pack(message), routing_key: routing_key
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
|
29
|
+
def instance
|
30
|
+
@instance ||= new
|
31
|
+
end
|
32
|
+
|
33
|
+
def register(receiver)
|
34
|
+
@receivers ||= []
|
35
|
+
@receivers << receiver.instance.tap { |r| r.init instance }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|