domain_driven 0.0.8.1 → 0.0.8.3
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.
- checksums.yaml +4 -4
- data/domain_driven.gemspec +2 -2
- data/lib/domain_driven/block_active_record.rb +3 -3
- data/lib/domain_driven/criteria.rb +1 -6
- data/lib/domain_driven/entity.rb +12 -4
- data/lib/domain_driven/model.rb +1 -1
- data/lib/domain_driven/repository.rb +22 -9
- data/lib/domain_driven/service.rb +48 -3
- data/lib/domain_driven/version.rb +2 -1
- data/spec/domain_driven/block_active_record_spec.rb +51 -0
- data/spec/domain_driven/criteria_spec.rb +2 -7
- data/spec/domain_driven/entity_spec.rb +59 -0
- data/spec/domain_driven/model_spec.rb +17 -0
- data/spec/domain_driven/repository_spec.rb +8 -0
- data/spec/domain_driven/service_spec.rb +56 -0
- data/spec/spec_helper.rb +35 -0
- metadata +14 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 212330e091078c401fb20be56c0afcbd6ab54743
|
4
|
+
data.tar.gz: 5f897a5e7a5a256d69483407f57585ee1551330f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a5fa9bb7c91cf0bfa70b02801a3d1dad1c26b4101a147a0cfd3006b5aca48d00fc1cf1c20bfbb5c47a5b7b9a733db2489d3bf2fe91482cbd82bdb43c08817b2
|
7
|
+
data.tar.gz: c0d1fe134f69849145a3575c38d03116ee9caec6092cb6dd9a8ba74d3ca50f84646f0c4b586340fb5cbcaece05950d6008dd245022c5fee2b0e0afc11e9dd292
|
data/domain_driven.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = DomainDriven::VERSION
|
9
9
|
spec.authors = ["Mark Windholtz", "Rob Biedenharn" ]
|
10
10
|
spec.email = ["mark@agiledna.com", "Rob@AgileConsultingLLC.com"]
|
11
|
-
spec.summary = %q{Domain Driven
|
12
|
-
spec.description = %q{
|
11
|
+
spec.summary = %q{domain_driven allows Domain Driven Design (DDD) within RubyOnRails }
|
12
|
+
spec.description = %q{domain_driven provides Rails abstract classes and generators to support for a Domain Driven Design}
|
13
13
|
spec.homepage = "https://github.com/mwindholtz/domain_driven"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
@@ -35,8 +35,8 @@ module DomainDriven
|
|
35
35
|
|
36
36
|
private
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
def no_db_methods
|
39
|
+
fail ActiveRecordNotAvailableError, "No DB methods on Business Objects"
|
40
|
+
end
|
41
41
|
end
|
42
42
|
end
|
@@ -24,17 +24,12 @@ module DomainDriven
|
|
24
24
|
@chain = []
|
25
25
|
end
|
26
26
|
|
27
|
-
def
|
27
|
+
def merge(criteria)
|
28
28
|
criteria.chain.each do |e|
|
29
29
|
add_message(e)
|
30
30
|
end
|
31
31
|
self
|
32
32
|
end
|
33
|
-
|
34
|
-
def unshift(criteria)
|
35
|
-
criteria.shift(self)
|
36
|
-
criteria
|
37
|
-
end
|
38
33
|
|
39
34
|
# sugar
|
40
35
|
def add(name, *args)
|
data/lib/domain_driven/entity.rb
CHANGED
@@ -27,18 +27,19 @@ module DomainDriven
|
|
27
27
|
def entity?
|
28
28
|
true
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
|
+
alias :_real_class :class
|
31
32
|
def class
|
32
33
|
_data.class
|
33
34
|
end
|
34
|
-
|
35
|
+
|
35
36
|
def self.wrap(entity)
|
36
37
|
entity ? new(entity) : nil
|
37
38
|
end
|
38
39
|
|
39
40
|
def self.wraps(entities)
|
40
41
|
return nil unless entities
|
41
|
-
wrap(entities.extend Model).extend Collection
|
42
|
+
wrap(entities.extend Model).extend Collection
|
42
43
|
end
|
43
44
|
|
44
45
|
end
|
@@ -46,9 +47,16 @@ module DomainDriven
|
|
46
47
|
module Collection
|
47
48
|
def each
|
48
49
|
_data.each do |_item|
|
49
|
-
yield wrap(_item)
|
50
|
+
yield self._real_class.wrap(_item)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_a
|
55
|
+
_data.inject([]) do |array, _item|
|
56
|
+
array << self._real_class.wrap(_item)
|
50
57
|
end
|
51
58
|
end
|
59
|
+
|
52
60
|
def include?(other)
|
53
61
|
if other.respond_to?(:_data)
|
54
62
|
_data.include?(other._data)
|
data/lib/domain_driven/model.rb
CHANGED
@@ -1,11 +1,24 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module DomainDriven
|
4
|
+
module Repository
|
5
|
+
attr_accessor :logger
|
6
|
+
def to_s
|
7
|
+
"#{@name}"
|
7
8
|
end
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
def initialize(name)
|
15
|
+
@name = name
|
16
|
+
self.logger = Logger.new(STDOUT)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.included(klass)
|
20
|
+
super
|
21
|
+
klass.extend ClassMethods
|
22
|
+
end
|
23
|
+
end
|
11
24
|
end
|
@@ -12,10 +12,46 @@ module DomainDriven
|
|
12
12
|
raise
|
13
13
|
rescue Pundit::NotAuthorizedError
|
14
14
|
raise
|
15
|
-
# rescue => x
|
16
|
-
# perform_rescue(x)
|
17
15
|
end
|
18
16
|
|
17
|
+
module Repo # this is the Repository Factory
|
18
|
+
module_function
|
19
|
+
|
20
|
+
class << self
|
21
|
+
attr_writer :logger, :factory
|
22
|
+
end
|
23
|
+
|
24
|
+
def logger
|
25
|
+
@logger ||= Logger.new(STDOUT)
|
26
|
+
end
|
27
|
+
|
28
|
+
def factory
|
29
|
+
return @factory if @factory
|
30
|
+
raise "Repo has no factory initialized. Add an intializer file with something like this: DomainDriven::Service::Repo.factory = { course: CourseRepository, ...}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def for(name)
|
34
|
+
logger.info("Provisioning Repo #{name}")
|
35
|
+
if repo_class_name = factory[name]
|
36
|
+
repo_class = repo_class_name.to_s.classify.constantize
|
37
|
+
repo = repo_class.new(name)
|
38
|
+
repo
|
39
|
+
else
|
40
|
+
raise "repository not found: (#{name})"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def method_missing(name,*args)
|
46
|
+
if name.to_s[0...4] == 'for_'
|
47
|
+
self.for(name.to_s[4..-1].to_sym)
|
48
|
+
else
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end # module Repo
|
54
|
+
|
19
55
|
protected
|
20
56
|
def current_user
|
21
57
|
context[:current_member]
|
@@ -25,6 +61,10 @@ module DomainDriven
|
|
25
61
|
context[:request]
|
26
62
|
end
|
27
63
|
|
64
|
+
def criteria
|
65
|
+
context[:criteria] || DomainDriven::Criteria.new
|
66
|
+
end
|
67
|
+
|
28
68
|
def perform_main
|
29
69
|
raise "subclass responsibility for #{self.class} "
|
30
70
|
end
|
@@ -34,6 +74,11 @@ module DomainDriven
|
|
34
74
|
context.fail!
|
35
75
|
end
|
36
76
|
|
37
|
-
end
|
77
|
+
end # class Service
|
78
|
+
|
38
79
|
|
80
|
+
class ServiceOrganizer
|
81
|
+
include Interactor::Organizer
|
82
|
+
end #
|
83
|
+
|
39
84
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class EntityUnderTest < DomainDriven::Entity
|
4
|
+
end
|
5
|
+
|
6
|
+
class RaiseEverything
|
7
|
+
def method_missing(name,*args)
|
8
|
+
raise "RaiseEverything should never receieve any message"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'Entity' do
|
13
|
+
|
14
|
+
Given! (:model) { RaiseEverything.new }
|
15
|
+
Given! (:entity) { EntityUnderTest.new(model) }
|
16
|
+
|
17
|
+
context 'attempting to call active_record methods should not delegate to the model: ' do
|
18
|
+
|
19
|
+
context "save" do
|
20
|
+
When(:result) { entity.save }
|
21
|
+
Then { expect(result).to have_failed(DomainDriven::ActiveRecordNotAvailableError) }
|
22
|
+
end
|
23
|
+
|
24
|
+
context "save!" do
|
25
|
+
When(:result) { entity.save! }
|
26
|
+
Then { expect(result).to have_failed(DomainDriven::ActiveRecordNotAvailableError) }
|
27
|
+
end
|
28
|
+
|
29
|
+
context "update_attribute" do
|
30
|
+
When(:result) { entity.update_attribute }
|
31
|
+
Then { expect(result).to have_failed(DomainDriven::ActiveRecordNotAvailableError) }
|
32
|
+
end
|
33
|
+
|
34
|
+
context "update_attribute!" do
|
35
|
+
When(:result) { entity.update_attribute! }
|
36
|
+
Then { expect(result).to have_failed(DomainDriven::ActiveRecordNotAvailableError) }
|
37
|
+
end
|
38
|
+
|
39
|
+
context "update_attributes" do
|
40
|
+
When(:result) { entity.update_attributes }
|
41
|
+
Then { expect(result).to have_failed(DomainDriven::ActiveRecordNotAvailableError) }
|
42
|
+
end
|
43
|
+
|
44
|
+
context "update_attributes!" do
|
45
|
+
When(:result) { entity.update_attributes! }
|
46
|
+
Then { expect(result).to have_failed(DomainDriven::ActiveRecordNotAvailableError) }
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -10,16 +10,11 @@ describe 'Criteria' do
|
|
10
10
|
Given { criteria1.where("id=5") }
|
11
11
|
Given { criteria2.page("3") }
|
12
12
|
|
13
|
-
context "
|
14
|
-
When(:result) { criteria1.
|
13
|
+
context "merge" do
|
14
|
+
When(:result) { criteria1.merge(criteria2) }
|
15
15
|
Then { result.to_s.should eq("target.where(id=5).page(3)") }
|
16
16
|
end
|
17
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
18
|
end
|
24
19
|
|
25
20
|
context "empty criteria has no effect" do
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class EntityUnderTest < DomainDriven::Entity
|
4
|
+
def some_biz_logic; 42; end
|
5
|
+
end
|
6
|
+
|
7
|
+
describe 'Entity' do
|
8
|
+
|
9
|
+
Given! (:model) { FakeModel.new }
|
10
|
+
Given! (:entity) { EntityUnderTest.wrap(model) }
|
11
|
+
|
12
|
+
context 'has model as _data ' do
|
13
|
+
Then { entity.entity?.should == true }
|
14
|
+
Then { entity._data.entity?.should == false }
|
15
|
+
Then { entity._data.should == model}
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'same as _data ' do
|
19
|
+
Then { entity._data == entity}
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'same as self' do
|
23
|
+
Then { entity == entity}
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'class name' do
|
27
|
+
context 'wrap same class as model' do
|
28
|
+
Then { entity.class.to_s == "FakeModel" }
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'wrap nil is still nil' do
|
32
|
+
When(:entity) { EntityUnderTest.wrap(nil) }
|
33
|
+
Then { entity.class.to_s == "NilClass"}
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'wraps nil is still nil' do
|
37
|
+
When(:entity_list) { EntityUnderTest.wraps(nil) }
|
38
|
+
Then { entity_list.class.to_s == "NilClass"}
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'class wraps a set of models' do
|
42
|
+
When(:entity_list) { EntityUnderTest.wraps( [model, model]) }
|
43
|
+
Then { entity_list.class.to_s == "Array"}
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'class wraps each model into a set of models' do
|
47
|
+
When(:entity_list) { EntityUnderTest.wraps( [model, model]) }
|
48
|
+
Then { entity_list.each{|e| e.should be_entity } }
|
49
|
+
Then { entity_list.each{|e| e.some_biz_logic.should == 42 } }
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.wrap(entity)
|
55
|
+
entity ? new(entity) : nil
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Model' do
|
4
|
+
|
5
|
+
Given! (:model) { FakeModel.new }
|
6
|
+
|
7
|
+
Then { model._data === model }
|
8
|
+
Then { model.entity? == false }
|
9
|
+
|
10
|
+
|
11
|
+
context "find" do
|
12
|
+
When(:result) { FakeModel.find('ignore') }
|
13
|
+
Then { expect(result).to have_failed( DomainDriven::RecordNotFound, 'clever message' ) }
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
class FakeService < DomainDriven::Service
|
5
|
+
def perform_main
|
6
|
+
context[:course] = 'Its a horse-course of course'
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'Service' do
|
12
|
+
|
13
|
+
context 'performing' do
|
14
|
+
When(:result) { FakeService.perform(request: {id: 123}, current_member: 'a-user',
|
15
|
+
page_param: 1,
|
16
|
+
logger: Logger.new($stdout)) }
|
17
|
+
Then { result.context[:course] == 'Its a horse-course of course' }
|
18
|
+
Then { result.context[:request][:id] == 123 }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'repository exists' do
|
22
|
+
|
23
|
+
Given(:repo) { DomainDriven::Service::Repo }
|
24
|
+
Given { repo.logger = FastSpecLogger.instance }
|
25
|
+
|
26
|
+
|
27
|
+
context 'lookup by string class name' do
|
28
|
+
Given { repo.factory = { course: 'FakeRepository' } }
|
29
|
+
|
30
|
+
context 'direct lookup ' do
|
31
|
+
When(:result) { repo.for(:course) }
|
32
|
+
Then { result.class.should == FakeRepository }
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'method_missing lookup ' do
|
36
|
+
When(:result) { repo.for_course }
|
37
|
+
Then { result.class.should == FakeRepository }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'lookup by symbol' do
|
42
|
+
Given { repo.factory = { course: :fake_repository } }
|
43
|
+
When(:result) { repo.for(:course) }
|
44
|
+
Then { result.class.should == FakeRepository }
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'lookup by lowercase string' do
|
48
|
+
Given { repo.factory = { course: 'fake_repository' } }
|
49
|
+
When(:result) { repo.for(:course) }
|
50
|
+
Then { result.class.should == FakeRepository }
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,31 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rspec-given'
|
3
3
|
require 'domain_driven'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
module ActiveRecord
|
7
|
+
class RecordNotFound < StandardError
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class RepositoryUnderTest
|
12
|
+
include DomainDriven::Repository
|
13
|
+
end
|
14
|
+
|
15
|
+
class FakeRepository
|
16
|
+
include DomainDriven::Repository
|
17
|
+
end
|
18
|
+
|
19
|
+
class FakeBaseModel
|
20
|
+
def self.find(*)
|
21
|
+
raise(ActiveRecord::RecordNotFound.new('clever message'))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class FakeModel < FakeBaseModel
|
26
|
+
include DomainDriven::Model
|
27
|
+
def course_type; 'A'; end
|
28
|
+
end
|
4
29
|
|
5
30
|
module Given
|
6
31
|
module ClassExtensions
|
@@ -8,3 +33,13 @@ module Given
|
|
8
33
|
alias Whereas! Given!
|
9
34
|
end
|
10
35
|
end
|
36
|
+
|
37
|
+
class FastSpecLogger
|
38
|
+
def self.instance
|
39
|
+
@instance ||= FastSpecLogger.new
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize; @last_error = ''; end
|
43
|
+
def error(value); @last_error = value; end
|
44
|
+
def info(value); @last_error = value; end
|
45
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: domain_driven
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.8.
|
4
|
+
version: 0.0.8.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark Windholtz
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-03-
|
12
|
+
date: 2014-03-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -123,7 +123,7 @@ dependencies:
|
|
123
123
|
- - ">="
|
124
124
|
- !ruby/object:Gem::Version
|
125
125
|
version: '0'
|
126
|
-
description:
|
126
|
+
description: domain_driven provides Rails abstract classes and generators to support
|
127
127
|
for a Domain Driven Design
|
128
128
|
email:
|
129
129
|
- mark@agiledna.com
|
@@ -148,7 +148,12 @@ files:
|
|
148
148
|
- lib/domain_driven/repository.rb
|
149
149
|
- lib/domain_driven/service.rb
|
150
150
|
- lib/domain_driven/version.rb
|
151
|
+
- spec/domain_driven/block_active_record_spec.rb
|
151
152
|
- spec/domain_driven/criteria_spec.rb
|
153
|
+
- spec/domain_driven/entity_spec.rb
|
154
|
+
- spec/domain_driven/model_spec.rb
|
155
|
+
- spec/domain_driven/repository_spec.rb
|
156
|
+
- spec/domain_driven/service_spec.rb
|
152
157
|
- spec/spec_helper.rb
|
153
158
|
homepage: https://github.com/mwindholtz/domain_driven
|
154
159
|
licenses:
|
@@ -173,7 +178,12 @@ rubyforge_project:
|
|
173
178
|
rubygems_version: 2.2.2
|
174
179
|
signing_key:
|
175
180
|
specification_version: 4
|
176
|
-
summary: Domain Driven
|
181
|
+
summary: domain_driven allows Domain Driven Design (DDD) within RubyOnRails
|
177
182
|
test_files:
|
183
|
+
- spec/domain_driven/block_active_record_spec.rb
|
178
184
|
- spec/domain_driven/criteria_spec.rb
|
185
|
+
- spec/domain_driven/entity_spec.rb
|
186
|
+
- spec/domain_driven/model_spec.rb
|
187
|
+
- spec/domain_driven/repository_spec.rb
|
188
|
+
- spec/domain_driven/service_spec.rb
|
179
189
|
- spec/spec_helper.rb
|