tzu 0.0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b809317c8b5ff13fcb4784360d28703691ea3d9d
4
+ data.tar.gz: 141562074c39e1d23fc945f2d6d7bf825eb37c6c
5
+ SHA512:
6
+ metadata.gz: 1b231785af2e5ca4fab9e1a8590676875f87796592050391f9e32a2358e70878e31cc85c42b5c383c2fb484ab00afc7abb549c15a56a5f0b29fa0dccd3f96878
7
+ data.tar.gz: bff733337e18acb528bd37978f3034f67e788225b23b487c878f60d00bca8c0ef95b07c362ec5166024b633b102f968d1c0418451b7ab13a4accee6911d4ff93
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2015 Blake Turner
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1 @@
1
+ # Tzu
@@ -0,0 +1,88 @@
1
+ require 'tzu/failure'
2
+ require 'tzu/hooks'
3
+ require 'tzu/invalid'
4
+ require 'tzu/match'
5
+ require 'tzu/organizer'
6
+ require 'tzu/outcome'
7
+ require 'tzu/validation'
8
+ require 'tzu/validation_result'
9
+
10
+ module Tzu
11
+ def self.included(base)
12
+ base.class_eval do
13
+ extend ClassMethods
14
+ include Hooks
15
+ end
16
+ end
17
+
18
+ module ClassMethods
19
+ def run(params, *context, &block)
20
+ result = get_instance(*context).run(params)
21
+ if block
22
+ result.handle(&block)
23
+ else
24
+ result
25
+ end
26
+ end
27
+
28
+ def run!(params, *context)
29
+ get_instance(*context).run!(params)
30
+ end
31
+
32
+ def get_instance(*context)
33
+ method = respond_to?(:build) ? :build : :new
34
+ send(method, *context)
35
+ end
36
+
37
+ def command_name(value = nil)
38
+ if value.nil?
39
+ @name ||= name.underscore.to_sym
40
+ else
41
+ @name = (value.presence && value.to_sym)
42
+ end
43
+ end
44
+ end
45
+
46
+ def command_name
47
+ self.class.command_name
48
+ end
49
+
50
+ def run(params)
51
+ run!(request_object(params))
52
+ rescue Failure => f
53
+ Outcome.new(false, f.errors, f.type)
54
+ end
55
+
56
+ def run!(params)
57
+ with_hooks(params) do |p|
58
+ outcome = call(p)
59
+ outcome.is_a?(Tzu::Outcome) ? outcome : Outcome.new(true, outcome)
60
+ end
61
+ rescue
62
+ rollback! if self.respond_to?(:rollback!)
63
+ raise
64
+ end
65
+
66
+ def fail!(type, data={})
67
+ raise Failure.new(type, data)
68
+ end
69
+
70
+ private
71
+
72
+ def request_object(params)
73
+ klass = request_class
74
+ return klass.new(params) if klass && !params.is_a?(klass)
75
+ params
76
+ end
77
+
78
+ # Get the name of a request class related to calling class
79
+ # ie. Tzus::MyNameSpace::MyTzu
80
+ # has Tzus::MyNameSpace::Requests::MyTzu
81
+ def request_class
82
+ namespace = self.class.name.deconstantize.constantize
83
+ request_object_name = "Requests::#{ self.class.name.demodulize}"
84
+ namespace.qualified_const_get(request_object_name)
85
+ rescue NameError
86
+ false
87
+ end
88
+ end
@@ -0,0 +1,10 @@
1
+ module Tzu
2
+ class Failure < StandardError
3
+ attr_reader :errors, :type
4
+
5
+ def initialize(type = nil, errors = nil)
6
+ @errors, @type = errors, type
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,57 @@
1
+ module Tzu
2
+
3
+ # Provides hooks for arbitrary pre- and post- processing within a command
4
+ module Hooks
5
+ def self.included(base)
6
+ base.class_eval do
7
+ extend ClassMethods
8
+ end
9
+ end
10
+
11
+ module ClassMethods
12
+ def before(*hooks, &block)
13
+ hooks << block if block
14
+ hooks.each { |hook| before_hooks.push(hook) }
15
+ end
16
+
17
+ def after(*hooks, &block)
18
+ hooks << block if block
19
+ hooks.each { |hook| after_hooks.push(hook) }
20
+ end
21
+
22
+ def before_hooks
23
+ @before_hooks ||= []
24
+ end
25
+
26
+ def after_hooks
27
+ @after_hooks ||= []
28
+ end
29
+ end
30
+
31
+ def with_hooks(params, &block)
32
+ run_before_hooks(params)
33
+ result = block.call(params)
34
+ run_after_hooks(params)
35
+ result
36
+ end
37
+
38
+ private
39
+
40
+ def run_before_hooks(params)
41
+ run_hooks(self.class.before_hooks, params)
42
+ end
43
+
44
+ def run_after_hooks(params)
45
+ run_hooks(self.class.after_hooks, params)
46
+ end
47
+
48
+ def run_hooks(hooks, params)
49
+ hooks.each { |hook| run_hook(hook, params) }
50
+ end
51
+
52
+ def run_hook(hook, args)
53
+ hook.is_a?(Symbol) ? send(hook, args) : instance_exec(args, &hook)
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,9 @@
1
+ module Tzu
2
+ class Invalid < Failure
3
+
4
+ def initialize(errors = nil)
5
+ super(:validation, errors)
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,55 @@
1
+ module Tzu
2
+
3
+ # with thanks to https://github.com/pzol/deterministic
4
+ class Match
5
+ def initialize(outcome, context)
6
+ @outcome, @context, @collection = outcome, context, []
7
+ end
8
+
9
+ def result
10
+ matcher = @collection.detect { |m| m.matches?(@outcome.type) }
11
+ raise "No match could be made for #{@outcome}" if matcher.nil?
12
+ @context.instance_exec(@outcome.result, &matcher.block)
13
+ end
14
+
15
+ %w(success failure).each do |type|
16
+ define_method type.to_sym do |condition=nil, &result_block|
17
+ push(type, condition, result_block)
18
+ end
19
+ end
20
+
21
+ # todo: hash and define_method if more overrides identified
22
+ def invalid(&result_block)
23
+ push('failure', :validation, result_block)
24
+ end
25
+
26
+ private
27
+
28
+ Matcher = Struct.new(:condition, :block) do
29
+ def matches?(value)
30
+ condition.call(value)
31
+ end
32
+ end
33
+
34
+ def push(type, condition, result_block)
35
+ condition_pred = case
36
+ when condition.nil?; ->(v) { true }
37
+ when condition.is_a?(Proc); condition
38
+ when condition.is_a?(Class); ->(v) { condition === @outcome.type }
39
+ else ->(v) { @outcome.type == condition }
40
+ end
41
+
42
+ matcher_pred = compose_predicates(type_pred[type], condition_pred)
43
+ @collection << Matcher.new(matcher_pred, result_block)
44
+ end
45
+
46
+ def compose_predicates(f, g)
47
+ ->(*args) { f[*args] && g[*args] }
48
+ end
49
+
50
+ # return a partial function for matching a matcher's type
51
+ def type_pred
52
+ (->(type, x) { @outcome.send("#{type.to_s}?") }).curry
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,35 @@
1
+ require 'ostruct'
2
+
3
+ module Tzu
4
+ module Organizer
5
+ def self.included(base)
6
+ base.class_eval do
7
+ include Tzu
8
+ end
9
+ end
10
+
11
+ Step = Struct.new(:command, :transform)
12
+
13
+ def steps
14
+ @steps ||= []
15
+ end
16
+
17
+ def add_step(command, &transform)
18
+ steps << Step.new(command, transform)
19
+ end
20
+
21
+ def call(params)
22
+ result = call_steps(params)
23
+ self.respond_to?(:parse) ? parse(result) : result
24
+ end
25
+
26
+ def call_steps(params)
27
+ result = ::OpenStruct.new
28
+ steps.each do |step|
29
+ call_with = step.transform ? step.transform(params, result) : params
30
+ result[step.command.command_name] = step.command.run!(call_with)
31
+ end
32
+ result
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,28 @@
1
+ module Tzu
2
+
3
+ # The result of executing a command
4
+ class Outcome
5
+ attr_reader :success, :result, :type
6
+
7
+ def initialize(success, result, type = nil)
8
+ @success = success
9
+ @result = result
10
+ @type = type
11
+ end
12
+
13
+ def success?
14
+ @success
15
+ end
16
+
17
+ def failure?
18
+ !@success
19
+ end
20
+
21
+ def handle(context=nil, &block)
22
+ context ||= block.binding.eval('self')
23
+ match = Match.new(self, context)
24
+ match.instance_eval &block
25
+ match.result
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,43 @@
1
+ module Tzu
2
+ module Validation
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+ # registers validation as a before hook
7
+ before :when_valid
8
+ end
9
+ end
10
+
11
+ def when_valid(params)
12
+ validation_result = validate(params)
13
+ invalid!(validation_result.errors) unless validation_result.valid?
14
+ validation_result
15
+ end
16
+
17
+ def validate(params)
18
+ # see if the command defines a valid? method
19
+ if self.respond_to?(:valid?)
20
+ result = valid?(params)
21
+ errors = self.respond_to?(:errors) ? self.errors : nil
22
+ return !!result == result ? ValidationResult.new(result, errors) : result
23
+ end
24
+
25
+ # do the params define their own validation method (cf. ActiveRecord)
26
+ if params.respond_to?(:valid?)
27
+ return ValidationResult.new(params.valid?, params.errors)
28
+ end
29
+
30
+ # otherwise valid
31
+ return ValidationResult.new(true)
32
+ end
33
+
34
+ def invalid!(obj)
35
+ output = [:errors, :messages].reduce(obj) do |result, m|
36
+ result = result.respond_to?(m) ? result.send(m) : result
37
+ end
38
+
39
+ raise Invalid.new(output)
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,13 @@
1
+ module Tzu
2
+ class ValidationResult
3
+ attr_reader :errors, :valid
4
+
5
+ def initialize(valid, errors = [])
6
+ @valid, @errors = valid, errors
7
+ end
8
+
9
+ def valid?
10
+ @valid
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe Tzu do
4
+
5
+ context 'command fails' do
6
+ subject do
7
+ Class.new do
8
+ include Tzu
9
+
10
+ def call(params)
11
+ fail! :something
12
+ end
13
+ end
14
+ end
15
+
16
+ it 'returns failure' do
17
+ result = subject.run(nil)
18
+ expect(result).to have_attributes(success: false, type: :something)
19
+ end
20
+ end
21
+
22
+ context 'command succeeds' do
23
+ subject do
24
+ Class.new do
25
+ include Tzu
26
+
27
+ def call(params)
28
+ 1234
29
+ end
30
+ end
31
+ end
32
+
33
+ it 'returns result' do
34
+ result = subject.run(nil)
35
+ expect(result).to have_attributes(success: true, result: 1234)
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,169 @@
1
+ require 'spec_helper'
2
+
3
+ describe Tzu::Hooks do
4
+ describe '#with_hooks' do
5
+ def build_hooked(&block)
6
+ hooked = Class.new.send(:include, Tzu::Hooks)
7
+
8
+ hooked.class_eval do
9
+ attr_reader :steps
10
+
11
+ def self.process
12
+ new.tap(&:process).steps
13
+ end
14
+
15
+ def initialize
16
+ @steps = []
17
+ end
18
+
19
+ def process
20
+ with_hooks({}) { steps << :process }
21
+ end
22
+ end
23
+
24
+ hooked.class_eval(&block) if block
25
+ hooked
26
+ end
27
+
28
+ context 'when before hook is a method' do
29
+ let(:hooked) do
30
+ build_hooked do
31
+ before :add_before
32
+
33
+ def add_before(p)
34
+ steps << :before
35
+ end
36
+ end
37
+ end
38
+
39
+ it 'runs the before hook method' do
40
+ expect(hooked.process).to eq([:before, :process])
41
+ end
42
+ end
43
+
44
+ context 'when before hook is a block' do
45
+ let(:hooked) do
46
+ build_hooked do
47
+ before do
48
+ steps << :before
49
+ end
50
+ end
51
+ end
52
+
53
+ it 'runs the before hook block' do
54
+ expect(hooked.process).to eq([:before, :process])
55
+ end
56
+ end
57
+
58
+ context 'when after hook is a method' do
59
+ let(:hooked) do
60
+ build_hooked do
61
+ after :add_after
62
+
63
+ def add_after(p)
64
+ steps << :after
65
+ end
66
+ end
67
+ end
68
+
69
+ it 'runs the after hook method' do
70
+ expect(hooked.process).to eq([:process, :after])
71
+ end
72
+ end
73
+
74
+ context 'when after hook is a block' do
75
+ let(:hooked) do
76
+ build_hooked do
77
+ after do
78
+ steps << :after
79
+ end
80
+ end
81
+ end
82
+
83
+ it 'runs the after hook block' do
84
+ expect(hooked.process).to eq([:process, :after])
85
+ end
86
+ end
87
+
88
+ context 'when both before and after blocks are defined' do
89
+ let(:hooked) do
90
+ build_hooked do
91
+ before do
92
+ steps << :before
93
+ end
94
+
95
+ after do
96
+ steps << :after
97
+ end
98
+ end
99
+ end
100
+
101
+ it 'runs hooks in the expected order' do
102
+ expect(hooked.process).to eq([:before, :process, :after])
103
+ end
104
+ end
105
+
106
+
107
+ context 'when both before and after methods are defined' do
108
+ let(:hooked) do
109
+ build_hooked do
110
+ before :add_before
111
+ after :add_after
112
+
113
+ def add_before(p)
114
+ steps << :before
115
+ end
116
+
117
+ def add_after(p)
118
+ steps << :after
119
+ end
120
+ end
121
+ end
122
+
123
+ it 'runs hooks in the expected order' do
124
+ expect(hooked.process).to eq([:before, :process, :after])
125
+ end
126
+ end
127
+
128
+ context 'when multiple before methods are defined' do
129
+ let(:hooked) do
130
+ build_hooked do
131
+ before :add_before1, :add_before2
132
+
133
+ def add_before1(p)
134
+ steps << :before1
135
+ end
136
+
137
+ def add_before2(p)
138
+ steps << :before2
139
+ end
140
+ end
141
+ end
142
+
143
+ it 'runs hooks in the expected order' do
144
+ expect(hooked.process).to eq([:before1, :before2, :process])
145
+ end
146
+ end
147
+
148
+ context 'when multiple after methods are defined' do
149
+ let(:hooked) do
150
+ build_hooked do
151
+ after :add_after1, :add_after2
152
+
153
+ def add_after1(p)
154
+ steps << :after1
155
+ end
156
+
157
+ def add_after2(p)
158
+ steps << :after2
159
+ end
160
+ end
161
+ end
162
+
163
+ it 'runs hooks in the expected order' do
164
+ expect(hooked.process).to eq([:process, :after1, :after2])
165
+ end
166
+ end
167
+
168
+ end
169
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe Tzu::Organizer do
4
+ let(:params) { [1, 2, 3] }
5
+ let(:commands) { [spy(run!: :step1, command_name: :step1), spy(run!: :step2, command_name: :step2)] }
6
+
7
+ let(:organized) do
8
+ organized = Class.new do
9
+ include Tzu::Organizer
10
+
11
+ def initialize(commands)
12
+ commands.each { |s| add_step(s) }
13
+ end
14
+ end
15
+
16
+ organized
17
+ end
18
+
19
+ context 'when organizer is called' do
20
+
21
+ it 'calls each step' do
22
+ organized.run(params, commands)
23
+ commands.each { |s| expect(s).to have_received(:run!).with(params) }
24
+ end
25
+
26
+ it 'collates result of steps' do
27
+ result = organized.run(params, commands)
28
+ expect(result.result.step1).to eq(:step1)
29
+ expect(result.result.step2).to eq(:step2)
30
+ end
31
+ end
32
+
33
+ context 'when organizer defines parse' do
34
+ before do
35
+ organized.class_eval do
36
+ def parse(result)
37
+ 12345
38
+ end
39
+ end
40
+ end
41
+
42
+ it 'it parses results from commands' do
43
+ result = organized.run(params, commands)
44
+ expect(result).to have_attributes(success: true, result: 12345)
45
+ end
46
+ end
47
+
48
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe Tzu::Outcome do
4
+
5
+ context 'outcome is failed with specified type' do
6
+ subject { Tzu::Outcome.new(false, 'abc', :validation) }
7
+
8
+ it 'calls appropriate handler' do
9
+ matched = false
10
+ subject.handle do
11
+ success { raise }
12
+ failure(:something) { raise }
13
+ failure(:validation) { matched = true }
14
+ end
15
+ expect(matched).to eq true
16
+ end
17
+ end
18
+
19
+ context 'outcome is failed with unspecified type' do
20
+ subject { Tzu::Outcome.new(false, 'abc', :validation) }
21
+
22
+ it 'calls appropriate handler' do
23
+ matched = false
24
+ subject.handle do
25
+ success { raise }
26
+ failure { matched = true }
27
+ failure(:validation) { raise }
28
+ end
29
+ expect(matched).to eq true
30
+ end
31
+ end
32
+
33
+ context 'outcome is successful' do
34
+ subject { Tzu::Outcome.new(true, 'abc') }
35
+
36
+ it 'calls success handler' do
37
+ matched = false
38
+ subject.handle do
39
+ success { matched = true }
40
+ failure(:something) { raise }
41
+ end
42
+ expect(matched).to eq true
43
+ end
44
+ end
45
+ end
46
+
47
+
48
+
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'byebug'
3
+ require 'rspec'
4
+ require 'active_record'
5
+ require 'bundler/setup'
6
+ Bundler.setup
7
+
8
+ require 'tzu'
@@ -0,0 +1,354 @@
1
+ # require 'spec_helper'
2
+ #
3
+ # if !defined?(ActiveRecord::Base)
4
+ # puts "** require 'active_record' to run the specs in #{__FILE__}"
5
+ # else
6
+ # ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
7
+ #
8
+ # ActiveRecord::Migration.suppress_messages do
9
+ # ActiveRecord::Schema.define(:version => 0) do
10
+ # create_table(:employers, force: true) {|t| t.string :name }
11
+ # create_table(:users, force: true) {|t| t.string :first_name; t.string :last_name; t.references :employer; }
12
+ # create_table(:sports_cars, force: true) {|t| t.string :make; t.references :employer; }
13
+ # end
14
+ # end
15
+ #
16
+ # module GetSpec
17
+ # class Employer < ActiveRecord::Base
18
+ # has_many :users
19
+ # has_many :sports_cars
20
+ # end
21
+ #
22
+ # class User < ActiveRecord::Base
23
+ # belongs_to :employer
24
+ # end
25
+ #
26
+ # class SportsCar < ActiveRecord::Base
27
+ # belongs_to :employer
28
+ # end
29
+ # end
30
+ # end
31
+ #
32
+ # describe Get do
33
+ # let(:last_name) { 'Turner' }
34
+ # let(:adapter) { :active_record }
35
+ #
36
+ # # Preserve system config for other tests
37
+ # before(:all) { @system_config = Get.configuration }
38
+ # after(:all) { Get.configuration = @system_config }
39
+ #
40
+ # # Reset base config with each iteration
41
+ # before { Get.configure { |config| config.set_adapter(adapter) } }
42
+ # after do
43
+ # GetSpec::User.delete_all
44
+ # GetSpec::Employer.delete_all
45
+ # Get.reset
46
+ # end
47
+ #
48
+ # class MyCustomEntity < Horza::Entities::Collection
49
+ # def east_london_length
50
+ # "#{length}, bruv"
51
+ # end
52
+ # end
53
+ #
54
+ # describe '#configure' do
55
+ # context '#register_entity' do
56
+ # let(:user_count) { 3 }
57
+ #
58
+ # before do
59
+ # Get.configure { |config| config.register_entity(:users_by_last_name, MyCustomEntity) }
60
+ # user_count.times { GetSpec::User.create(last_name: last_name) }
61
+ # end
62
+ # after { Get.reset }
63
+ #
64
+ # it 'gets registers entity' do
65
+ # expect(Get.configuration.entity_for(:users_by_last_name)).to eq MyCustomEntity
66
+ # end
67
+ #
68
+ # it 'returns specified entity type after querying db' do
69
+ # result = Get::UsersByLastName.run(last_name)
70
+ # expect(result.is_a? MyCustomEntity).to be true
71
+ # expect(result.east_london_length).to eq "#{user_count}, bruv"
72
+ # end
73
+ # end
74
+ # end
75
+ #
76
+ # context '#entity_for' do
77
+ # context 'when entity has been registered' do
78
+ # before do
79
+ # Get.configure do |config|
80
+ # config.set_adapter(adapter)
81
+ # config.register_entity(:users_by_last_name, MyCustomEntity)
82
+ # end
83
+ # end
84
+ # after { Get.reset }
85
+ #
86
+ # it 'registers entity' do
87
+ # expect(Get.entity_for(:users_by_last_name)).to eq MyCustomEntity
88
+ # end
89
+ # end
90
+ #
91
+ # context 'when entity has not been registered' do
92
+ # it 'returns nil' do
93
+ # expect(Get.entity_for(:users_by_last_name)).to be nil
94
+ # end
95
+ # end
96
+ # end
97
+ #
98
+ # context '#adapter' do
99
+ # context 'when the adapter is set' do
100
+ # it 'returns the correct adapter class' do
101
+ # expect(Get.adapter).to eq Horza::Adapters::ActiveRecord
102
+ # end
103
+ # end
104
+ #
105
+ # context 'when the adapter is not set' do
106
+ # before { Get.reset }
107
+ # after { Get.reset }
108
+ #
109
+ # it 'throws error' do
110
+ # expect { Get.adapter }.to raise_error(Get::Errors::Base)
111
+ # end
112
+ # end
113
+ # end
114
+ #
115
+ # context '#reset' do
116
+ # before do
117
+ # Get.configure do |config|
118
+ # config.set_adapter('my_adapter')
119
+ # config.register_entity(:users_by_last_name, MyCustomEntity)
120
+ # end
121
+ # Get.reset
122
+ # end
123
+ # it 'resets the config' do
124
+ # expect(Get.configuration.adapter).to be nil
125
+ # expect(Get.entity_for(:users_by_last_name)).to be nil
126
+ # end
127
+ # end
128
+ #
129
+ # context '#run!' do
130
+ # context 'singular form' do
131
+ # context 'when the record exists' do
132
+ # let!(:user) { GetSpec::User.create(last_name: last_name) }
133
+ #
134
+ # context 'field in class name' do
135
+ # it 'gets the records based on By[KEY]' do
136
+ # result = Get::UserById.run!(user.id)
137
+ # expect(result.to_h).to eq user.attributes
138
+ # end
139
+ #
140
+ # it 'returns a dynamically generated response entity' do
141
+ # expect(Get::UserById.run!(user.id).is_a?(Horza::Entities::Single)).to be true
142
+ # end
143
+ # end
144
+ #
145
+ # context 'field in parameters' do
146
+ # it 'gets the records based on parameters' do
147
+ # result = Get::UserBy.run!(last_name: last_name)
148
+ # expect(result.to_h).to eq user.attributes
149
+ # end
150
+ #
151
+ # it 'returns a dynamically generated response entity' do
152
+ # expect(Get::UserBy.run!(last_name: last_name).is_a?(Horza::Entities::Single)).to be true
153
+ # end
154
+ # end
155
+ # end
156
+ #
157
+ # context 'when the record does not exist' do
158
+ # it 'returns nil' do
159
+ # expect { Get::UserById.run!(999) }.to raise_error Get::Errors::Base
160
+ # end
161
+ # end
162
+ # end
163
+ #
164
+ # context 'ancestry' do
165
+ # context 'valid ancestry with no saved parent' do
166
+ # let(:user2) { GetSpec::User.create }
167
+ # it 'returns nil' do
168
+ # expect { Get::EmployerFromUser.run!(user2) }.to raise_error Get::Errors::RecordNotFound
169
+ # end
170
+ # end
171
+ # end
172
+ # end
173
+ #
174
+ # context '#run' do
175
+ # context 'singular form' do
176
+ # context 'when the record exists' do
177
+ # let!(:user) { GetSpec::User.create(last_name: last_name) }
178
+ #
179
+ # context 'field in class name' do
180
+ # it 'gets the records based on By[KEY]' do
181
+ # result = Get::UserById.run(user.id)
182
+ # expect(result.to_h).to eq user.attributes
183
+ # end
184
+ #
185
+ # it 'returns a dynamically generated response entity' do
186
+ # expect(Get::UserById.run(user.id).is_a?(Horza::Entities::Single)).to be true
187
+ # end
188
+ # end
189
+ #
190
+ # context 'field in parameters' do
191
+ # it 'gets the records based on parameters' do
192
+ # result = Get::UserBy.run(last_name: last_name)
193
+ # expect(result.to_h).to eq user.attributes
194
+ # end
195
+ #
196
+ # it 'returns a dynamically generated response entity' do
197
+ # expect(Get::UserBy.run(last_name: last_name).is_a?(Horza::Entities::Single)).to be true
198
+ # end
199
+ # end
200
+ # end
201
+ #
202
+ # context 'when the record does not exist' do
203
+ # it 'returns nil' do
204
+ # expect(Get::UserById.run(999)).to eq nil
205
+ # end
206
+ # end
207
+ # end
208
+ #
209
+ # context 'plural form' do
210
+ # let(:last_name) { 'Turner' }
211
+ # let(:match_count) { 3 }
212
+ # let(:miss_count) { 2 }
213
+ #
214
+ # context 'when records exist' do
215
+ # before do
216
+ # match_count.times { GetSpec::User.create(last_name: last_name) }
217
+ # miss_count.times { GetSpec::User.create }
218
+ # end
219
+ #
220
+ # context 'field in class name' do
221
+ # it 'gets the records based on By[KEY]' do
222
+ # result = Get::UsersByLastName.run(last_name)
223
+ # expect(result.length).to eq match_count
224
+ # end
225
+ #
226
+ # it 'returns a dynamically generated response entity' do
227
+ # expect(Get::UsersByLastName.run(last_name).is_a?(Horza::Entities::Collection)).to be true
228
+ # end
229
+ # end
230
+ #
231
+ # context 'field in parameters' do
232
+ # it 'gets the records based on parameters' do
233
+ # result = Get::UsersBy.run(last_name: last_name)
234
+ # expect(result.length).to eq match_count
235
+ # end
236
+ #
237
+ # it 'returns a dynamically generated response entity' do
238
+ # expect(Get::UsersBy.run(last_name: last_name).is_a?(Horza::Entities::Collection)).to be true
239
+ # end
240
+ # end
241
+ # end
242
+ #
243
+ # context 'when no records exist' do
244
+ # it 'returns empty collection' do
245
+ # expect(Get::UsersBy.run(last_name: last_name).empty?).to be true
246
+ # end
247
+ # end
248
+ # end
249
+ #
250
+ # context 'ancestry' do
251
+ # context 'direct relation' do
252
+ # let(:employer) { GetSpec::Employer.create }
253
+ # let!(:user1) { GetSpec::User.create(employer: employer) }
254
+ # let!(:user2) { GetSpec::User.create(employer: employer) }
255
+ #
256
+ # context 'ParentFromChild' do
257
+ # it 'returns parent' do
258
+ # expect(Get::EmployerFromUser.run(user1).to_h).to eq employer.attributes
259
+ # end
260
+ # end
261
+ #
262
+ # context 'ChildrenFromParent' do
263
+ # it 'returns children' do
264
+ # result = Get::UsersFromEmployer.run(employer)
265
+ # expect(result.first.to_h).to eq user1.attributes
266
+ # expect(result.last.to_h).to eq user2.attributes
267
+ # end
268
+ # end
269
+ #
270
+ # context 'invalid ancestry' do
271
+ # it 'throws error' do
272
+ # expect { Get::UserFromEmployer.run(employer) }.to raise_error Get::Errors::InvalidAncestry
273
+ # end
274
+ # end
275
+ #
276
+ # context 'valid ancestry with no saved childred' do
277
+ # let(:employer2) { GetSpec::Employer.create }
278
+ # it 'returns empty collection error' do
279
+ # expect(Get::UsersFromEmployer.run(employer2).empty?).to be true
280
+ # end
281
+ # end
282
+ #
283
+ # context 'valid ancestry with no saved parent' do
284
+ # let(:user2) { GetSpec::User.create }
285
+ # it 'returns nil' do
286
+ # expect(Get::EmployerFromUser.run(user2)).to be nil
287
+ # end
288
+ # end
289
+ # end
290
+ #
291
+ # context 'using via' do
292
+ # let(:employer) { GetSpec::Employer.create }
293
+ # let(:user) { GetSpec::User.create(employer: employer) }
294
+ # let(:sportscar) { GetSpec::SportsCar.create(employer: employer) }
295
+ #
296
+ # before do
297
+ # employer.sports_cars << sportscar
298
+ # end
299
+ #
300
+ # it 'returns the correct ancestor (single via symbol)' do
301
+ # result = Get::SportsCarsFromUser.run(user, via: :employer)
302
+ # expect(result.first.to_h).to eq sportscar.attributes
303
+ # end
304
+ #
305
+ # it 'returns the correct ancestor (array of via symbols)' do
306
+ # result = Get::SportsCarsFromUser.run(user, via: [:employer])
307
+ # expect(result.first.to_h).to eq sportscar.attributes
308
+ # end
309
+ # end
310
+ # end
311
+ # end
312
+ # end
313
+ #
314
+ # describe Get::Builders::AncestryBuilder do
315
+ # let(:name) { 'UserFromEmployer' }
316
+ #
317
+ # before { Get.configure { |config| config.set_adapter(:active_record) } }
318
+ # after { Get.reset }
319
+ #
320
+ # subject { Get::Builders::AncestryBuilder.new(name) }
321
+ #
322
+ # describe '#class' do
323
+ # it 'builds a class that inherits from Get::Db' do
324
+ # expect(subject.class.superclass).to eq Get::Db
325
+ # end
326
+ #
327
+ # it 'correctly assigns class-level variables' do
328
+ # [:entity, :query_key, :collection, :store, :result_key].each do |class_var|
329
+ # expect(subject.class.respond_to? class_var).to be true
330
+ # end
331
+ # end
332
+ # end
333
+ # end
334
+ #
335
+ # describe Get::Builders::QueryBuilder do
336
+ # let(:name) { 'UserFromEmployer' }
337
+ #
338
+ # before { Get.configure { |config| config.set_adapter(:active_record) } }
339
+ # after { Get.reset }
340
+ #
341
+ # subject { Get::Builders::QueryBuilder.new(name) }
342
+ #
343
+ # describe '#class' do
344
+ # it 'builds a class that inherits from Get::Db' do
345
+ # expect(subject.class.superclass).to eq Get::Db
346
+ # end
347
+ #
348
+ # it 'correctly assigns class-level variables' do
349
+ # [:entity, :query_key, :collection, :store, :field].each do |class_var|
350
+ # expect(subject.class.respond_to? class_var).to be true
351
+ # end
352
+ # end
353
+ # end
354
+ # end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe Tzu::Validation do
4
+
5
+ context 'params define valid? method' do
6
+ subject do
7
+ Class.new do
8
+ include Tzu
9
+ include Tzu::Validation
10
+ end
11
+ end
12
+
13
+ let(:errors) { [1, 2, 3] }
14
+ let(:params) { spy(errors: errors, valid?: false) }
15
+
16
+ it 'valid? method is called' do
17
+ subject.run(params)
18
+ expect(params).to have_received(:valid?)
19
+ end
20
+
21
+ it 'returns validation result' do
22
+ result = subject.run(params)
23
+ expect(result).to have_attributes(success: false, type: :validation, result: errors)
24
+ end
25
+ end
26
+
27
+ context 'command defines valid? method' do
28
+ subject do
29
+ Class.new do
30
+ include Tzu
31
+ include Tzu::Validation
32
+
33
+ def valid?(params)
34
+ Tzu::ValidationResult.new(false, [])
35
+ end
36
+ end
37
+ end
38
+
39
+ it 'returns validation result' do
40
+ result = subject.run(nil)
41
+ expect(result).to have_attributes(success: false, type: :validation)
42
+ end
43
+ end
44
+ end
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tzu
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Morgan Bruce
8
+ - Blake Turner
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-05-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 1.0.0
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: 1.0.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: activerecord
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 3.2.15
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 3.2.15
42
+ - !ruby/object:Gem::Dependency
43
+ name: activesupport
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: 3.2.15
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: 3.2.15
56
+ - !ruby/object:Gem::Dependency
57
+ name: rspec
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 2.4.0
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 2.4.0
70
+ - !ruby/object:Gem::Dependency
71
+ name: sqlite3
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: byebug
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ description: Tzu is a library for issuing commands in Ruby
99
+ email: morgan@onfido.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - LICENSE.txt
105
+ - README.md
106
+ - lib/tzu.rb
107
+ - lib/tzu/failure.rb
108
+ - lib/tzu/hooks.rb
109
+ - lib/tzu/invalid.rb
110
+ - lib/tzu/match.rb
111
+ - lib/tzu/organizer.rb
112
+ - lib/tzu/outcome.rb
113
+ - lib/tzu/validation.rb
114
+ - lib/tzu/validation_result.rb
115
+ - spec/command_spec.rb
116
+ - spec/hooks_spec.rb
117
+ - spec/organizer_spec.rb
118
+ - spec/outcome_spec.rb
119
+ - spec/spec_helper.rb
120
+ - spec/tzu_spec.rb
121
+ - spec/validation_spec.rb
122
+ homepage: https://github.com/onfido/tzu
123
+ licenses:
124
+ - MIT
125
+ metadata: {}
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubyforge_project:
142
+ rubygems_version: 2.2.2
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: Standardise and encapsulate your application's actions
146
+ test_files:
147
+ - spec/command_spec.rb
148
+ - spec/hooks_spec.rb
149
+ - spec/organizer_spec.rb
150
+ - spec/outcome_spec.rb
151
+ - spec/spec_helper.rb
152
+ - spec/tzu_spec.rb
153
+ - spec/validation_spec.rb
154
+ has_rdoc: