edr 0.0.1 → 0.0.2

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.
data/.gitignore CHANGED
@@ -11,6 +11,8 @@ spec/reports
11
11
  test/tmp
12
12
  test/version_tmp
13
13
  tmp
14
+ .idea
15
+ Gemfile.lock
14
16
 
15
17
  # YARD artifacts
16
18
  .yardoc
data/edr.gemspec CHANGED
@@ -16,4 +16,11 @@ Gem::Specification.new do |gem|
16
16
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
18
  gem.require_paths = ["lib"]
19
+
20
+ gem.add_development_dependency "rspec"
21
+ gem.add_development_dependency "rake"
22
+ gem.add_development_dependency "activerecord", "~> 3.2.8"
23
+ gem.add_development_dependency "sqlite3", "1.3.6"
24
+ gem.add_development_dependency "database_cleaner", "~> 0.9.1"
25
+ gem.add_dependency "orm_adapter", "~> 0.4.0"
19
26
  end
@@ -0,0 +1,14 @@
1
+ module Edr
2
+ module AR
3
+ module DataValidator
4
+ def self.validate model
5
+ data = model._data
6
+ if data.valid?
7
+ []
8
+ else
9
+ data.errors.full_messages
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ module Edr
2
+ module AR
3
+ module Repository
4
+ include ::Edr::Repository
5
+
6
+ def persist model
7
+ data_object = data(model)
8
+ data_object.save!
9
+
10
+ model.id = data_object.id
11
+ model.send(:repository=, self)
12
+
13
+ model
14
+ end
15
+ end
16
+ end
17
+ end
data/lib/edr/model.rb ADDED
@@ -0,0 +1,77 @@
1
+ require_relative 'registry'
2
+
3
+ module Edr
4
+ module Model
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ base.extend ::Forwardable
8
+ end
9
+
10
+ attr_accessor :_data
11
+
12
+ def initialize _data = _new_instance
13
+ if _data.kind_of?(Hash)
14
+ @_data = _new_instance _data
15
+ else
16
+ @_data = _data
17
+ end
18
+ end
19
+
20
+ def mass_assign(attributes)
21
+ attributes.each do |k, v|
22
+ self.send "#{k}=", v
23
+ end
24
+ end
25
+
26
+ def as_json(options={})
27
+ _data.as_json(options)
28
+ end
29
+
30
+ protected
31
+
32
+ def repository
33
+ raise "Transient objects don't have repositories" unless @repository
34
+ @repository
35
+ end
36
+
37
+ def repository= repo
38
+ @repository = repo
39
+ end
40
+
41
+ def association name
42
+ _data.send(name)
43
+ end
44
+
45
+ def wrap association
46
+ if association.respond_to? :first
47
+ return [] if association.empty?
48
+ association.map{|c| wrap c}
49
+ else
50
+ return nil if association.nil?
51
+ model_class = Registry.model_class_for(association.class)
52
+ model_class.new association
53
+ end
54
+ end
55
+
56
+ def _new_instance hash = {}
57
+ Registry.data_class_for(self.class).new hash
58
+ end
59
+
60
+ module ClassMethods
61
+ def fields *field_names
62
+ field_names.each do |field_name|
63
+ def_delegators :_data, field_name
64
+ def_delegators :_data, "#{field_name}="
65
+ end
66
+ end
67
+
68
+ def wrap_associations *association_names
69
+ association_names.each do |association_name|
70
+ define_method association_name do
71
+ wrap(association association_name)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,52 @@
1
+ require 'ostruct'
2
+
3
+ module Edr
4
+ class Registry
5
+ def self.define(&block)
6
+ @@instance = self.new(&block)
7
+ end
8
+
9
+ def self.data_class_for(model_class)
10
+ @@instance.data_class_for(model_class)
11
+ end
12
+
13
+ def self.model_class_for(data_class)
14
+ @@instance.model_class_for(data_class)
15
+ end
16
+
17
+ def initialize(&block)
18
+ @data_to_model_map = {}
19
+ @model_to_data_map = {}
20
+ self.instance_eval &block
21
+ end
22
+
23
+ def map model_class, data_class
24
+ @model_to_data_map[model_class] = data_class
25
+ @data_to_model_map[data_class] = model_class
26
+ end
27
+
28
+ def data_class_for model_class
29
+ data_class = @model_to_data_map[model_class]
30
+ return OpenStruct unless data_class
31
+
32
+ data_class
33
+ end
34
+
35
+ def model_class_for data_class
36
+ model_class = @data_to_model_map[data_class]
37
+ raise "No model class for #{data_class.to_s}" unless model_class
38
+
39
+ model_class
40
+ end
41
+
42
+ private
43
+
44
+ def model_to_data_map
45
+ @model_to_data_map
46
+ end
47
+
48
+ def data_to_model_map
49
+ @data_to_model_map
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,53 @@
1
+ require_relative 'registry'
2
+
3
+ module Edr
4
+ module Repository
5
+ def delete model
6
+ data_class.destroy data(model)
7
+ end
8
+
9
+ def delete_by_id id
10
+ data_class.destroy(data_class.get!(id))
11
+ end
12
+
13
+ def find id
14
+ wrap(data_class.get!(id))
15
+ end
16
+
17
+ def all
18
+ data_class.find_all.map do |data|
19
+ wrap(data)
20
+ end
21
+ end
22
+
23
+ protected
24
+
25
+ def wrap data
26
+ model_class.new(data).tap do |m|
27
+ m.send(:repository=, self)
28
+ end
29
+ end
30
+
31
+ def data model
32
+ model._data
33
+ end
34
+
35
+ def set_model_class model_class
36
+ singleton_class.send :define_method, :data_class do
37
+ Registry.data_class_for(model_class).to_adapter
38
+ end
39
+
40
+ singleton_class.send :define_method, :model_class do
41
+ model_class
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def where attrs
48
+ data_class.find_all(attrs).map do |data|
49
+ wrap(data)
50
+ end
51
+ end
52
+ end
53
+ end
data/lib/edr/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Edr
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/edr.rb CHANGED
@@ -1,5 +1,9 @@
1
1
  require "edr/version"
