ddd-associations 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 +18 -0
- data/.travis.yml +5 -0
- data/Gemfile +2 -0
- data/Guardfile +11 -0
- data/README.md +15 -0
- data/Rakefile +11 -0
- data/ddd-associations.gemspec +18 -0
- data/lib/ddd-associations.rb +8 -0
- data/lib/ddd-associations/associations.rb +78 -0
- data/lib/ddd-associations/many_collection.rb +24 -0
- data/lib/ddd-associations/many_to_many_collection.rb +25 -0
- data/lib/ddd-associations/one_to_many_collection.rb +25 -0
- data/lib/ddd-associations/version.rb +5 -0
- metadata +90 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
guard 'bundler' do
|
2
|
+
watch('Gemfile')
|
3
|
+
end
|
4
|
+
|
5
|
+
guard 'minitest' do
|
6
|
+
watch(%r|^test/unit/test_(.*)\.rb|)
|
7
|
+
watch(%r|^lib/*\.rb|){'test'}
|
8
|
+
watch(%r|^test/domain/undilatory/*\.rb|){'test'}
|
9
|
+
watch(%r{^test/domain/undilatory/([^/]+)\.rb$}){|m| "test/unit/test_#{m[1]}.rb"}
|
10
|
+
watch(%r|^test/helper\.rb|){'test'}
|
11
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# DDD Associations
|
2
|
+
|
3
|
+
A metaprogramming exercise that implements associations in pure Ruby, inspired by domain-driven design.
|
4
|
+
|
5
|
+
Please see the [indilatory](http://github.com/nerab/indilatory) sample application for usage. It also serves as the initial unit test bed for this gem.
|
6
|
+
|
7
|
+
[](http://travis-ci.org/nerab/ddd-associations)
|
8
|
+
|
9
|
+
## Contributing
|
10
|
+
|
11
|
+
1. Fork it
|
12
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
13
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
14
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
15
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path('../lib/ddd-associations/version', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.authors = ["Nicholas E. Rabenau"]
|
5
|
+
gem.email = ["nerab@gmx.net"]
|
6
|
+
gem.summary = %q{DDD Associations}
|
7
|
+
gem.description = %q{Associations in pure Ruby, inspired by domain-driven design.}
|
8
|
+
|
9
|
+
gem.files = `git ls-files`.split($\)
|
10
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
11
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
12
|
+
gem.name = "ddd-associations"
|
13
|
+
gem.require_paths = ["lib"]
|
14
|
+
gem.version = DDD::Associations::VERSION
|
15
|
+
|
16
|
+
gem.add_dependency 'activesupport', '~> 3.2'
|
17
|
+
gem.add_development_dependency 'rake'
|
18
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'forwardable'
|
3
|
+
require 'active_support/inflector'
|
4
|
+
|
5
|
+
require 'ddd-associations/associations'
|
6
|
+
require 'ddd-associations/many_collection'
|
7
|
+
require 'ddd-associations/one_to_many_collection'
|
8
|
+
require 'ddd-associations/many_to_many_collection'
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module DDD
|
2
|
+
module Associations
|
3
|
+
module ClassMethods
|
4
|
+
#
|
5
|
+
# One project has many tasks
|
6
|
+
#
|
7
|
+
def has_many(others)
|
8
|
+
@others = others
|
9
|
+
@collection = OneToManyCollection
|
10
|
+
self.send('attr_reader', others)
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# A task belongs to a project
|
15
|
+
#
|
16
|
+
def belongs_to(other)
|
17
|
+
self.send('attr_reader', other)
|
18
|
+
self.send('define_method', "#{other}=") do |one|
|
19
|
+
return if self.instance_variable_get("@#{other}") == one
|
20
|
+
|
21
|
+
collection_name = self.class.name.demodulize.downcase.pluralize
|
22
|
+
|
23
|
+
if one.nil?
|
24
|
+
# de-register self at one it currently belongs to
|
25
|
+
self.instance_variable_get("@#{other}").send(collection_name).delete(self) if self.instance_variable_get("@#{other}")
|
26
|
+
else
|
27
|
+
# register self at the one
|
28
|
+
one.send(collection_name) << self
|
29
|
+
end
|
30
|
+
|
31
|
+
self.instance_variable_set("@#{other}", one)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# Regular attribute definition:
|
37
|
+
#
|
38
|
+
# has_one :title
|
39
|
+
#
|
40
|
+
# is almost equivalent to
|
41
|
+
#
|
42
|
+
# attr_accessor
|
43
|
+
#
|
44
|
+
# In addition, it registers the attribute(s) to they can be enumerated in the repository etc.
|
45
|
+
#
|
46
|
+
def has_one(*attr)
|
47
|
+
if attr.nil? or attr.empty?
|
48
|
+
@attributes
|
49
|
+
else
|
50
|
+
@attributes = attr
|
51
|
+
@attributes.each{|attr| self.send('attr_accessor', attr)}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
attr_reader :attributes
|
56
|
+
|
57
|
+
#
|
58
|
+
# Many tasks belong to many tags and vice versa
|
59
|
+
#
|
60
|
+
def has_and_belongs_to_many(others)
|
61
|
+
@others = others
|
62
|
+
@collection = ManyToManyCollection
|
63
|
+
self.send('attr_reader', others)
|
64
|
+
end
|
65
|
+
|
66
|
+
def new(*args)
|
67
|
+
super(*args).tap do |instance|
|
68
|
+
instance.instance_variable_set("@#{@others}", @collection.new(instance))
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# http://blog.jayfields.com/2006/12/ruby-instance-and-class-methods-from.html
|
74
|
+
def self.included(base)
|
75
|
+
base.extend(ClassMethods)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module DDD
|
2
|
+
module Associations
|
3
|
+
#
|
4
|
+
# Base class for has_many and has_and_belongs_to_many collections
|
5
|
+
#
|
6
|
+
class ManyCollection
|
7
|
+
extend Forwardable
|
8
|
+
include Enumerable
|
9
|
+
def_delegators :@many, :size, :each
|
10
|
+
|
11
|
+
#
|
12
|
+
# +one+ is what we are the collection for
|
13
|
+
#
|
14
|
+
def initialize(one)
|
15
|
+
@one = one
|
16
|
+
@many = Set.new
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
attr_reader :many, :one
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module DDD
|
2
|
+
module Associations
|
3
|
+
class ManyToManyCollection < ManyCollection
|
4
|
+
def <<(another_one)
|
5
|
+
unless many.include?(another_one)
|
6
|
+
many << another_one
|
7
|
+
another_one.send("#{collection_name}") << one
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def delete(another_one)
|
12
|
+
if many.include?(another_one)
|
13
|
+
many.delete(another_one)
|
14
|
+
another_one.send("#{collection_name}").delete(one)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def collection_name
|
21
|
+
one.class.name.demodulize.downcase.pluralize
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module DDD
|
2
|
+
module Associations
|
3
|
+
class OneToManyCollection < ManyCollection
|
4
|
+
def <<(another_one)
|
5
|
+
unless many.include?(another_one)
|
6
|
+
many << another_one
|
7
|
+
another_one.send("#{collection_name}=", one)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def delete(another_one)
|
12
|
+
if many.include?(another_one)
|
13
|
+
many.delete(another_one)
|
14
|
+
another_one.send("#{collection_name}=", nil)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def collection_name
|
21
|
+
one.class.name.demodulize.downcase.singularize
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ddd-associations
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Nicholas E. Rabenau
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-22 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.2'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.2'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description: Associations in pure Ruby, inspired by domain-driven design.
|
47
|
+
email:
|
48
|
+
- nerab@gmx.net
|
49
|
+
executables: []
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- .gitignore
|
54
|
+
- .travis.yml
|
55
|
+
- Gemfile
|
56
|
+
- Guardfile
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
59
|
+
- ddd-associations.gemspec
|
60
|
+
- lib/ddd-associations.rb
|
61
|
+
- lib/ddd-associations/associations.rb
|
62
|
+
- lib/ddd-associations/many_collection.rb
|
63
|
+
- lib/ddd-associations/many_to_many_collection.rb
|
64
|
+
- lib/ddd-associations/one_to_many_collection.rb
|
65
|
+
- lib/ddd-associations/version.rb
|
66
|
+
homepage:
|
67
|
+
licenses: []
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.8.24
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: DDD Associations
|
90
|
+
test_files: []
|