rddd 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.DS_Store +0 -0
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +3 -1
  4. data/README.md +8 -2
  5. data/Rakefile +5 -0
  6. data/lib/rddd.rb +5 -5
  7. data/lib/rddd/aggregates/entity.rb +75 -0
  8. data/lib/rddd/aggregates/finders.rb +23 -0
  9. data/lib/rddd/aggregates/repositories/base.rb +20 -0
  10. data/lib/rddd/aggregates/repositories/factory.rb +33 -0
  11. data/lib/rddd/aggregates/root.rb +25 -0
  12. data/lib/rddd/configuration.rb +1 -9
  13. data/lib/rddd/{service.rb → services/service.rb} +11 -9
  14. data/lib/rddd/{service_bus.rb → services/service_bus.rb} +23 -20
  15. data/lib/rddd/services/service_factory.rb +23 -0
  16. data/lib/rddd/version.rb +1 -1
  17. data/lib/rddd/views/cache.rb +24 -0
  18. data/lib/rddd/views/cacheable.rb +50 -0
  19. data/lib/rddd/views/view.rb +38 -0
  20. data/spec/integration_spec.rb +9 -9
  21. data/spec/lib/aggregates/entity_spec.rb +17 -0
  22. data/spec/lib/{aggregate_root_spec.rb → aggregates/root_spec.rb} +7 -7
  23. data/spec/lib/repositories/repository_factory_spec.rb +49 -0
  24. data/spec/lib/repositories/repository_spec.rb +30 -0
  25. data/spec/lib/{service_bus_spec.rb → services/service_bus_spec.rb} +6 -6
  26. data/spec/lib/services/service_factory_spec.rb +61 -0
  27. data/spec/lib/services/service_spec.rb +31 -0
  28. data/spec/lib/views/integration_spec.rb +153 -0
  29. metadata +29 -24
  30. data/lib/rddd/aggregate_root.rb +0 -17
  31. data/lib/rddd/aggregate_root_finders.rb +0 -21
  32. data/lib/rddd/entity.rb +0 -11
  33. data/lib/rddd/repository.rb +0 -16
  34. data/lib/rddd/repository_factory.rb +0 -25
  35. data/lib/rddd/service_factory.rb +0 -21
  36. data/spec/lib/entity_spec.rb +0 -17
  37. data/spec/lib/repository_factory_spec.rb +0 -47
  38. data/spec/lib/repository_spec.rb +0 -26
  39. data/spec/lib/service_factory_spec.rb +0 -59
  40. data/spec/lib/service_spec.rb +0 -27
data/.DS_Store CHANGED
Binary file
data/Gemfile CHANGED
@@ -3,6 +3,8 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in rddd.gemspec
4
4
  gemspec
5
5
 
6
+ gem 'rake'
7
+
6
8
  group :testing do
7
9
  gem 'rspec'
8
10
  gem 'mocha'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rddd (0.1.3)
