response_state 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 07191e8d424f92fed0fef312a55e39af9bfb7167
4
+ data.tar.gz: 704aa92961c5bed9a8b6f3214ec81c8d16c75dd8
5
+ SHA512:
6
+ metadata.gz: 5f98c261f52b66717c562f1b3c76bbf761971ad7b9154468c7618a5ca6aaa876a8151f2de684e0a7a3ce3bb11ca1aa7ae79e401597f0f3a2eb82f375c7d406c6
7
+ data.tar.gz: 35943cdbbc29d7145b80718b6b812cbd3a52c00450f584ed762efdd36733984c9ff1eee10abaa229bdba2e27c66094f5e3dd009d64455680b691ca1cc5c0f8f4
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .ruby-version
7
+ .ruby-gemset
8
+ .rvmrc
9
+ Gemfile.lock
10
+ InstalledFiles
11
+ _yardoc
12
+ coverage
13
+ doc/
14
+ lib/bundler/man
15
+ pkg
16
+ rdoc
17
+ spec/reports
18
+ test/tmp
19
+ test/version_tmp
20
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in responsive_service.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 alexpeachey
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # Response State
2
+
3
+ The Response State gem is an implementation of the Response State pattern by @brianvh
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'response_state'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install response_state
18
+
19
+ ## Usage
20
+
21
+ ### ResponseState::Service
22
+
23
+ Create a service class and subclass ResponseState::Service.
24
+
25
+ ```ruby
26
+ class MyService < ResponseState::Service
27
+ def initialize(param)
28
+ @param = param
29
+ end
30
+
31
+ def call(&block)
32
+ # do some work
33
+ yield ResponseState::Response.new(:success)
34
+ end
35
+ end
36
+ ```
37
+
38
+ You must implement a `call` method.
39
+ Your call method should yield with a `ResponseState::Response`.
40
+
41
+ ### Response
42
+
43
+ A `ResponseState::Response` can take up to 4 arguments but must at least have the first argument which is the state of the response. In addition it can take a message, a context, and a set of valid states. The message by convention should
44
+ be a string but there are no restrictions. The context can be any object. The valid states should be an array of symbols
45
+ that are the allowed states. An exception will be thrown if initialized with a type of response that is not in the valid states if a set of valid states was specified.
46
+
47
+ ```ruby
48
+ response = Response.new(:success, 'You win!', {an_important_value: 'some value'})
49
+ response.type # :success
50
+ response.message # 'You win!'
51
+ response.context # {an_important_value: 'some value'}
52
+
53
+ response.success { puts 'I succeeded' } # I succeeded
54
+ response.failure { puts 'I failed' } # nil
55
+
56
+ response = Response.new(:foo, 'FOO!', {}, [:success, :failure])
57
+ # exception => Invalid type of response: foo
58
+
59
+ response = Response.new(:success, '', {}, [:success, :failure])
60
+ response.foo { puts 'Not going to work' }
61
+ # exception => NoMethodError: undefined method `foo'
62
+ ```
63
+
64
+ You can also choose to subclass `ResponseState::Response` and define valid states for all instances of that class.
65
+ If you want to only allow certain states, this is the prefered method,
66
+ rather than passing the 4th argument in the construction of the object.
67
+
68
+ ```ruby
69
+ class MyResponse < ResponseState::Response
70
+ valid_states :success, :failure
71
+ end
72
+
73
+ response = MyResponse.new(:success)
74
+ response.success { puts 'I succeeded' } # I succeeded
75
+ response.failure { puts 'I failed' } # nil
76
+ response.foo { puts 'Not going to work' }
77
+ # exception => NoMethodError: undefined method `foo'
78
+ ```
79
+
80
+ ### Your service API
81
+
82
+ Your service can now be used as such:
83
+
84
+ ```ruby
85
+ MyService.('Some param') do |response|
86
+ response.success { puts 'I was successful.' }
87
+ response.failure { puts 'I failed.' }
88
+ end
89
+ ```
90
+
91
+ ## Contributing
92
+
93
+ 1. Fork it
94
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
95
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
96
+ 4. Push to the branch (`git push origin my-new-feature`)
97
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
8
+ task :console do
9
+ require 'irb'
10
+ require 'irb/completion'
11
+ require 'response_state'
12
+ ARGV.clear
13
+ IRB.start
14
+ end
@@ -0,0 +1,37 @@
1
+ module ResponseState
2
+ class Response
3
+ attr_reader :state, :message, :context, :valid_states
4
+
5
+ def initialize(state, message=nil, context=nil, valid_states=nil)
6
+ @valid_states = Array(valid_states || self.class.class_valid_states)
7
+ raise "Invalid state of response: #{state}" unless (@valid_states.empty? || @valid_states.include?(state))
8
+ @state = state
9
+ @context = context
10
+ @message = message
11
+ end
12
+
13
+ def self.valid_states(*args)
14
+ @class_valid_states = Array(args)
15
+ end
16
+
17
+ def self.class_valid_states
18
+ @class_valid_states
19
+ end
20
+
21
+ def method_missing(method, *args, &block)
22
+ if validate_state?(method)
23
+ yield if method == state
24
+ else
25
+ super
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def validate_state?(call_state)
32
+ return true if valid_states.empty?
33
+ return true if valid_states.include?(call_state)
34
+ raise "Invalid state: #{call_state}"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,12 @@
1
+ module ResponseState
2
+ class Service
3
+
4
+ def self.call(*args, &block)
5
+ self.new(*args).call(&block)
6
+ end
7
+
8
+ def call(&block)
9
+ yield Response.new(:unimplemented, "A ResponseState::Service should implement the call method.\nThe call method should perform the relevant work of the service and yield a ResponseState::Response object.\n")
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module ResponseState
2
+ VERSION = "0.2.0"
3
+ end
@@ -0,0 +1,6 @@
1
+ require 'response_state/version'
2
+ require 'response_state/response'
3
+ require 'response_state/service'
4
+
5
+ module ResponseState
6
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'response_state/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'response_state'
8
+ spec.version = ResponseState::VERSION
9
+ spec.authors = ['alexpeachey']
10
+ spec.email = ['alex.peachey@originate.com']
11
+ spec.description = 'Easy to use response state pattern'
12
+ spec.summary = 'Easy to use response state pattern'
13
+ spec.homepage = 'http://github.com/Originate/response_state'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.3'
22
+ spec.add_development_dependency 'rake'
23
+ spec.add_development_dependency 'rspec'
24
+ spec.add_development_dependency 'simplecov'
25
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ class MyResponse < ResponseState::Response
4
+ valid_states :success, :failure
5
+ end
6
+
7
+ class MyService < ResponseState::Service
8
+ attr_reader :object, :pass
9
+
10
+ def initialize(object, pass)
11
+ @object = object
12
+ @pass = pass
13
+ end
14
+
15
+ def call
16
+ yield (success_response or failure_response)
17
+ end
18
+
19
+ private
20
+
21
+ def success_response
22
+ return unless pass
23
+ MyResponse.new(:success, 'Yay! It works.', object)
24
+ end
25
+
26
+ def failure_response
27
+ MyResponse.new(:failure, 'Boo! It failed.')
28
+ end
29
+ end
30
+
31
+ describe 'Using the service' do
32
+ let(:object) { Object.new }
33
+ let(:yielded) { OpenStruct.new(message: nil, context: nil) }
34
+ before do
35
+ MyService.(object, pass) do |response|
36
+ response.success { yielded.message = response.message; yielded.context = response.context }
37
+ response.failure { yielded.message = response.message; yielded.context = response.context }
38
+ end
39
+ end
40
+
41
+ context 'when pass is true' do
42
+ let(:pass) { true }
43
+
44
+ it 'has a happy message' do
45
+ expect(yielded.message).to eq 'Yay! It works.'
46
+ end
47
+
48
+ it 'has a valid context' do
49
+ expect(yielded.context).to eq object
50
+ end
51
+ end
52
+
53
+ context 'when pass is false' do
54
+ let(:pass) { false }
55
+
56
+ it 'has a sad message' do
57
+ expect(yielded.message).to eq 'Boo! It failed.'
58
+ end
59
+
60
+ it 'has a nil context' do
61
+ expect(yielded.context).to eq nil
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,98 @@
1
+ require 'spec_helper'
2
+
3
+ module ResponseState
4
+ describe Response do
5
+ subject(:response) { Response.new(state) }
6
+ let(:state) { :success }
7
+ before { Response.instance_variable_set :@class_valid_states, nil }
8
+
9
+ it 'yields when sent :success with a state of :success' do
10
+ expect { |b| response.success(&b) }.to yield_control
11
+ end
12
+
13
+ it 'does not yield when sent :failure with a state of :success' do
14
+ expect { |b| response.failure(&b) }.not_to yield_control
15
+ end
16
+
17
+ context 'when initialized with a message' do
18
+ subject(:response) { Response.new(state, message) }
19
+ let(:message) { double(:message) }
20
+
21
+ specify { expect(response.message).to eq message }
22
+
23
+ context 'when additionally initialized with a context' do
24
+ subject(:response) { Response.new(state, message, context) }
25
+ let(:context) { double(:context) }
26
+
27
+ specify { expect(response.context).to eq context }
28
+
29
+ context 'when additionally initiaized with a set of valid states' do
30
+ subject(:response) { Response.new(state, message, context, valid_states) }
31
+ let(:valid_states) { [:success, :failure] }
32
+
33
+ it 'yields when sent :success with a state of :success' do
34
+ expect { |b| response.success(&b) }.to yield_control
35
+ end
36
+
37
+ it 'does not yield when sent :failure with a state of :success' do
38
+ expect { |b| response.failure(&b) }.not_to yield_control
39
+ end
40
+
41
+ it 'throws method missing exception if sent a non-valid state' do
42
+ expect { |b| response.foo(&b) }.to raise_exception
43
+ end
44
+
45
+ it 'reflects the valid_states' do
46
+ expect(response.valid_states).to eq [:success, :failure]
47
+ end
48
+
49
+ context 'when initialized with an invalid state' do
50
+ let(:state) { :foo }
51
+
52
+ it 'raises an exception' do
53
+ expect { response }.to raise_exception('Invalid state of response: foo')
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ describe '.valid_states' do
61
+ context 'when set to an array of states' do
62
+ before { Response.valid_states(:success, :failure) }
63
+
64
+ context 'when the response state is :success' do
65
+ subject(:response) { Response.new(:success) }
66
+
67
+ it 'yields when sent :success' do
68
+ expect { |b| response.success(&b) }.to yield_control
69
+ end
70
+
71
+ it 'does not yield when sent :failure' do
72
+ expect { |b| response.failure(&b) }.not_to yield_control
73
+ end
74
+
75
+ it 'throws method missing exception if sent a non-valid state' do
76
+ expect { |b| response.foo(&b) }.to raise_exception
77
+ end
78
+ end
79
+
80
+ context 'when initialized with an invalid state' do
81
+ subject(:response) { Response.new(:foo) }
82
+
83
+ it 'raises an exception' do
84
+ expect { response }.to raise_exception('Invalid state of response: foo')
85
+ end
86
+ end
87
+
88
+ describe '#valid_states' do
89
+ subject(:response) { Response.new(:success) }
90
+
91
+ it 'reflects the valid_states' do
92
+ expect(response.valid_states).to eq [:success, :failure]
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+
4
+ module ResponseState
5
+ describe Service do
6
+ subject(:service) { Service.new }
7
+ before { Response.instance_variable_set :@class_valid_states, nil }
8
+
9
+ describe '.call' do
10
+ it 'news up an instance and passes the block on to #call' do
11
+ expect { |b| Service.(&b) }.to yield_control
12
+ end
13
+ end
14
+
15
+ describe '#call' do
16
+ let(:yielded) { OpenStruct.new(response: nil) }
17
+ let(:response) { yielded.response }
18
+ before { service.call { |response| yielded.response = response} }
19
+
20
+ it 'yields an unimplemented response' do
21
+ expect(response.state).to eq :unimplemented
22
+ end
23
+
24
+ it 'yields with a response indicating instructions' do
25
+ expect(response.message).to eq "A ResponseState::Service should implement the call method.\nThe call method should perform the relevant work of the service and yield a ResponseState::Response object.\n"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,27 @@
1
+ require 'simplecov'
2
+
3
+ SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
4
+
5
+ SimpleCov.start do
6
+ command_name 'spec'
7
+ add_filter 'config/'
8
+ add_filter 'spec'
9
+ add_filter '.bundle'
10
+ minimum_coverage 100
11
+ end
12
+
13
+ require 'rspec'
14
+ require 'response_state'
15
+
16
+ RSpec.configure do |config|
17
+ config.mock_with :rspec
18
+ config.treat_symbols_as_metadata_keys_with_true_values = true
19
+ config.run_all_when_everything_filtered = true
20
+ config.filter_run :focus
21
+
22
+ # Run specs in random order to surface order dependencies. If you find an
23
+ # order dependency and want to debug it, you can fix the order by providing
24
+ # the seed, which is printed after each run.
25
+ # --seed 1234
26
+ config.order = 'random'
27
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: response_state
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - alexpeachey
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-16 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: simplecov
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
+ description: Easy to use response state pattern
70
+ email:
71
+ - alex.peachey@originate.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - .rspec
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - lib/response_state.rb
83
+ - lib/response_state/response.rb
84
+ - lib/response_state/service.rb
85
+ - lib/response_state/version.rb
86
+ - response_state.gemspec
87
+ - spec/features/full_example_spec.rb
88
+ - spec/lib/response_state/response_spec.rb
89
+ - spec/lib/response_state/service_spec.rb
90
+ - spec/spec_helper.rb
91
+ homepage: http://github.com/Originate/response_state
92
+ licenses:
93
+ - MIT
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.2.2
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Easy to use response state pattern
115
+ test_files:
116
+ - spec/features/full_example_spec.rb
117
+ - spec/lib/response_state/response_spec.rb
118
+ - spec/lib/response_state/service_spec.rb
119
+ - spec/spec_helper.rb