domain_driven 0.0.3 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -0
- data/domain_driven.gemspec +7 -0
- data/lib/domain_driven.rb +8 -1
- data/lib/domain_driven/block_active_record.rb +42 -0
- data/lib/domain_driven/criteria.rb +81 -0
- data/lib/domain_driven/entity.rb +61 -0
- data/lib/domain_driven/errors.rb +5 -0
- data/lib/domain_driven/model.rb +45 -0
- data/lib/domain_driven/repository.rb +11 -0
- data/lib/domain_driven/service.rb +39 -0
- data/lib/domain_driven/version.rb +1 -1
- data/spec/domain_driven/criteria_spec.rb +84 -0
- data/spec/spec_helper.rb +10 -0
- metadata +99 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 251c9fc04d7f0f75d31e9297e0955f0f4283fcd7
|
4
|
+
data.tar.gz: aade59de5dc666d44fb172661e058e647c66eddb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 42d95a7056e73495ecca772bdedc44e6099730cdd96afc13a4d0a6f6d6444f8ccc4f70005a79f5cc2401bfcb82020564cd15f676e0717c5af2e97e0cafc58cc2
|
7
|
+
data.tar.gz: 4f4a720942aeb869d88a806575dad97e50a3a2967947f2ee10c318fed3a03cbac7cb95c50a58b03dd67c6cca4f6880826b63f2afe255e5017683a0c03684ae2f
|
data/README.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/domain_driven.png)](http://badge.fury.io/rb/domain_driven)
|
2
|
+
|
3
|
+
# Gem Status
|
4
|
+
As of 4 March 2014, Incomplete Extraction.
|
5
|
+
This gem is in the process of being extracted from a reference architecture RubyOnRails application.
|
6
|
+
|
1
7
|
# DomainDriven
|
2
8
|
|
3
9
|
This gem and set of techniques allow you to use *DDD* to controll complexity and dependencies as your RubyOnRails application grows. The additional benefit is that since core domain logic is decoupled from the Rails framework and database, automated tests of the core domain logic can run very fast.
|
@@ -58,3 +64,4 @@ For providing the concept of Domain Driven Design
|
|
58
64
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
59
65
|
4. Push to the branch (`git push origin my-new-feature`)
|
60
66
|
5. Create new Pull Request
|
67
|
+
|
data/domain_driven.gemspec
CHANGED
@@ -20,4 +20,11 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.5"
|
22
22
|
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rails", '~> 4.0.2'
|
24
|
+
spec.add_development_dependency "rspec"
|
25
|
+
spec.add_development_dependency 'rspec-given'
|
26
|
+
spec.add_runtime_dependency 'interactor'
|
27
|
+
spec.add_runtime_dependency 'pundit'
|
28
|
+
spec.add_runtime_dependency 'activerecord'
|
29
|
+
|
23
30
|
end
|
data/lib/domain_driven.rb
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
require "domain_driven/version"
|
2
|
+
require "domain_driven/errors"
|
3
|
+
require "domain_driven/service"
|
4
|
+
require "domain_driven/repository"
|
5
|
+
require "domain_driven/block_active_record"
|
6
|
+
require "domain_driven/model"
|
7
|
+
require "domain_driven/entity"
|
8
|
+
require "domain_driven/criteria"
|
2
9
|
|
3
10
|
module DomainDriven
|
4
|
-
|
11
|
+
|
5
12
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Thanks to Jim Weirich for the concepts in this class.
|
2
|
+
# Extracted from https://github.com/jimweirich/wyriki
|
3
|
+
# domain_driven would not be possible without this critical part.
|
4
|
+
|
5
|
+
module DomainDriven
|
6
|
+
|
7
|
+
ActiveRecordNotAvailableError = Class.new(StandardError)
|
8
|
+
|
9
|
+
# Include this module to block ActiveRecord save/update_attributes
|
10
|
+
# calls on the object.
|
11
|
+
module BlockActiveRecord
|
12
|
+
def save
|
13
|
+
no_db_methods
|
14
|
+
end
|
15
|
+
|
16
|
+
def save!
|
17
|
+
no_db_methods
|
18
|
+
end
|
19
|
+
|
20
|
+
def update_attribute(*args)
|
21
|
+
no_db_methods
|
22
|
+
end
|
23
|
+
|
24
|
+
def update_attribute!(*args)
|
25
|
+
no_db_methods
|
26
|
+
end
|
27
|
+
|
28
|
+
def update_attributes(*args)
|
29
|
+
no_db_methods
|
30
|
+
end
|
31
|
+
|
32
|
+
def update_attributes!(*args)
|
33
|
+
no_db_methods
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def no_db_methods
|
39
|
+
fail ActiveRecordNotAvailableError, "No DB methods on Business Objects"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module DomainDriven
|
2
|
+
|
3
|
+
class Message
|
4
|
+
def initialize(name, args=nil)
|
5
|
+
@name, @args = name, args
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
"#{@name}(#{@args})"
|
10
|
+
end
|
11
|
+
|
12
|
+
def sendable
|
13
|
+
if @args
|
14
|
+
[@name.to_sym, @args]
|
15
|
+
else
|
16
|
+
@name.to_sym
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Criteria
|
22
|
+
|
23
|
+
def initialize()
|
24
|
+
@chain = []
|
25
|
+
end
|
26
|
+
|
27
|
+
def shift(criteria)
|
28
|
+
criteria.chain.each do |e|
|
29
|
+
add_message(e)
|
30
|
+
end
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def unshift(criteria)
|
35
|
+
criteria.shift(self)
|
36
|
+
criteria
|
37
|
+
end
|
38
|
+
|
39
|
+
# sugar
|
40
|
+
def add(name, *args)
|
41
|
+
add_message(DomainDriven::Message.new(name, *args))
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
return '' if @chain.empty?
|
46
|
+
"target." + @chain.map(&:to_s).join('.')
|
47
|
+
end
|
48
|
+
|
49
|
+
def send_to(target)
|
50
|
+
@chain.each do |e|
|
51
|
+
target = target.send(*(e.sendable))
|
52
|
+
end
|
53
|
+
target
|
54
|
+
end
|
55
|
+
|
56
|
+
def method_missing(sym, *args, &block)
|
57
|
+
self.add(sym.to_sym, *args)
|
58
|
+
end
|
59
|
+
|
60
|
+
# override Kernal.load
|
61
|
+
def load
|
62
|
+
self.add(:load)
|
63
|
+
end
|
64
|
+
|
65
|
+
def find(*)
|
66
|
+
raise "not supported"
|
67
|
+
end
|
68
|
+
|
69
|
+
def chain
|
70
|
+
@chain
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def add_message(message)
|
76
|
+
@chain << message
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Thanks to Jim Weirich for the concepts in this class.
|
2
|
+
# Extracted from https://github.com/jimweirich/wyriki
|
3
|
+
# domain_driven would not be possible without this critical part.
|
4
|
+
|
5
|
+
require 'delegate'
|
6
|
+
|
7
|
+
module DomainDriven
|
8
|
+
class Entity < SimpleDelegator
|
9
|
+
include BlockActiveRecord
|
10
|
+
|
11
|
+
def _data
|
12
|
+
datum = self
|
13
|
+
while datum.entity?
|
14
|
+
datum = datum.__getobj__
|
15
|
+
end
|
16
|
+
datum
|
17
|
+
end
|
18
|
+
|
19
|
+
def ==(other)
|
20
|
+
if other.respond_to?(:_data)
|
21
|
+
_data == other._data
|
22
|
+
else
|
23
|
+
_data == other
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def entity?
|
28
|
+
true
|
29
|
+
end
|
30
|
+
|
31
|
+
def class
|
32
|
+
_data.class
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.wrap(entity)
|
36
|
+
entity ? new(entity) : nil
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.wraps(entities)
|
40
|
+
return nil unless entities
|
41
|
+
wrap(entities.extend Model).extend Collection
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
module Collection
|
47
|
+
def each
|
48
|
+
_data.each do |_item|
|
49
|
+
yield wrap(_item)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
def include?(other)
|
53
|
+
if other.respond_to?(:_data)
|
54
|
+
_data.include?(other._data)
|
55
|
+
else
|
56
|
+
_data.include?(other)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# Thanks to Jim Weirich for the concepts in this class.
|
2
|
+
# Extracted from https://github.com/jimweirich/wyriki
|
3
|
+
# domain_driven would not be possible without this critical part.
|
4
|
+
|
5
|
+
module DomainDriven
|
6
|
+
|
7
|
+
# Include in ActiveRecord models to mimic Entity models.
|
8
|
+
module Model
|
9
|
+
def _data
|
10
|
+
self
|
11
|
+
end
|
12
|
+
|
13
|
+
def entity?
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
def ==(other)
|
18
|
+
if other.respond_to?(:entity?) && other.entity?
|
19
|
+
self == other._data
|
20
|
+
else
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module ClassMethods
|
26
|
+
|
27
|
+
def find(*)
|
28
|
+
super
|
29
|
+
rescue ActiveRecord::RecordNotFound => error
|
30
|
+
raise DomainDriven::RecordNotFound.new(error)
|
31
|
+
rescue => error
|
32
|
+
raise DomainDriven::Error.new(error)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def self.included(klass)
|
39
|
+
super
|
40
|
+
klass.extend ClassMethods
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "interactor"
|
2
|
+
require "pundit"
|
3
|
+
|
4
|
+
module DomainDriven
|
5
|
+
class Service
|
6
|
+
include Interactor
|
7
|
+
include Pundit
|
8
|
+
|
9
|
+
def perform
|
10
|
+
perform_main
|
11
|
+
rescue DomainDriven::RecordNotFound
|
12
|
+
raise
|
13
|
+
rescue Pundit::NotAuthorizedError
|
14
|
+
raise
|
15
|
+
# rescue => x
|
16
|
+
# perform_rescue(x)
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
def current_user
|
21
|
+
context[:current_member]
|
22
|
+
end
|
23
|
+
|
24
|
+
def request
|
25
|
+
context[:request]
|
26
|
+
end
|
27
|
+
|
28
|
+
def perform_main
|
29
|
+
raise "subclass responsibility for #{self.class} "
|
30
|
+
end
|
31
|
+
|
32
|
+
def perform_rescue(x)
|
33
|
+
context[:errors] = x.message
|
34
|
+
context.fail!
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Criteria' do
|
4
|
+
|
5
|
+
Given! (:criteria) { DomainDriven::Criteria.new }
|
6
|
+
|
7
|
+
context "combining two criteria" do
|
8
|
+
Given! (:criteria1) { criteria }
|
9
|
+
Given! (:criteria2) { DomainDriven::Criteria.new }
|
10
|
+
Given { criteria1.where("id=5") }
|
11
|
+
Given { criteria2.page("3") }
|
12
|
+
|
13
|
+
context "shift" do
|
14
|
+
When(:result) { criteria1.shift(criteria2) }
|
15
|
+
Then { result.to_s.should eq("target.where(id=5).page(3)") }
|
16
|
+
end
|
17
|
+
|
18
|
+
context "unshift" do
|
19
|
+
When(:result) { criteria2.unshift(criteria1) }
|
20
|
+
Then { result.to_s.should eq("target.where(id=5).page(3)") }
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
context "empty criteria has no effect" do
|
26
|
+
Then { criteria.to_s.should eq("")}
|
27
|
+
|
28
|
+
context "send_to" do
|
29
|
+
Given(:target) { 'hello' }
|
30
|
+
When(:result) { criteria.send_to(target) }
|
31
|
+
Then { result.should eq('hello') }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "send #where" do
|
36
|
+
Given(:email) { 'todd@example.com' }
|
37
|
+
When { criteria.where("email='#{email}'") }
|
38
|
+
Then { criteria.to_s.should eq("target.where(email='#{email}')") }
|
39
|
+
end
|
40
|
+
|
41
|
+
context "send #load" do
|
42
|
+
When(:result) { criteria.load }
|
43
|
+
Then { criteria.to_s.should eq("target.load()") }
|
44
|
+
# Then { expect(result).to have_failed(ArgumentError, /wrong number of arguments/) }
|
45
|
+
end
|
46
|
+
|
47
|
+
context "send #reverse " do
|
48
|
+
When { criteria.reverse }
|
49
|
+
Then { criteria.to_s.should eq("target.reverse()")}
|
50
|
+
|
51
|
+
context "send_to" do
|
52
|
+
Given(:target) { '123456' }
|
53
|
+
When(:result) { criteria.send_to(target) }
|
54
|
+
Then { result.should eq('654321') }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "send #chomp with arugment " do
|
59
|
+
Whereas { criteria.chomp("llo") }
|
60
|
+
Then { criteria.to_s.should eq("target.chomp(llo)") }
|
61
|
+
|
62
|
+
context "send_to" do
|
63
|
+
Given(:target) { 'hello' }
|
64
|
+
Whereas(:result) { criteria.send_to(target) }
|
65
|
+
Then { result.should eq('he') }
|
66
|
+
|
67
|
+
context "send a second message" do
|
68
|
+
Given { criteria.add('+', " is great" ) }
|
69
|
+
Then { criteria.to_s.should eq("target.chomp(llo).+( is great)")}
|
70
|
+
Whereas(:result) { criteria.send_to(target) }
|
71
|
+
Then { result.should eq('he is great') }
|
72
|
+
end
|
73
|
+
|
74
|
+
context "send a second message with syntactic sugar" do
|
75
|
+
Given { criteria + " is great" }
|
76
|
+
Then { criteria.to_s.should eq("target.chomp(llo).+( is great)")}
|
77
|
+
Whereas(:result) { criteria.send_to(target) }
|
78
|
+
Then { result.should eq('he is great') }
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: domain_driven
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark Windholtz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-03-
|
11
|
+
date: 2014-03-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -38,6 +38,90 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 4.0.2
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 4.0.2
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec-given
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: interactor
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pundit
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: activerecord
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
41
125
|
description: Domain Driven provides Rails abstract classes and generators to support
|
42
126
|
for a Domain Driven Design
|
43
127
|
email:
|
@@ -53,7 +137,16 @@ files:
|
|
53
137
|
- Rakefile
|
54
138
|
- domain_driven.gemspec
|
55
139
|
- lib/domain_driven.rb
|
140
|
+
- lib/domain_driven/block_active_record.rb
|
141
|
+
- lib/domain_driven/criteria.rb
|
142
|
+
- lib/domain_driven/entity.rb
|
143
|
+
- lib/domain_driven/errors.rb
|
144
|
+
- lib/domain_driven/model.rb
|
145
|
+
- lib/domain_driven/repository.rb
|
146
|
+
- lib/domain_driven/service.rb
|
56
147
|
- lib/domain_driven/version.rb
|
148
|
+
- spec/domain_driven/criteria_spec.rb
|
149
|
+
- spec/spec_helper.rb
|
57
150
|
homepage: https://github.com/mwindholtz/domain_driven
|
58
151
|
licenses:
|
59
152
|
- MIT
|
@@ -74,8 +167,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
74
167
|
version: '0'
|
75
168
|
requirements: []
|
76
169
|
rubyforge_project:
|
77
|
-
rubygems_version: 2.
|
170
|
+
rubygems_version: 2.2.2
|
78
171
|
signing_key:
|
79
172
|
specification_version: 4
|
80
173
|
summary: Domain Driven provides Rails hooks for a Domain Driven Design
|
81
|
-
test_files:
|
174
|
+
test_files:
|
175
|
+
- spec/domain_driven/criteria_spec.rb
|
176
|
+
- spec/spec_helper.rb
|