tradesman 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +20 -0
- data/README.md +85 -0
- data/lib/tradesman/builders/base.rb +24 -0
- data/lib/tradesman/builders/create.rb +19 -0
- data/lib/tradesman/builders/create_for_parent.rb +34 -0
- data/lib/tradesman/builders/delete.rb +19 -0
- data/lib/tradesman/builders/update.rb +19 -0
- data/lib/tradesman/builders.rb +20 -0
- data/lib/tradesman/configuration.rb +30 -0
- data/lib/tradesman/errors.rb +12 -0
- data/lib/tradesman/parser.rb +41 -0
- data/lib/tradesman/run_methods.rb +11 -0
- data/lib/tradesman/template.rb +26 -0
- data/lib/tradesman.rb +44 -0
- data/spec/builders_spec.rb +29 -0
- data/spec/parser_spec.rb +74 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/tradesman_spec.rb +152 -0
- metadata +180 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c3709e7f3b27cda92aaf6dc131eaf6aa1b3de869
|
4
|
+
data.tar.gz: 9862de5df502cccce9682806e1710df738f4bde1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c1219785e7c94d998ba219a11764daf8df4d76d1073e43ba0a73ee9f31b40df982f90e15d602ebdb53f7ac7fbdd805331207765074549fdde8c9e0817d0f5da2
|
7
|
+
data.tar.gz: 56650c880247e12f10b6638bfab459bbddd5cbcc1c5c6d533235221175d7aa27c1ee2059f185d49dbe4bcddae3b1b26cc7aa9ab6a6bea1514a653cb63e0b56fb
|
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# Tradesman
|
2
|
+
|
3
|
+
Encapsulate common application behaviour with dynamically generated classes.
|
4
|
+
|
5
|
+
Tradesman dynamically generates classes with human-readble names that handle the pass, fail, and invalid results of common create, update, and delete actions.
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
# Simple - Returns an Outcome
|
11
|
+
outcome = Tradesman::CreateUser.run(user_params)
|
12
|
+
outcome.success? #=> true
|
13
|
+
outcome.result #=> User Entity
|
14
|
+
|
15
|
+
# Passing a block - Well-suited for Controllers
|
16
|
+
Tradesman::UpdateUser.run(params[:id], user_update_params) do
|
17
|
+
success do |result|
|
18
|
+
render(text: 'true', status: 200)
|
19
|
+
end
|
20
|
+
|
21
|
+
invalid do |error|
|
22
|
+
render(text: 'false', status: 404)
|
23
|
+
end
|
24
|
+
|
25
|
+
failure do |result|
|
26
|
+
render(text: 'false', status: 400)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Can also Delete
|
31
|
+
Tradesman::DeleteUser.run(params[:id])
|
32
|
+
|
33
|
+
# Or Create as a child of an existing record
|
34
|
+
Tradesman::CreateUserForEmployer.run(employer_id, user_params)
|
35
|
+
```
|
36
|
+
|
37
|
+
## Why is this necessary?
|
38
|
+
|
39
|
+
At Onfido, we observed that many Create, Update and Delete actions we programmed were are simple and repeated (albeit with different records and parameter lists) in several locations. They can generally be broken in to the following steps:
|
40
|
+
|
41
|
+
- Query existing record by some group of parameters, but generally just by :id (Update and Delete only)
|
42
|
+
- Return 404 if record does not exist
|
43
|
+
- Update or Create a new record with a given set of parameters. For Deletion, no parameters are required.
|
44
|
+
- Return a success, invalid, or failure message based on the result of this persistence action.
|
45
|
+
|
46
|
+
Example:
|
47
|
+
```ruby
|
48
|
+
# users_controller.rb
|
49
|
+
def update
|
50
|
+
@user = User.find(params[:id])
|
51
|
+
return render(text: 'false', status: 404) unless @user
|
52
|
+
|
53
|
+
@user.assign_attributes(user_params)
|
54
|
+
return render(text: 'false', status: 400) unless @user.save
|
55
|
+
|
56
|
+
render 'user'
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def user_params
|
62
|
+
params.permit(:first_name, :last_name, :email)
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
Yes, the above example is trivial, but many such trivial actions are necessary in web applications.
|
67
|
+
Tradesman is designed to handle the above and a few other common use-cases to reduce such tedious, often-repeated boilerplate code.
|
68
|
+
|
69
|
+
Tradesman version of the above:
|
70
|
+
```ruby
|
71
|
+
Tradesman::UpdateUser.run(params[:id], user_params) do
|
72
|
+
success do |result|
|
73
|
+
@user = result
|
74
|
+
render 'user'
|
75
|
+
end
|
76
|
+
|
77
|
+
invalid do |error|
|
78
|
+
render(text: 'false', status: 404)
|
79
|
+
end
|
80
|
+
|
81
|
+
failure { |result| render(text: 'false', status: 400) } # If you prefer one-liners
|
82
|
+
end
|
83
|
+
```
|
84
|
+
|
85
|
+
The Tradesman version says exactly what it does, is cruft free, and is much quicker to test (more on that later).
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Tradesman
|
2
|
+
module Builders
|
3
|
+
class Base
|
4
|
+
def initialize(class_name)
|
5
|
+
parser = ::Tradesman::Parser.new(class_name)
|
6
|
+
@subject = parser.subject
|
7
|
+
@parent = parser.parent
|
8
|
+
end
|
9
|
+
|
10
|
+
def class
|
11
|
+
template_class(class_args)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def class_args
|
17
|
+
{
|
18
|
+
subject: @subject,
|
19
|
+
parent: @parent
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Tradesman
|
2
|
+
module Builders
|
3
|
+
class Create < Base
|
4
|
+
private
|
5
|
+
|
6
|
+
def template_class(args)
|
7
|
+
Class.new(::Tradesman::Template) do
|
8
|
+
@store = Tradesman.adapter.context_for_entity(args[:subject])
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def execute(params)
|
13
|
+
self.class.adapter.create!(params)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Tradesman
|
2
|
+
module Builders
|
3
|
+
class CreateForParent < Base
|
4
|
+
private
|
5
|
+
|
6
|
+
def template_class(args)
|
7
|
+
Class.new(::Tradesman::Template) do
|
8
|
+
@store = Tradesman.adapter.context_for_entity(args[:subject])
|
9
|
+
@parent_store = Tradesman.adapter.context_for_entity(args[:parent])
|
10
|
+
@parent_key = args[:parent]
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_reader :parent_store, :parent_key
|
14
|
+
|
15
|
+
def parent_adapter
|
16
|
+
Tradesman.adapter.new(parent_store)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def execute(params)
|
23
|
+
parent = self.class.parent_adapter.get!(params[:parent_id])
|
24
|
+
self.class.adapter.create!(params.except(:parent_id).merge(relation_id => parent.id))
|
25
|
+
end
|
26
|
+
|
27
|
+
def relation_id
|
28
|
+
"#{self.class.parent_key}_id"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Tradesman
|
2
|
+
module Builders
|
3
|
+
class Delete < Base
|
4
|
+
private
|
5
|
+
|
6
|
+
def template_class(args)
|
7
|
+
Class.new(::Tradesman::Template) do
|
8
|
+
@store = Tradesman.adapter.context_for_entity(args[:subject])
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def execute(params)
|
13
|
+
self.class.adapter.delete!(params[:id])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Tradesman
|
2
|
+
module Builders
|
3
|
+
class Update < Base
|
4
|
+
private
|
5
|
+
|
6
|
+
def template_class(args)
|
7
|
+
Class.new(::Tradesman::Template) do
|
8
|
+
@store = Tradesman.adapter.context_for_entity(args[:subject])
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def execute(params)
|
13
|
+
self.class.adapter.update!(params[:id], params.except(:id))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Tradesman
|
2
|
+
module Builders
|
3
|
+
class << self
|
4
|
+
def generate_class(parser)
|
5
|
+
Tradesman.const_set(parser.class_name, builder_for_action(parser.action, parser.parent).new(parser.class_name).class)
|
6
|
+
end
|
7
|
+
|
8
|
+
def builder_for_action(action, parent)
|
9
|
+
case action
|
10
|
+
when :create
|
11
|
+
parent ? CreateForParent : Create
|
12
|
+
when :update
|
13
|
+
Update
|
14
|
+
when :delete
|
15
|
+
Delete
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Tradesman
|
2
|
+
module Configuration
|
3
|
+
def configuration
|
4
|
+
@configuration ||= Config.new
|
5
|
+
end
|
6
|
+
|
7
|
+
def reset
|
8
|
+
@configuration = Config.new
|
9
|
+
@adapter, @adapter_map = nil, nil # Class-level cache clear
|
10
|
+
end
|
11
|
+
|
12
|
+
def configure
|
13
|
+
yield(configuration)
|
14
|
+
end
|
15
|
+
|
16
|
+
def adapter
|
17
|
+
raise ::Tradesman::Errors::Base.new('Adapter has not been configured') unless configuration.adapter
|
18
|
+
@adapter ||= configuration.adapter
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Config
|
23
|
+
attr_accessor :adapter
|
24
|
+
|
25
|
+
def set_adapter(adapter)
|
26
|
+
Horza.configure { |config| config.adapter = adapter }
|
27
|
+
@adapter = Horza.adapter
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Tradesman
|
2
|
+
class Parser
|
3
|
+
attr_reader :class_name, :action_string, :subject_string, :parent_string
|
4
|
+
|
5
|
+
PARSE_REGEX = /(Create|Update|Delete)(.+)/
|
6
|
+
PARSE_REGEX_WITH_PARENT = /(Create|Update|Delete)(.+)4(.+)/
|
7
|
+
|
8
|
+
def initialize(class_name)
|
9
|
+
@class_name = class_name
|
10
|
+
@match = class_name.to_s.match(regex)
|
11
|
+
@action_string, @subject_string, @parent_string = @match.values_at(1, 2, 3) if @match
|
12
|
+
end
|
13
|
+
|
14
|
+
def match?
|
15
|
+
!!@match
|
16
|
+
end
|
17
|
+
|
18
|
+
def action
|
19
|
+
str_to_sym(@action_string)
|
20
|
+
end
|
21
|
+
|
22
|
+
def subject
|
23
|
+
str_to_sym(@subject_string)
|
24
|
+
end
|
25
|
+
|
26
|
+
def parent
|
27
|
+
return nil unless @parent_string
|
28
|
+
str_to_sym(@parent_string)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def regex
|
34
|
+
/.+4.+/.match(@class_name) ? PARSE_REGEX_WITH_PARENT : PARSE_REGEX
|
35
|
+
end
|
36
|
+
|
37
|
+
def str_to_sym(str)
|
38
|
+
str.underscore.symbolize
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Tradesman
|
2
|
+
class Template
|
3
|
+
include ::Tzu
|
4
|
+
include ::Tzu::Validation
|
5
|
+
|
6
|
+
class << self
|
7
|
+
attr_reader :store
|
8
|
+
|
9
|
+
def adapter
|
10
|
+
Tradesman.adapter.new(store)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(params)
|
15
|
+
execute(params)
|
16
|
+
rescue Horza::Errors::RecordInvalid, Horza::Errors::RecordNotFound => e
|
17
|
+
invalid! e
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def execute(params)
|
23
|
+
raise Tradesman::Errors::MethodNotImplemented.new
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/tradesman.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'horza'
|
2
|
+
require 'tzu'
|
3
|
+
require 'tradesman/builders'
|
4
|
+
require 'tradesman/builders/base'
|
5
|
+
require 'tradesman/builders/create'
|
6
|
+
require 'tradesman/builders/create_for_parent'
|
7
|
+
require 'tradesman/builders/delete'
|
8
|
+
require 'tradesman/builders/update'
|
9
|
+
require 'tradesman/configuration'
|
10
|
+
require 'tradesman/errors'
|
11
|
+
require 'tradesman/parser'
|
12
|
+
require 'tradesman/run_methods'
|
13
|
+
require 'tradesman/template'
|
14
|
+
|
15
|
+
module Tradesman
|
16
|
+
extend Tradesman::Configuration
|
17
|
+
|
18
|
+
class << self
|
19
|
+
attr_writer :configuration
|
20
|
+
|
21
|
+
def included(base)
|
22
|
+
base.class_eval do
|
23
|
+
extend ::Tradesman::RunMethods
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def const_missing(class_name)
|
28
|
+
parser = ::Tradesman::Parser.new(class_name)
|
29
|
+
return super(class_name) unless parser.match?
|
30
|
+
Builders.generate_class(parser)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def run
|
35
|
+
run!
|
36
|
+
rescue ::Tradesman::Errors::Base
|
37
|
+
end
|
38
|
+
|
39
|
+
def run!
|
40
|
+
call
|
41
|
+
rescue *Tradesman.adapter.expected_errors => e
|
42
|
+
raise ::Tradesman::Errors::Base.new(e.message)
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Tradesman::Builders do
|
4
|
+
context '#builder_for_action' do
|
5
|
+
context 'Create' do
|
6
|
+
it 'returns Create class' do
|
7
|
+
expect(Tradesman::Builders.builder_for_action(:create, nil)).to eq Tradesman::Builders::Create
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'Create with Parent' do
|
12
|
+
it 'returns Create class' do
|
13
|
+
expect(Tradesman::Builders.builder_for_action(:create, :user)).to eq Tradesman::Builders::CreateForParent
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'Update' do
|
18
|
+
it 'returns Create class' do
|
19
|
+
expect(Tradesman::Builders.builder_for_action(:update, nil)).to eq Tradesman::Builders::Update
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'Delete' do
|
24
|
+
it 'returns Create class' do
|
25
|
+
expect(Tradesman::Builders.builder_for_action(:delete, nil)).to eq Tradesman::Builders::Delete
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/spec/parser_spec.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Tradesman::Parser do
|
4
|
+
context 'when pattern is valid' do
|
5
|
+
context 'CreateUser' do
|
6
|
+
subject { Tradesman::Parser.new('CreateUser') }
|
7
|
+
|
8
|
+
it 'finds a match' do
|
9
|
+
expect(subject.match?).to be true
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'parses the action' do
|
13
|
+
expect(subject.action).to eq :create
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'parses the subject' do
|
17
|
+
expect(subject.subject).to eq :user
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'CreateUser4Employer' do
|
22
|
+
subject { Tradesman::Parser.new('CreateUser4Employer') }
|
23
|
+
|
24
|
+
it 'finds a match' do
|
25
|
+
expect(subject.match?).to be true
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'parses the action' do
|
29
|
+
expect(subject.action).to eq :create
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'parses the subject' do
|
33
|
+
expect(subject.subject).to eq :user
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'parses the parent' do
|
37
|
+
expect(subject.parent).to eq :employer
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'DeleteCustomerInvoice' do
|
42
|
+
subject { Tradesman::Parser.new('DeleteCustomerInvoice') }
|
43
|
+
|
44
|
+
it 'finds a match' do
|
45
|
+
expect(subject.match?).to be true
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'parses the action' do
|
49
|
+
expect(subject.action).to eq :delete
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'parses the subject' do
|
53
|
+
expect(subject.subject).to eq :customer_invoice
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'UpdateTransaction' do
|
58
|
+
subject { Tradesman::Parser.new('UpdateTransaction') }
|
59
|
+
|
60
|
+
it 'finds a match' do
|
61
|
+
expect(subject.match?).to be true
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'parses the action' do
|
65
|
+
expect(subject.action).to eq :update
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'parses the subject' do
|
69
|
+
expect(subject.subject).to eq :transaction
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,152 @@
|
|
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(:strict_users, force: true) {|t| t.string :first_name; t.string :last_name; t.references :employer; }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module TradesmanSpec
|
17
|
+
class Employer < ActiveRecord::Base
|
18
|
+
has_many :users
|
19
|
+
end
|
20
|
+
|
21
|
+
class User < ActiveRecord::Base
|
22
|
+
belongs_to :employer
|
23
|
+
end
|
24
|
+
|
25
|
+
class StrictUser < ActiveRecord::Base
|
26
|
+
belongs_to :employer
|
27
|
+
|
28
|
+
validates :last_name, presence: true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe Tradesman do
|
34
|
+
let(:adapter) { :active_record }
|
35
|
+
before { Tradesman.configure { |config| config.set_adapter(adapter) } }
|
36
|
+
after do
|
37
|
+
TradesmanSpec::User.delete_all
|
38
|
+
TradesmanSpec::StrictUser.delete_all
|
39
|
+
TradesmanSpec::Employer.delete_all
|
40
|
+
end
|
41
|
+
|
42
|
+
context '#configure' do
|
43
|
+
context 'when the adapter is set' do
|
44
|
+
it 'returns the correct adapter class' do
|
45
|
+
expect(Tradesman.adapter).to eq Horza::Adapters::ActiveRecord
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'when the adapter is not set' do
|
50
|
+
before { Tradesman.reset }
|
51
|
+
after { Tradesman.reset }
|
52
|
+
|
53
|
+
it 'throws error' do
|
54
|
+
expect { Tradesman.adapter }.to raise_error(Tradesman::Errors::Base)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context '#run' do
|
60
|
+
context 'Create' do
|
61
|
+
context 'when parameters are valid' do
|
62
|
+
let(:outcome) { Tradesman::CreateUser.run(last_name: 'Turner') }
|
63
|
+
|
64
|
+
it 'creates a new record' do
|
65
|
+
expect(outcome.success?).to be true
|
66
|
+
expect(outcome.result.id.is_a? Integer).to be true
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'returns Horza Entity' do
|
70
|
+
expect(outcome.result.is_a? Horza::Entities::Single).to be true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when parameters are invalid' do
|
75
|
+
let(:outcome) { Tradesman::CreateStrictUser.run(first_name: 'Turner') }
|
76
|
+
it 'returns an invalid outcome' do
|
77
|
+
expect(outcome.success?).to be false
|
78
|
+
expect(outcome.type).to eq :validation
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'for parent' do
|
83
|
+
let(:employer) { TradesmanSpec::Employer.create }
|
84
|
+
let(:outcome) { Tradesman::CreateUser4Employer.run(parent_id: employer.id, last_name: 'Turner') }
|
85
|
+
|
86
|
+
it 'creates a new record' do
|
87
|
+
expect(outcome.success?).to be true
|
88
|
+
expect(outcome.result.id.is_a? Integer).to be true
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'associates child with parent' do
|
92
|
+
expect(outcome.result.employer_id).to eq employer.id
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'associates parent with child' do
|
96
|
+
outcome
|
97
|
+
expect(employer.users.first.id).to eq outcome.result.id
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'Update' do
|
104
|
+
let(:user) { TradesmanSpec::User.create(last_name: 'Smith') }
|
105
|
+
context 'when parameters are valid' do
|
106
|
+
let(:outcome) { Tradesman::UpdateUser.run(id: user.id, last_name: 'Turner') }
|
107
|
+
|
108
|
+
it 'executes successfully' do
|
109
|
+
expect(outcome.success?).to be true
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'updates record' do
|
113
|
+
expect(outcome.result.last_name).to eq 'Turner'
|
114
|
+
expect(user.reload.last_name).to eq 'Turner'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'when parameters are invalid' do
|
119
|
+
let(:strict_user) { TradesmanSpec::StrictUser.create(last_name: 'Smith') }
|
120
|
+
let(:outcome) { Tradesman::UpdateStrictUser.run(id: strict_user.id, last_name: nil) }
|
121
|
+
|
122
|
+
it 'returns an invalid outcome' do
|
123
|
+
expect(outcome.success?).to be false
|
124
|
+
expect(outcome.type).to eq :validation
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context 'Delete' do
|
130
|
+
context 'when parameters are valid' do
|
131
|
+
let!(:user) { TradesmanSpec::User.create }
|
132
|
+
let(:outcome) { Tradesman::DeleteUser.run(id: user.id) }
|
133
|
+
|
134
|
+
it 'executes successfully' do
|
135
|
+
expect(outcome.success?).to be true
|
136
|
+
expect(outcome.result).to be true
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'deletes record' do
|
140
|
+
expect { outcome }.to change(TradesmanSpec::User, :count).by(-1)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context 'when input parameters are invalid' do
|
145
|
+
let(:outcome) { Tradesman::DeleteUser.run(id: 999) }
|
146
|
+
it 'returns an invalid outcome' do
|
147
|
+
expect(outcome.success?).to be false
|
148
|
+
expect(outcome.type).to eq :validation
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
metadata
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tradesman
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Blake Turner
|
8
|
+
- Morgan Bruce
|
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: horza
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - '='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 0.2.0
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - '='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 0.2.0
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: tzu
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - '='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.0.1.0
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - '='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 0.0.1.0
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: bundler
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 1.0.0
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 1.0.0
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: activerecord
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 3.2.15
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 3.2.15
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: activesupport
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 3.2.15
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 3.2.15
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: rspec
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 2.4.0
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: 2.4.0
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: sqlite3
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: byebug
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description: Encapsulate common application behaviour with dynamically generated classes
|
127
|
+
email: mail@blakewilliamturner.com
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- LICENSE.txt
|
133
|
+
- README.md
|
134
|
+
- lib/tradesman.rb
|
135
|
+
- lib/tradesman/builders.rb
|
136
|
+
- lib/tradesman/builders/base.rb
|
137
|
+
- lib/tradesman/builders/create.rb
|
138
|
+
- lib/tradesman/builders/create_for_parent.rb
|
139
|
+
- lib/tradesman/builders/delete.rb
|
140
|
+
- lib/tradesman/builders/update.rb
|
141
|
+
- lib/tradesman/configuration.rb
|
142
|
+
- lib/tradesman/errors.rb
|
143
|
+
- lib/tradesman/parser.rb
|
144
|
+
- lib/tradesman/run_methods.rb
|
145
|
+
- lib/tradesman/template.rb
|
146
|
+
- spec/builders_spec.rb
|
147
|
+
- spec/parser_spec.rb
|
148
|
+
- spec/spec_helper.rb
|
149
|
+
- spec/tradesman_spec.rb
|
150
|
+
homepage: https://github.com/onfido/tradesman
|
151
|
+
licenses:
|
152
|
+
- MIT
|
153
|
+
metadata: {}
|
154
|
+
post_install_message:
|
155
|
+
rdoc_options: []
|
156
|
+
require_paths:
|
157
|
+
- lib
|
158
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
159
|
+
requirements:
|
160
|
+
- - ">="
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: '0'
|
163
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
164
|
+
requirements:
|
165
|
+
- - ">="
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: '0'
|
168
|
+
requirements: []
|
169
|
+
rubyforge_project:
|
170
|
+
rubygems_version: 2.2.2
|
171
|
+
signing_key:
|
172
|
+
specification_version: 4
|
173
|
+
summary: Tradesman dynamically generates classes with human-readble names that handle
|
174
|
+
the pass, fail, and invalid results of common create, update, and delete actions.
|
175
|
+
test_files:
|
176
|
+
- spec/builders_spec.rb
|
177
|
+
- spec/parser_spec.rb
|
178
|
+
- spec/spec_helper.rb
|
179
|
+
- spec/tradesman_spec.rb
|
180
|
+
has_rdoc:
|