tzu 0.0.1.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.
@@ -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: