cistern 2.1.0 → 2.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5db3cc1e5afb1123c9118dffecb14ff09e856775
4
- data.tar.gz: baecd5ac5ed70c005256852169d83cf378878f13
3
+ metadata.gz: 32675f1c34663bf47fbea19c1f56e009884b8851
4
+ data.tar.gz: bd418e9510d7dfde5bc563a0e8a3471b174c6a2a
5
5
  SHA512:
6
- metadata.gz: 19a35e7fdc627b77faeaad765971c1bcb71674918209545b7d33adfa377be0dbb703ab3c38453c09074eea278064b824e86dc33cf8866cccf39c55c8de55ec5c
7
- data.tar.gz: 14993c84e82145d7d81f357a98bd50fcb8a9f0f99df0210529e2d9a84164fe005839a1ec4d8f1043240d522ac2cc4d7c790a9f1ef22f756d66be92d71c8be059
6
+ metadata.gz: 4f5044d67c9c3195a7ea0ec3f50186c4746aba1dec07cb16e582dc92d5b35219e913b9eeaef651ea75f71e6fc143603216339179627a516975741ccbf0c26014
7
+ data.tar.gz: 9db86cbe754c36b2c3f07e33b1b44c01d1c10dc97a746a61dd59cc1f616dd7574477b09c3ef0af24a5d1c7c025ff8a6d7162591c81ab4679746b2a1223bef0c7
data/Gemfile CHANGED
@@ -8,7 +8,7 @@ group :test do
8
8
  gem "guard-bundler", "~> 2.0", require: false
9
9
  gem "pry-nav"
10
10
  gem "rake"
11
- gem "rspec", "~> 2.99"
11
+ gem "rspec", "~> 3.3"
12
12
  gem "redis-namespace", "~> 1.4", "< 1.5"
13
13
  gem "codeclimate-test-reporter", require: false
14
14
  end
data/README.md CHANGED
@@ -9,6 +9,61 @@ Cistern helps you consistently build your API clients and faciliates building mo
9
9
 
10
10
  ## Usage
11
11
 
12
+ ### Custom Architecture
13
+
14
+ By default a service's `Request`, `Collection`, and `Model` are all classes. In Cistern ~> 3.0, the default will be modules.
15
+
16
+ You can modify your client's architecture to be forwards compatible by using `Cistern::Client.with`
17
+
18
+ ```ruby
19
+ class Foo::Client
20
+ include Cistern::Client.with(interface: :module)
21
+ end
22
+ ```
23
+
24
+ Now request classes would look like:
25
+
26
+ ```ruby
27
+ class Foo::GetBar
28
+ include Foo::Request
29
+
30
+ def real
31
+ "bar"
32
+ end
33
+ end
34
+ ```
35
+
36
+ Other options include `:collection`, `:request`, and `:model`. This options define the name of module or class interface for the service component.
37
+
38
+ If `Request` is to reserved for a model, then the `Request` component name can be remapped to `Prayer`
39
+
40
+ For example:
41
+
42
+ ```ruby
43
+ class Foo::Client
44
+ include Cistern::Client.with(request: "Prayer")
45
+ end
46
+ ```
47
+
48
+ allows a model named `Request` to exist
49
+
50
+ ```ruby
51
+ class Foo::Request < Foo::Model
52
+ identity :jovi
53
+ end
54
+ ```
55
+
56
+ while living on a `Prayer`
57
+
58
+ ```ruby
59
+ class Foo::GetBar < Foo::Prayer
60
+ def real
61
+ service.request.get("/wing")
62
+ end
63
+ end
64
+ ```
65
+
66
+
12
67
  ### Service
13
68
 
14
69
  This represents the remote service that you are wrapping. If the service name is `foo` then a good name is `Foo::Client`.
@@ -10,79 +10,120 @@ module Cistern::Client
10
10
  end
11
11
  end
12
12
 
13
+ # custom include
14
+ def self.with(options={})
15
+ client_module = Module.new
16
+
17
+ custom_include = <<-EOS
18
+ def self.included(klass)
19
+ Cistern::Client.setup(klass, #{options.inspect})
20
+
21
+ super
22
+ end
23
+ EOS
24
+
25
+ client_module.class_eval(custom_include, __FILE__, __LINE__)
26
+
27
+ client_module
28
+ end
29
+
30
+ # vanilla include
13
31
  def self.included(klass)
32
+ self.setup(klass)
33
+
34
+ super
35
+ end
36
+
37
+ def self.setup(klass, options={})
38
+ request_class = options[:request] || "Request"
39
+ collection_class = options[:collection] || "Collection"
40
+ model_class = options[:model] || "Model"
41
+
42
+ interface = options[:interface] || :class
43
+ interface_callback = (:class == interface) ? :inherited : :included
44
+
45
+ unless klass.name
46
+ raise ArgumentError, "can't turn anonymous class into a Cistern service"
47
+ end
48
+
14
49
  klass.class_eval <<-EOS, __FILE__, __LINE__
15
- module Collections
16
- include Cistern::Client::Collections
50
+ module Collections
51
+ include ::Cistern::Client::Collections
17
52
 
18
- def service
19
- #{klass.name}
20
- end
53
+ def service
54
+ #{klass.name}
21
55
  end
56
+ end
22
57
 
23
- def self.service
24
- #{klass.name}
25
- end
58
+ def self.service
59
+ #{klass.name}
60
+ end
26
61
 
27
- class Real
28
- def initialize(options={})
29
- end
62
+ class Real
63
+ def initialize(options={})
30
64
  end
65
+ end
31
66
 
32
- class Mock
33
- def initialize(options={})
34
- end
67
+ class Mock
68
+ def initialize(options={})
35
69
  end
70
+ end
36
71
 
37
- class Model
38
- include Cistern::Model
72
+ #{interface} #{model_class}
73
+ def self.#{interface_callback}(klass)
74
+ service.models << klass
39
75
 
40
- def self.inherited(klass)
41
- service.models << klass
42
- end
76
+ klass.send(:include, ::Cistern::Model)
77
+
78
+ super
79
+ end
43
80
 
44
- def self.service
45
- #{klass.name}
46
- end
81
+ def self.service
82
+ #{klass.name}
47
83
  end
84
+ end
48
85
 
49
- class Collection
50
- include Cistern::Collection
86
+ #{interface} #{collection_class}
87
+ include ::Cistern::Collection
51
88
 
52
- def self.inherited(klass)
53
- klass.send(:extend, Cistern::Attributes::ClassMethods)
54
- klass.send(:extend, Cistern::Collection::ClassMethods)
55
- klass.send(:include, Cistern::Attributes::InstanceMethods)
89
+ def self.#{interface_callback}(klass)
90
+ klass.send(:extend, Cistern::Attributes::ClassMethods)
91
+ klass.send(:extend, Cistern::Collection::ClassMethods)
92
+ klass.send(:include, Cistern::Attributes::InstanceMethods)
56
93
 
57
- service.collections << klass
58
- end
94
+ service.collections << klass
59
95
 
60
- def self.service
61
- #{klass.name}
62
- end
96
+ super
63
97
  end
64
98
 
65
- class Request
66
- include Cistern::Request
99
+ def self.service
100
+ #{klass.name}
101
+ end
102
+ end
67
103
 
68
- def self.inherited(klass)
69
- klass.extend(Cistern::Request::ClassMethods)
104
+ #{interface} #{request_class}
105
+ include ::Cistern::Request
70
106
 
71
- service.requests << klass
72
- end
107
+ def self.service
108
+ #{klass.name}
109
+ end
73
110
 
74
- def self.service
75
- #{klass.name}
76
- end
111
+ def self.#{interface_callback}(klass)
112
+ klass.extend(::Cistern::Request::ClassMethods)
77
113
 
78
- def _mock(*args)
79
- mock(*args)
80
- end
114
+ service.requests << klass
81
115
 
82
- def _real(*args)
83
- real(*args)
84
- end
116
+ super
85
117
  end
118
+
119
+ def _mock(*args)
120
+ mock(*args)
121
+ end
122
+
123
+ def _real(*args)
124
+ real(*args)
125
+ end
126
+ end
86
127
  EOS
87
128
 
88
129
  klass.send(:extend, Cistern::Client::ClassMethods)
@@ -97,8 +138,6 @@ module Cistern::Client
97
138
  klass::Real.send(:include, klass::Collections)
98
139
  klass::Real.send(:extend, Cistern::WaitFor)
99
140
  klass::Real.timeout_error = klass::Timeout
100
-
101
- super
102
141
  end
103
142
 
104
143
  module ClassMethods
@@ -1,5 +1,9 @@
1
1
  module Cistern::Request
2
2
  def self.service_request(service, klass, name)
3
+ unless klass.name
4
+ raise ArgumentError, "can't turn anonymous class into a Cistern request"
5
+ end
6
+
3
7
  service::Mock.module_eval <<-EOS, __FILE__, __LINE__
4
8
  def #{name}(*args)
5
9
  #{klass}.new(self)._mock(*args)
@@ -1,3 +1,3 @@
1
1
  module Cistern
2
- VERSION = "2.1.0"
2
+ VERSION = "2.2.1"
3
3
  end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe "client" do
4
+ context "with specific architecture" do
5
+ it "allows module-based interfaces" do
6
+ class ModuleClient
7
+ include Cistern::Client.with(interface: :module)
8
+ end
9
+
10
+ class ModuleClient::Shoot
11
+ include ModuleClient::Request
12
+
13
+ def real(mod)
14
+ mod % 9
15
+ end
16
+ end
17
+
18
+ class ModuleClient::Moon
19
+ include ModuleClient::Model
20
+
21
+ identity :on
22
+
23
+ def save
24
+ self.identity % 3
25
+ end
26
+ end
27
+
28
+ class ModuleClient::Moons
29
+ include ModuleClient::Collection
30
+
31
+ model ModuleClient::Moon
32
+
33
+ end
34
+
35
+ expect(
36
+ ModuleClient.new.shoot(12)
37
+ ).to eq(3)
38
+
39
+ expect(ModuleClient.collections).to contain_exactly(ModuleClient::Moons)
40
+ expect(ModuleClient.models).to contain_exactly(ModuleClient::Moon)
41
+ expect(ModuleClient.requests).to contain_exactly(ModuleClient::Shoot)
42
+
43
+ expect(
44
+ ModuleClient.new.moons.new(on: 5).save
45
+ ).to eq(2)
46
+ end
47
+
48
+ it "allows custom model interface" do
49
+ class AskClient
50
+ include Cistern::Client.with(model: "Ask", interface: :module)
51
+ end
52
+
53
+ class AskClient::Model
54
+ include AskClient::Ask
55
+
56
+ identity :id
57
+
58
+ def save
59
+ 9 % identity
60
+ end
61
+ end
62
+
63
+ expect(
64
+ AskClient.new.model(id: 9).save
65
+ ).to eq(0)
66
+ end
67
+ end
68
+ end
@@ -11,8 +11,6 @@ Bundler.require(:test)
11
11
  Cistern.deprecation_warnings = false
12
12
 
13
13
  RSpec.configure do |c|
14
- c.treat_symbols_as_metadata_keys_with_true_values = true
15
-
16
14
  if Kernel.respond_to?(:caller_locations)
17
15
  require File.expand_path('../../lib/cistern/coverage', __FILE__)
18
16
  else
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cistern
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Lane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-29 00:00:00.000000000 Z
11
+ date: 2015-10-02 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: API client framework extracted from Fog
14
14
  email:
@@ -50,6 +50,7 @@ files:
50
50
  - lib/cistern/timeout.rb
51
51
  - lib/cistern/version.rb
52
52
  - lib/cistern/wait_for.rb
53
+ - spec/client_spec.rb
53
54
  - spec/collection_spec.rb
54
55
  - spec/dirty_spec.rb
55
56
  - spec/formatter_spec.rb
@@ -87,6 +88,7 @@ signing_key:
87
88
  specification_version: 4
88
89
  summary: API client framework
89
90
  test_files:
91
+ - spec/client_spec.rb
90
92
  - spec/collection_spec.rb
91
93
  - spec/dirty_spec.rb
92
94
  - spec/formatter_spec.rb