entitymap 0.0.1

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 ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in entity_map.gemspec
4
+ gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2011 Peter Vandenabeele (http://vandenabeele.com)
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,155 @@
1
+ # EntityMap
2
+
3
+ The concept is to have an in-memory entity-relationsip model that has unique and
4
+ unambiguous mapping of the (business) entities and the relationships between them,
5
+ but still closely linked to the SQL concept of a foreign key as unambiguous
6
+ implementation of a relationship between tables).
7
+
8
+ In ActiveRecord, there is an IdentityMap being developed, but even with
9
+ IdentityMap turned on, the belongs_to and has_many relationships between
10
+ Parent and Child are not uniquely mapped to each_other. It is possible to
11
+ have at the same time
12
+
13
+ ``
14
+ daisy.children # => [#<Child:0x9275fe8 @name="Peter">]
15
+ peter.mother # => #<Mother:0x964c518 @name="Maria">
16
+ ```
17
+ If I am the child of Daisy, then she should be my mother, huh ...
18
+
19
+ So, the focus is on unambiguous and easy use of the relationships
20
+ in an ER model (Entity-Relationship model) where the reciprocity
21
+ of the 2 directions of the relationship is guaranteed correct.
22
+
23
+ Mapping this form of ER-model to a database as a persistence
24
+ layer is a second priority, after establishing the correct
25
+ Entity and Relationship behavior.
26
+
27
+ ## Feature wish list
28
+
29
+ it "handles belongs_to - has_many atomically in 1 place"
30
+ it "has identity_map for entities"
31
+ it "can be persisted in 1 command"
32
+ it "will check validations on _all_ entities before persisting"
33
+ it "allows select list in a form to work cleverly"
34
+ it "has 'live' scopes that are honored in memory"
35
+ it "can be marshalled"
36
+ it "works with a Plain Old Ruby Object"
37
+ it "works with other ORMs (ActiveRecord, Sequel, Datamapper)"
38
+ it "works with SimpleForm 2"
39
+ it "works with JRuby"
40
+ it "works in other languages (Java, ...) ?"
41
+
42
+ ## Usage Example
43
+
44
+ I want to be able to do:
45
+
46
+ ```ruby
47
+ class Parent
48
+ include EntityMap #(A)
49
+ entity_attributes :name #(B)
50
+ end
51
+
52
+ class Child
53
+ include EntityMap
54
+ entity_attributes :name, :grade
55
+ end
56
+
57
+ Child.relates_to Parent, :as => :mother #(B)
58
+ Child.relates_to Parent, :as => :father #(C)
59
+
60
+ daisy = Parent.new(:name => "Daisy")
61
+ peter = Child.new(:name => "Peter")
62
+
63
+ peter.mother = daisy #(D)
64
+
65
+ peter.mother # => #<Parent:0x961af18 @name="Daisy"> #(E)
66
+ daisy.mother_of # => [#<Child:0x9275fe8 @name="Peter">] #(F)
67
+
68
+ peter.mother = nil #(G)
69
+
70
+ peter.mother # => nil
71
+ daisy.mother_of # => [] #(H)
72
+
73
+ daisy.mother_of << peter #(I)
74
+
75
+ daisy.mother_of # => [#<Child:0x9275fe8 @name="Peter">]
76
+ peter.mother # => #<Parent:0x961af18 @name="Daisy"> #(J)
77
+
78
+ maria = Parent.new(:name => "Maria") #(K)
79
+
80
+ peter.mother = maria
81
+
82
+ peter.mother # => #<Parent:0x964c518 @name="Maria">
83
+ maria.mother_of # => [#<Child:0x9275fe8 @name="Peter">] #(L)
84
+ daisy.mother_of # => [] #(M)
85
+
86
+ peter.mother = daisy # back to reality
87
+
88
+ frans = Parent.new(:name => "Frans")
89
+ peter.father = frans
90
+
91
+ jan = Child.new(:name => "Jan")
92
+ jan.mother = maria
93
+ jan.father = frans
94
+
95
+ peter.father.father_of # => [#<Child:0x9275fe8 @name="Peter">, #<Child:0xa49e030 @name="Jan">] #(N)
96
+
97
+ # adding an implementation of persistence
98
+
99
+ class Child
100
+ # not sure if this is enough ActiveRecord
101
+ include ActiveRecord::Persistence
102
+ include ActiveRecord::FinderMethods
103
+ end
104
+
105
+ class Parent
106
+ # not sure if this is enough ActiveRecord
107
+ include ActiveRecord::Persistence
108
+ include ActiveRecord::FinderMethods
109
+ end
110
+
111
+ EntityMap.save # => true #(O)
112
+ peter.id # => 1 #(P)
113
+ jan.id # => 2
114
+ new_peter = Child.find(1)
115
+ peter.object_id == new_peter.object_id # => true #(Q)
116
+ peter.ref == new_peter.ref # => true #(R)
117
+ ```
118
+
119
+ ## Example of the Parent-Child ambiguity in ActiveRecord (4.0.0.beta)
120
+
121
+ ```
122
+ peterv@ASUS:~/b/github/rails/rails/new_app$ cat app/models/*
123
+ class Child < ActiveRecord::Base
124
+ belongs_to :parent, :inverse_of => :children
125
+ end
126
+ class Parent < ActiveRecord::Base
127
+ has_many :children, :inverse_of => :parent
128
+ end
129
+
130
+ peterv@ASUS:~/b/github/rails/rails/new_app$ bundle exec rails c
131
+ Loading development environment (Rails 4.0.0.beta)
132
+ 001:0> daisy = Parent.new(:name => "Daisy")
133
+ => #<Parent id: nil, name: "Daisy", created_at: nil, updated_at: nil>
134
+ 002:0> peter = Child.new(:name => "Peter")
135
+ => #<Child id: nil, name: "Peter", parent_id: nil, created_at: nil, updated_at: nil>
136
+ 003:0> peter.parent = daisy
137
+ => #<Parent id: nil, name: "Daisy", created_at: nil, updated_at: nil>
138
+ 004:0> daisy.children
139
+ => []
140
+ 005:0> daisy.children << peter
141
+ => [#<Child id: nil, name: "Peter", parent_id: nil, created_at: nil, updated_at: nil>]
142
+ 006:0> daisy.children
143
+ => [#<Child id: nil, name: "Peter", parent_id: nil, created_at: nil, updated_at: nil>]
144
+ 007:0> maria = Parent.new(:name => "Maria")
145
+ => #<Parent id: nil, name: "Maria", created_at: nil, updated_at: nil>
146
+ 009:0> peter.parent = maria
147
+ => #<Parent id: nil, name: "Maria", created_at: nil, updated_at: nil>
148
+ 010:0> maria.children
149
+ => []
150
+ 011:0> daisy.children
151
+ => [#<Child id: nil, name: "Peter", parent_id: nil, created_at: nil, updated_at: nil>]
152
+ 012:0> peter.parent
153
+ => #<Parent id: nil, name: "Maria", created_at: nil, updated_at: nil>
154
+ 013:0> # giving up ... my mother is "Maria", but I am child of "Daisy" ...
155
+ ```
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/entitymap.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "entity_map/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "entitymap"
7
+ s.version = EntityMap::VERSION
8
+ s.authors = ["Peter Vandenabeele"]
9
+ s.email = ["peter@vandenabeele.com"]
10
+ s.homepage = "http://github.com/petervandenabeele/entitymap"
11
+ s.summary = %q{Mapping business entities and relations}
12
+ s.description = <<DESCRIPTION
13
+ Mapping entities and relations atomically in memory during the
14
+ processing of a business transaction.
15
+ Persistance is executed later in 1 separate atomic transaction.
16
+ DESCRIPTION
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {spec}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+
23
+ s.add_development_dependency "rspec"
24
+ # s.add_runtime_dependency "none for the moment"
25
+ end
@@ -0,0 +1,11 @@
1
+ module EntityMap
2
+ class Entity
3
+
4
+ class << self
5
+ def object_ref(o)
6
+ o.object_id
7
+ end
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module EntityMap
2
+ class Entity
3
+
4
+ def ==(other)
5
+ Entity.object_ref(self) == Entity.object_ref(other)
6
+ end
7
+
8
+ alias :eql? :==
9
+
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ module EntityMap
2
+ class Entity
3
+
4
+ attr_reader :object, :entity_group
5
+
6
+ def initialize(object, entity_group)
7
+ raise "INTERNAL ERROR: Entity.new must have EntityGroup argument" unless EntityGroup === entity_group
8
+ @entity_group = entity_group
9
+ @object = object
10
+ insert_into_entity_hash
11
+ end
12
+
13
+ private
14
+
15
+ def insert_into_entity_hash
16
+ eh = @entity_group.instance_variable_get(:@entity_hash)
17
+ ref = Entity.object_ref(@object)
18
+ # object_ref should be unique in the hash
19
+ raise "INTERNAL ERROR: Second Entity for same object_ref is built" if eh[ref]
20
+ eh[ref] = self
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ require 'entity_map/entity/new'
2
+ require 'entity_map/entity/methods'
3
+ require 'entity_map/entity/class_methods'
@@ -0,0 +1,15 @@
1
+ module EntityMap
2
+ class EntityGroup
3
+
4
+ def find(o)
5
+ @entity_hash[Entity.object_ref(o)]
6
+ end
7
+
8
+ class << self
9
+ def find(o)
10
+ single.find(o)
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ module EntityMap
2
+ class EntityGroup
3
+
4
+ def initialize
5
+ @entity_hash = {}
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module EntityMap
2
+ class EntityGroup
3
+
4
+ class << self
5
+ def single
6
+ @single ||=EntityGroup.new
7
+ end
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ require 'entity_map/entity_group/initialize'
2
+ require 'entity_map/entity_group/single'
3
+ require 'entity_map/entity_group/find'
@@ -0,0 +1,10 @@
1
+ module EntityMap
2
+
3
+ attr_accessor :entity
4
+
5
+ def initialize
6
+ entity = Entity.new(self, EntityGroup.single)
7
+ super
8
+ end
9
+
10
+ end
@@ -0,0 +1,3 @@
1
+ module EntityMap
2
+ VERSION = "0.0.1"
3
+ end
data/lib/entity_map.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'entity_map/version'
2
+ require 'entity_map/entity_group'
3
+ require 'entity_map/entity'
4
+ require 'entity_map/included'
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ module EntityMap
4
+
5
+ describe Entity, "class methods" do
6
+
7
+ it "object_ref is equal for same objects" do
8
+ s1 = "test"
9
+ Entity.object_ref(s1).should == Entity.object_ref(s1)
10
+ end
11
+
12
+ it "object_ref is different for different objects" do
13
+ s1 = "test"
14
+ s2 = "test"
15
+ Entity.object_ref(s1).should_not == Entity.object_ref(s2)
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ module EntityMap
4
+
5
+ describe Entity do
6
+
7
+ it "creates a new entity when an object in target class is created" do
8
+ class Test
9
+ include EntityMap
10
+ end
11
+
12
+ t = Test.new
13
+ EntityGroup.find(t).should_not be_nil
14
+ end
15
+
16
+ it "raises error when object with same object_ref would be added to hash" do
17
+ class Test
18
+ include EntityMap
19
+ end
20
+
21
+ t = Test.new
22
+ lambda {Entity.new(t, EntityGroup.single)}.should raise_error
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ module EntityMap
4
+
5
+ describe Entity do
6
+
7
+ let(:random_object) {Object.new}
8
+ let(:entity_group) {EntityGroup.new}
9
+ let(:entity) {Entity.new(random_object, entity_group)}
10
+
11
+ it "== uses object_ref to check" do
12
+ entity.should == entity
13
+ end
14
+
15
+ it "eql uses object_ref to check" do
16
+ entity.eql?(entity).should be_true
17
+ end
18
+
19
+ it "different entity is not ==" do
20
+ entity_2=Entity.new("a", entity_group)
21
+ entity.should_not == entity_2
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ module EntityMap
4
+
5
+ describe Entity do
6
+
7
+ let(:random_object) {Object.new}
8
+ let(:entity_group) {EntityGroup.new}
9
+ let(:entity) {Entity.new(random_object, entity_group)}
10
+
11
+ it "new creates new entity" do
12
+ entity.should_not be_nil
13
+ end
14
+
15
+ it "is part of an EntityGroup" do
16
+ entity.entity_group.should_not be_nil
17
+ end
18
+
19
+ it "raises an exception when supplied entity_group is not correct" do
20
+ lambda {Entity.new(random_object,nil)}.should raise_error
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ module EntityMap
4
+
5
+ describe EntityGroup do
6
+
7
+ let(:object) {Object.new}
8
+
9
+ it "finds an entity in this group (INSTANCE)" do
10
+ entity = Entity.new(object,subject)
11
+ eh = subject.instance_variable_get(:@entity_hash)
12
+ eh[Entity.object_ref(object)] = entity
13
+ subject.find(object).should == entity
14
+ end
15
+
16
+ it "finds an entity in all groups (CLASS)" do
17
+ entity_group = EntityGroup.single
18
+ entity = Entity.new(object, entity_group)
19
+ eh = entity_group.instance_variable_get(:@entity_hash)
20
+ eh[Entity.object_ref(object)] = entity
21
+ EntityGroup.find(object).should == entity
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ module EntityMap
4
+
5
+ describe EntityGroup do
6
+
7
+ it "new creates new entity_group" do
8
+ subject.should_not be_nil
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ module EntityMap
4
+
5
+ describe EntityGroup do
6
+
7
+ # in a later phase, multiple groups can exist
8
+ it "single returns an instance of EntityGroup" do
9
+ EntityGroup.single.should be_a(EntityGroup)
10
+ end
11
+
12
+ it "single returns a singleton instance of EntityGroup" do
13
+ eg_1 = EntityGroup.single
14
+ eg_2 = EntityGroup.single
15
+ eg_1.object_id.should == eg_2.object_id
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1 @@
1
+ require 'entity_map'
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: entitymap
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Peter Vandenabeele
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &72628490 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *72628490
25
+ description: ! 'Mapping entities and relations atomically in memory during the
26
+
27
+ processing of a business transaction.
28
+
29
+ Persistance is executed later in 1 separate atomic transaction.
30
+
31
+ '
32
+ email:
33
+ - peter@vandenabeele.com
34
+ executables: []
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - .gitignore
39
+ - .rspec
40
+ - Gemfile
41
+ - MIT-LICENSE
42
+ - README.md
43
+ - Rakefile
44
+ - entitymap.gemspec
45
+ - lib/entity_map.rb
46
+ - lib/entity_map/entity.rb
47
+ - lib/entity_map/entity/class_methods.rb
48
+ - lib/entity_map/entity/methods.rb
49
+ - lib/entity_map/entity/new.rb
50
+ - lib/entity_map/entity_group.rb
51
+ - lib/entity_map/entity_group/find.rb
52
+ - lib/entity_map/entity_group/initialize.rb
53
+ - lib/entity_map/entity_group/single.rb
54
+ - lib/entity_map/included.rb
55
+ - lib/entity_map/version.rb
56
+ - spec/entity/class_methods_spec.rb
57
+ - spec/entity/include_spec.rb
58
+ - spec/entity/methods_spec.rb
59
+ - spec/entity/new_spec.rb
60
+ - spec/entity_group/find_spec.rb
61
+ - spec/entity_group/new_spec.rb
62
+ - spec/entity_group/single_spec.rb
63
+ - spec/spec_helper.rb
64
+ homepage: http://github.com/petervandenabeele/entitymap
65
+ licenses: []
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 1.8.10
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: Mapping business entities and relations
88
+ test_files: []