4
+ rddd (0.2.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -10,6 +10,7 @@ GEM
10
10
  metaclass (0.0.1)
11
11
  mocha (0.12.7)
12
12
  metaclass (~> 0.0.1)
13
+ rake (0.9.2.2)
13
14
  rspec (2.11.0)
14
15
  rspec-core (~> 2.11.0)
15
16
  rspec-expectations (~> 2.11.0)
@@ -24,5 +25,6 @@ PLATFORMS
24
25
 
25
26
  DEPENDENCIES
26
27
  mocha
28
+ rake
27
29
  rddd!
28
30
  rspec
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  [rDDD@sniffer](https://sniffer.io/info/public-project-135230969156499600) - see the code quality on Sniffer.io
2
2
 
3
+ [![Build Status](https://travis-ci.org/petrjanda/rddd.png?branch=master)](https://travis-ci.org/petrjanda/rddd)
4
+
3
5
  rddd
4
6
  ====
5
7
 
@@ -63,9 +65,13 @@ Presenter is the view to the domain data, which is typically aggregation over mu
63
65
  language presenter is report. In Rddd, views output plain Ruby hashes, thus no domain object is leaking outside the domain
64
66
  itself. The key design goal was to dont let framework later call any additional adhoc methods. Thats why framework doesn't interact with view object directly and pure Hash is returned back.
65
67
 
66
- ### Aggregate root, entity, repository
68
+ ### Aggregate root
69
+
70
+ ### Entity
71
+
72
+ ### Repository
67
73
 
68
- TBA
74
+ ### Factory
69
75
 
70
76
  ## Planned features
71
77
 
data/Rakefile CHANGED
@@ -1 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/lib/rddd.rb CHANGED
@@ -8,13 +8,13 @@ module Rddd
8
8
  #
9
9
  # ## Usage
10
10
  #
11
- # Rddd.configure do |config|
12
- # config.service_creator = lambda do |name|
13
- # class_name = "#{name.to_s.camel_case}Service"
11
+ # Rddd.configure do |config|
12
+ # config.service_creator = lambda do |name|
13
+ # class_name = "#{name.to_s.camel_case}Service"
14
14
  #
15
- # Rddd::Services.const_get(class_name.to_sym)
15
+ # Rddd::Services.const_get(class_name.to_sym)
16
+ # end
16
17
  # end
17
- # end
18
18
  #
19
19
  def self.configure
20
20
  yield(Rddd::Configuration.instance)
@@ -0,0 +1,75 @@
1
+ module Rddd
2
+ module Aggregates
3
+ #
4
+ # Entity is the object with identity, which is unique across the system. Entities
5
+ # are considered equal only in case their identity is equal discregarding rest
6
+ # of the similarities.
7
+ #
8
+ # rDDD works agnostic to identifier type. String is used by default.
9
+ #
10
+ # Lets create a model of flat and rooms with some basic calculations:
11
+ #
12
+ # class Room < Entity
13
+ # def initialize(id, rooms)
14
+ # super(id)
15
+ #
16
+ # @rooms = rooms
17
+ # end
18
+ # end
19
+ #
20
+ # # (see AggregateRoot)
21
+ # class Flat < AggregateRoot
22
+ # attr_reader :size
23
+ #
24
+ # def initialize(id, size)
25
+ # super(id)
26
+ #
27
+ # @size = size
28
+ # end
29
+ #
30
+ # def size
31
+ # @rooms.reduce(0) {|total, room| total += room.size }
32
+ # end
33
+ # end
34
+ #
35
+ # rooms = []
36
+ # rooms << Room.new('kitchen', 15)
37
+ # rooms << Room.new('living room', 35)
38
+ # rooms << Room.new('bed room', 20)
39
+ #
40
+ # flat = Flat.new('A12TY83', rooms)
41
+ #
42
+ # flat.size #= 70
43
+ #
44
+ # As you can see Room entity has its identity (room name) which although isn't
45
+ # global, but local to the given flat instead. Its natural way how we seem rooms.
46
+ # We always identify flat/house first and then room within it. Maintaining global
47
+ # identity is sometimes overkill. Local identity can do just fine.
48
+ #
49
+ class Entity
50
+
51
+ #
52
+ # Entity unique identifier.
53
+ #
54
+ attr_reader :id
55
+
56
+ #
57
+ # Create entity with given identity.
58
+ #
59
+ # @param [String] Entity identity.
60
+ #
61
+ def initialize(id)
62
+ @id = id
63
+ end
64
+
65
+ #
66
+ # Compare two entities for equality.
67
+ #
68
+ # @param [Entity] Entity to compare with.
69
+ #
70
+ def ==(other)
71
+ @id == other.id
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,23 @@
1
+ module Rddd
2
+ module Aggregates
3
+ module Finders
4
+ def finder(name)
5
+ craete_static_method(name) do |*args|
6
+ repository.send(name, *args)
7
+ end
8
+ end
9
+
10
+ def repository
11
+ Repositories::Factory.build(self)
12
+ end
13
+
14
+ private
15
+
16
+ def craete_static_method(name, &block)
17
+ (class << self; self; end).module_eval do
18
+ define_method name, &block
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ #
2
+ # Repository base class.
3
+ #
4
+ module Rddd
5
+ module Repositories
6
+ class Base
7
+ def create(subject)
8
+ raise NotImplementedError
9
+ end
10
+
11
+ def update(subject)
12
+ raise NotImplementedError
13
+ end
14
+
15
+ def delete(subject)
16
+ raise NotImplementedError
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ require 'rddd/configuration'
2
+ require 'rddd/aggregates/repositories/base'
3
+
4
+ module Rddd
5
+ module Repositories
6
+ class NotExistingRepository < RuntimeError
7
+ end
8
+
9
+ #
10
+ # @private
11
+ #
12
+ # Create instance of Repository using the Configration#repository_creator
13
+ # and passing the class along.
14
+ #
15
+ class Factory
16
+ CreatorNotGiven = Class.new(RuntimeError)
17
+
18
+ def self.build(clazz)
19
+ creator = Configuration.instance.repository_creator
20
+
21
+ raise CreatorNotGiven unless creator
22
+
23
+ begin
24
+ repository = creator.call(clazz)
25
+ rescue
26
+ raise NotExistingRepository unless repository
27
+ end
28
+
29
+ repository.new
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,25 @@
1
+ require 'rddd/aggregates/entity'
2
+ require 'rddd/aggregates/finders'
3
+ require 'rddd/aggregates/repositories/factory'
4
+
5
+ module Rddd
6
+ module Aggregates
7
+ #
8
+ # Domain model Entities (see Entity#initialize) are organized to clusters
9
+ # called Aggregates. Every Aggregate should have its Root. Aggregate Root
10
+ # should be only entity from Aggregate visible from outside to guarantee
11
+ # consistency of all operations performed on the given Aggregate.
12
+ #
13
+ class Root < Entity
14
+ extend Finders
15
+
16
+ finder :find
17
+
18
+ [:create, :update, :delete].each do |action|
19
+ define_method action do
20
+ self.class.repository.send(action, self)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -8,14 +8,6 @@ module Rddd
8
8
  class Configuration
9
9
  include Singleton
10
10
 
11
- attr_writer :service_creator, :repository_creator
12
-
13
- def service_creator
14
- @service_creator
15
- end
16
-
17
- def repository_creator
18
- @repository_creator
19
- end
11
+ attr_accessor :service_creator, :repository_creator
20
12
  end
21
13
  end
@@ -14,17 +14,19 @@
14
14
  # Remember dummy data goes in, dummy data goes out.
15
15
  #
16
16
  module Rddd
17
- class Service
18
- def initialize(attributes = {})
19
- @attributes = attributes
20
- end
17
+ module Services
18
+ class Service
19
+ def initialize(attributes = {})
20
+ @attributes = attributes
21
+ end
21
22
 
22
- def valid?
23
- true
24
- end
23
+ def valid?
24
+ true
25
+ end
25
26
 
26
- def execute
27
- raise NotImplementedError
27
+ def execute
28
+ raise NotImplementedError
29
+ end
28
30
  end
29
31
  end
30
32
  end
@@ -1,4 +1,4 @@
1
- require 'rddd/service_factory'
1
+ require 'rddd/services/service_factory'
2
2
 
3
3
  module Rddd
4
4
  #
@@ -30,29 +30,32 @@ module Rddd
30
30
  # end
31
31
  #
32
32
  #
33
- module ServiceBus
34
- #
35
- # Execute the given service.
36
- #
37
- # @param [Symbol] service to be executed.
38
- # @param [Hash] attributes to be passed to the service call.
39
- # @param [Block] optional error callback block.
40
- #
41
- def execute_service(service_name, attributes = {})
42
- service = build_service(service_name, attributes)
43
33
 
44
- unless service.valid?
45
- yield(service.errors) if block_given?
46
- return
47
- end
34
+ module Services
35
+ module ServiceBus
36
+ #
37
+ # Execute the given service.
38
+ #
39
+ # @param [Symbol] service to be executed.
40
+ # @param [Hash] attributes to be passed to the service call.
41
+ # @param [Block] optional error callback block.
42
+ #
43
+ def execute_service(service_name, attributes = {})
44
+ service = build_service(service_name, attributes)
48
45
 
49
- service.execute
50
- end
46
+ unless service.valid?
47
+ yield(service.errors) if block_given?
48
+ return
49
+ end
51
50
 
52
- private
51
+ service.execute
52
+ end
53
53
 
54
- def build_service(service_name, attributes)
55
- ServiceFactory.build(service_name, attributes)
54
+ private
55
+
56
+ def build_service(service_name, attributes)
57
+ ServiceFactory.build(service_name, attributes)
58
+ end
56
59
  end
57
60
  end
58
61
  end
@@ -0,0 +1,23 @@
1
+ require 'rddd/configuration'
2
+
3
+ module Rddd
4
+ module Services
5
+ class ServiceFactory
6
+ CreatorNotGiven = Class.new(RuntimeError)
7
+
8
+ InvalidService = Class.new(RuntimeError)
9
+
10
+ def self.build(name, attributes)
11
+ creator = Configuration.instance.service_creator
12
+
13
+ raise CreatorNotGiven unless creator
14
+
15
+ begin
16
+ creator.call(name).new(attributes)
17
+ rescue
18
+ raise Rddd::Services::ServiceFactory::InvalidService
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
data/lib/rddd/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Rddd
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -0,0 +1,24 @@
1
+ module Rddd
2
+ module Views
3
+ class Cache
4
+ def initialize(id, repository)
5
+ @id = id
6
+ @repository = repository
7
+ end
8
+
9
+ def read
10
+ @repository.get(@id) if @repository
11
+ end
12
+
13
+ def invalidate
14
+ @repository.set(@id, nil)
15
+ end
16
+
17
+ def write(data, timeout)
18
+ expire_at = timeout ? Time.now + timeout : nil
19
+
20
+ @repository.set(@id, data, expire_at)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,50 @@
1
+ module Rddd
2
+ module Views
3
+ module Caching
4
+ attr_reader :cache_disabled, :timeout
5
+
6
+ def cache(attributes)
7
+ @cache_disabled = attributes.has_key?(:enabled) ? !attributes[:enabled] : false
8
+ @timeout = attributes.has_key?(:timeout) ? 60 * attributes[:timeout] : nil
9
+ end
10
+ end
11
+
12
+ module Cacheable
13
+ def invalidate
14
+ cache.invalidate if cache
15
+ end
16
+
17
+ def warm_cache
18
+ update_cache(build)
19
+ end
20
+
21
+ private
22
+
23
+ def read_cache
24
+ cache.read if cache
25
+ end
26
+
27
+ def update_cache(data)
28
+ cache.write(data, self.class.timeout) if cache
29
+ end
30
+
31
+ def cache
32
+ nil unless repository
33
+
34
+ @cache ||= Cache.new(cache_id, repository)
35
+ end
36
+
37
+ def cache_id
38
+ "#{name}#{id}"
39
+ end
40
+
41
+ def repository
42
+ begin
43
+ @repository ||= ViewRepository.new
44
+ rescue
45
+ nil
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end