2
+ require 'orm_adapter'
2
3
 
3
- module Edr
4
- # Your code goes here...
5
- end
4
+ require_relative 'edr/registry'
5
+ require_relative 'edr/repository'
6
+ require_relative 'edr/model'
7
+
8
+ require_relative 'edr/ar/data_validator'
9
+ require_relative 'edr/ar/repository'
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+ require_relative '../test_data'
3
+
4
+ describe Edr::AR::DataValidator do
5
+
6
+ describe "Using data validation for a saved model" do
7
+ example do
8
+ order_data = OrderData.create! amount: 10, deliver_at: Date.today
9
+ order = Order.new(order_data)
10
+ order.amount = "blah"
11
+ Edr::AR::DataValidator.validate(order).should be_present
12
+ end
13
+ end
14
+
15
+ describe "Using data validation for a new model" do
16
+ example do
17
+ order = Order.new amount: "blah"
18
+ Edr::AR::DataValidator.validate(order).should be_present
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+ require_relative '../test_data'
3
+
4
+ describe Edr::Model do
5
+
6
+ describe "Using data objects directly" do
7
+ example do
8
+ order = OrderData.create! amount: 10, deliver_at: Date.today
9
+ order.items.create! amount: 6, name: 'Item 1'
10
+ order.items.create! amount: 4, name: 'Item 2'
11
+
12
+ order.reload.items.size.should == 2
13
+ order.items.first.amount.should == 6
14
+ end
15
+ end
16
+
17
+ describe "Creating a new model object" do
18
+ example do
19
+ order = Order.new
20
+ order.amount = 15
21
+
22
+ order.amount.should == 15
23
+ end
24
+
25
+ it "uses hash to initialize fields" do
26
+ order = Order.new amount: 15
27
+
28
+ order.amount.should == 15
29
+ end
30
+
31
+ it "creating an aggregate with children" do
32
+ order = Order.new
33
+ item = order.add_item name: 'item1', amount: 10
34
+
35
+ item.name.should == 'item1'
36
+ end
37
+ end
38
+
39
+ describe "Using data structure instead of a data object" do
40
+ example do
41
+ order = Order.new OpenStruct.new
42
+ order.amount = 99
43
+ order.amount.should == 99
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+ require_relative '../test_data'
3
+
4
+ describe Edr::Repository do
5
+ describe "Persisting objects" do
6
+ example do
7
+ order = Order.new amount: 10
8
+
9
+ OrderRepository.persist order
10
+
11
+ order.id.should be_present
12
+ order.amount.should == 10
13
+ end
14
+
15
+ it "persists an aggregate with children" do
16
+ order = Order.new amount: 10
17
+ order.add_item name: 'item1', amount: 5
18
+
19
+ OrderRepository.persist order
20
+
21
+ from_db = OrderRepository.find(order.id)
22
+ from_db.items.first.amount.should == 5
23
+ end
24
+
25
+ it "raises an exception when invalid data" do
26
+ order = Order.new amount: "invalid"
27
+
28
+ ->{OrderRepository.persist order}.should raise_error
29
+ end
30
+ end
31
+
32
+ describe "Creating objects through the repository variable" do
33
+ let(:order) do
34
+ order = Order.new(amount: 10)
35
+ OrderRepository.persist order
36
+ order
37
+ end
38
+
39
+ example do
40
+ order.add_item_through_repository name: 'item', amount: 10
41
+ order.items.first.name.should == 'item'
42
+ end
43
+
44
+ it "raises an exception when the root is not persisted" do
45
+ ->{Order.new(amount: 10).add_item_through_repository({})}.should raise_error
46
+ end
47
+ end
48
+
49
+ describe "Selecting models" do
50
+ let!(:data){OrderData.create! amount: 10, deliver_at: Date.today}
51
+
52
+ example do
53
+ orders = OrderRepository.find_by_amount 10
54
+ orders.first.id == data.id
55
+ end
56
+
57
+ it "finds by id" do
58
+ order = OrderRepository.find data.id
59
+ order.id.should == data.id
60
+ end
61
+
62
+ it "returns all saved objects" do
63
+ orders = OrderRepository.all
64
+ orders.first.id == data.id
65
+ end
66
+
67
+ it "raises an exception when cannot find cannot find object" do
68
+ ->{OrderRepository.find 999}.should raise_error
69
+ end
70
+ end
71
+
72
+ describe "Deleting models" do
73
+ let!(:order) do
74
+ order = Order.new amount: 10, deliver_at: Date.today
75
+ OrderRepository.persist order
76
+ order
77
+ end
78
+
79
+ example do
80
+ OrderRepository.delete order
81
+ OrderRepository.find_by_id(order.id).should be_nil
82
+ end
83
+
84
+ it "deletes by id" do
85
+ OrderRepository.delete_by_id order.id
86
+ OrderRepository.find_by_id(order.id).should be_nil
87
+ end
88
+
89
+ it "raises an exception when cannot find cannot find object" do
90
+ ->{OrderRepository.delete_by_id 999}.should raise_error
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,21 @@
1
+ require 'active_record'
2
+ require 'database_cleaner'
3
+
4
+ ActiveRecord::Base.establish_connection(
5
+ adapter: 'sqlite3',
6
+ database: ':memory:',
7
+ )
8
+
9
+ RSpec.configure do |config|
10
+ config.before(:suite) do
11
+ DatabaseCleaner.strategy = :transaction
12
+ end
13
+
14
+ config.before(:each) do
15
+ DatabaseCleaner.start
16
+ end
17
+
18
+ config.after(:each) do
19
+ DatabaseCleaner.clean
20
+ end
21
+ end
data/spec/test_data.rb ADDED
@@ -0,0 +1,116 @@
1
+ require_relative '../lib/edr'
2
+
3
+ # STEP0: Schema
4
+ # --------------------------------------------------
5
+ ActiveRecord::Schema.define(:version => 1) do
6
+ create_table "orders", :force => true do |t|
7
+ t.decimal "amount"
8
+ t.date "deliver_at"
9
+ t.datetime "created_at", :null => false
10
+ t.datetime "updated_at", :null => false
11
+ end
12
+
13
+ create_table "items", :force => true do |t|
14
+ t.string "name"
15
+ t.decimal "amount"
16
+ t.integer "order_id"
17
+ t.datetime "created_at", :null => false
18
+ t.datetime "updated_at", :null => false
19
+ end
20
+ end
21
+
22
+
23
+
24
+ # STEP1: Define data objects
25
+ # --------------------------------------------------
26
+ class OrderData < ActiveRecord::Base
27
+ self.table_name = "orders"
28
+
29
+ attr_accessible :amount, :deliver_at
30
+
31
+ validates :amount, numericality: true
32
+ has_many :items, class_name: 'ItemData', foreign_key: 'order_id'
33
+ end
34
+
35
+ class ItemData < ActiveRecord::Base
36
+ self.table_name = "items"
37
+
38
+ attr_accessible :amount, :name
39
+
40
+ validates :amount, numericality: true
41
+ validates :name, presence: true
42
+ end
43
+
44
+
45
+
46
+ # STEP2: Define domain objects
47
+ # --------------------------------------------------
48
+ class Order
49
+ include Edr::Model
50
+
51
+ fields :id, :amount, :deliver_at
52
+ wrap_associations :items
53
+
54
+ def add_item attrs
55
+ wrap association(:items).new(attrs)
56
+ end
57
+
58
+ def add_item_through_repository attrs
59
+ repository.create_item self, attrs
60
+ end
61
+ end
62
+
63
+ class Item
64
+ include Edr::Model
65
+
66
+ fields :id, :name, :amount, :order_id
67
+ end
68
+
69
+
70
+
71
+ # STEP3: map data objects to domain objects
72
+ # --------------------------------------------------
73
+ Edr::Registry.define do
74
+ map Order, OrderData
75
+ map Item, ItemData
76
+ end
77
+
78
+
79
+
80
+ # STEP4: Define repository to access data
81
+ # --------------------------------------------------
82
+ module OrderRepository
83
+ extend Edr::AR::Repository
84
+
85
+ set_model_class Order
86
+
87
+ def self.find_by_amount amount
88
+ where(amount: amount)
89
+ end
90
+
91
+ def self.find_by_id id
92
+ where(id: id).first
93
+ end
94
+
95
+ def self.create_item order, attrs
96
+ item = ItemRepository.create_item(order, attrs)
97
+ data(order).reload
98
+ return item
99
+ end
100
+
101
+ module ItemRepository
102
+ extend Edr::AR::Repository
103
+ set_model_class Item
104
+
105
+ def self.create_item order, attrs
106
+ item = Item.new(attrs)
107
+ item.order_id = order.id
108
+ persist item
109
+ end
110
+ end
111
+ end
112
+
113
+
114
+
115
+ # STEP5: Profit
116
+ # --------------------------------------------------
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: edr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -12,8 +12,104 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2012-09-24 00:00:00.000000000 Z
16
- dependencies: []
15
+ date: 2012-11-20 00:00:00.000000000 Z
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: rspec
19
+ requirement: !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ! '>='
23
+ - !ruby/object:Gem::Version
24
+ version: '0'
25
+ type: :development
26
+ prerelease: false
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: rake
35
+ requirement: !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ type: :development
42
+ prerelease: false
43
+ version_requirements: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ - !ruby/object:Gem::Dependency
50
+ name: activerecord
51
+ requirement: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: 3.2.8
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ~>
63
+ - !ruby/object:Gem::Version
64
+ version: 3.2.8
65
+ - !ruby/object:Gem::Dependency
66
+ name: sqlite3
67
+ requirement: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - '='
71
+ - !ruby/object:Gem::Version
72
+ version: 1.3.6
73
+ type: :development
74
+ prerelease: false
75
+ version_requirements: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - '='
79
+ - !ruby/object:Gem::Version
80
+ version: 1.3.6
81
+ - !ruby/object:Gem::Dependency
82
+ name: database_cleaner
83
+ requirement: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ~>
87
+ - !ruby/object:Gem::Version
88
+ version: 0.9.1
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: 0.9.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: orm_adapter
99
+ requirement: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ~>
103
+ - !ruby/object:Gem::Version
104
+ version: 0.4.0
105
+ type: :runtime
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ~>
111
+ - !ruby/object:Gem::Version
112
+ version: 0.4.0
17
113
  description: Entity Data-object Repository framework
18
114
  email:
19
115
  - vsavkin@nulogy.com
@@ -31,7 +127,17 @@ files:
31
127
  - Rakefile
32
128
  - edr.gemspec
33
129
  - lib/edr.rb
130
+ - lib/edr/ar/data_validator.rb
131
+ - lib/edr/ar/repository.rb
132
+ - lib/edr/model.rb
133
+ - lib/edr/registry.rb
134
+ - lib/edr/repository.rb
34
135
  - lib/edr/version.rb
136
+ - spec/edr/data_validator_spec.rb
137
+ - spec/edr/model_spec.rb
138
+ - spec/edr/repository_spec.rb
139
+ - spec/spec_helper.rb
140
+ - spec/test_data.rb
35
141
  homepage: ''
36
142
  licenses: []
37
143
  post_install_message:
@@ -56,4 +162,9 @@ rubygems_version: 1.8.24
56
162
  signing_key:
57
163
  specification_version: 3
58
164
  summary: Separate persistence from the domain model.
59
- test_files: []
165
+ test_files:
166
+ - spec/edr/data_validator_spec.rb
167
+ - spec/edr/model_spec.rb
168
+ - spec/edr/repository_spec.rb
169
+ - spec/spec_helper.rb
170
+ - spec/test_data.rb