modelling 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 +4 -0
- data/.rspec +1 -0
- data/Gemfile +3 -0
- data/MIT-LICENSE +19 -0
- data/README +64 -0
- data/Rakefile +6 -0
- data/lib/modelling.rb +78 -0
- data/lib/modelling/version.rb +3 -0
- data/modelling.gemspec +24 -0
- data/spec/modelling_spec.rb +153 -0
- metadata +94 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2007-2012 Ryan Allen, Ian Leitch, John Barton, Mark Turnley, Steve Hodgkiss, FlashDen Pty Ltd
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
This module, when included, enforces the typical instantiation pattern
|
2
|
+
that accepts a hash of attributes, like so:
|
3
|
+
|
4
|
+
class Car
|
5
|
+
include Modelling
|
6
|
+
attr_accessor :make
|
7
|
+
attr_accessor :model
|
8
|
+
end
|
9
|
+
|
10
|
+
car = Car.new(:make => 'Lotus', :model => 'Elise')
|
11
|
+
car.make # => 'Lotus'
|
12
|
+
car.model # => 'Elise'
|
13
|
+
|
14
|
+
Additionally you get three meta methods for making your so called domain
|
15
|
+
modelling a little more concise, these three methods are:
|
16
|
+
|
17
|
+
* attributes
|
18
|
+
* collections
|
19
|
+
* maps
|
20
|
+
* structs
|
21
|
+
|
22
|
+
Attributes simply is an alias for attr_accessor, collections defines accessors
|
23
|
+
that are arrays, and maps defines hashes. The constructor ensures that each
|
24
|
+
map and collection are initalized as [] and {} respectively by default:
|
25
|
+
|
26
|
+
class Car
|
27
|
+
include Modelling
|
28
|
+
attributes :make, :model
|
29
|
+
collections :tyres, :accessories
|
30
|
+
maps :dealer_locations, :telephone_services
|
31
|
+
end
|
32
|
+
|
33
|
+
car = Car.new
|
34
|
+
car.make # => nil
|
35
|
+
car.model # => nil
|
36
|
+
car.tyres # => []
|
37
|
+
car.accessories # => []
|
38
|
+
car.dealer_locations # => {}
|
39
|
+
car.telephone_services # => {}
|
40
|
+
|
41
|
+
Structs are as the name suggests, structs. Or more specifically OpenStructs:
|
42
|
+
|
43
|
+
class Car
|
44
|
+
include Modelling
|
45
|
+
structs :features
|
46
|
+
end
|
47
|
+
|
48
|
+
car.features.abs true
|
49
|
+
car.features.air_con true
|
50
|
+
car.features.valves 8
|
51
|
+
|
52
|
+
TODO: WRITE DOCS FOR NEW CUSTOM INITIALIZERS!!!1 (AND UPDATE DOCS ABOVE,
|
53
|
+
THEY ARE LIKE OUT OF DATE OR SOMETHING).
|
54
|
+
i.e. collections :categories => CategoryCollection
|
55
|
+
and
|
56
|
+
attributes :total => Proc.new { Money.new(0) }
|
57
|
+
and
|
58
|
+
maps :domain => CustomDNSWithHashLookupAwesome
|
59
|
+
|
60
|
+
This makes modelling with PORO's [1] more eloquent, and stops us from
|
61
|
+
having to write and re-write constructors that accept hashes, as
|
62
|
+
is typically done.
|
63
|
+
|
64
|
+
[1] http://en.wikipedia.org/wiki/POJO
|
data/Rakefile
ADDED
data/lib/modelling.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'modelling/version'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module Modelling
|
5
|
+
|
6
|
+
def self.included(receiver)
|
7
|
+
receiver.extend ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def inherited(descendant)
|
12
|
+
descendant.instance_variable_set(:@members, members.dup)
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def attributes(*args)
|
17
|
+
generate_accessors_from_args(args, Proc.new { nil })
|
18
|
+
end
|
19
|
+
|
20
|
+
def collections(*args)
|
21
|
+
generate_accessors_from_args(args, Proc.new { Array.new })
|
22
|
+
end
|
23
|
+
|
24
|
+
def maps(*args)
|
25
|
+
generate_accessors_from_args(args, Proc.new { Hash.new })
|
26
|
+
end
|
27
|
+
|
28
|
+
def structs(*args)
|
29
|
+
generate_accessors_from_args(args, Proc.new { OpenStruct.new })
|
30
|
+
end
|
31
|
+
|
32
|
+
def members
|
33
|
+
@members ||= {}
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def generate_accessors_from_args(args, default_initializer)
|
39
|
+
names_to_initializer = args.last.is_a?(Hash) ? args.pop : {}
|
40
|
+
args.each do |name|
|
41
|
+
names_to_initializer[name] = default_initializer
|
42
|
+
end
|
43
|
+
generate_accessors(names_to_initializer)
|
44
|
+
end
|
45
|
+
|
46
|
+
def generate_accessors(names_to_initializer)
|
47
|
+
names_to_initializer.each do |name, initializer|
|
48
|
+
create_accessor(name)
|
49
|
+
if initializer.is_a?(Proc)
|
50
|
+
members[name] = initializer
|
51
|
+
else
|
52
|
+
members[name] = Proc.new { initializer.new }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def create_accessor(name)
|
58
|
+
instance_eval { attr_accessor name }
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
def members
|
64
|
+
self.class.members
|
65
|
+
end
|
66
|
+
|
67
|
+
def initialize(args = {})
|
68
|
+
members.each do |accessor, initializer|
|
69
|
+
if initializer.arity > 0
|
70
|
+
send "#{accessor}=", initializer.call(self)
|
71
|
+
else
|
72
|
+
send "#{accessor}=", initializer.call
|
73
|
+
end
|
74
|
+
end
|
75
|
+
args.each { |name, value| send "#{name}=", value }
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
data/modelling.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'modelling/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'modelling'
|
7
|
+
s.version = Modelling::VERSION
|
8
|
+
s.authors = ['Ryan Allen', 'Steve Hodgkiss', 'Mark Turnley', 'John Barton']
|
9
|
+
s.email = ['ryan@eden.cc', 'steve@hodgkiss.me', 'ravagedcarrot@gmail.com', 'jrbarton@gmail.com']
|
10
|
+
s.homepage = 'http://github.com/ryan-allen/modelling'
|
11
|
+
s.summary = %q{Wraps some common-ish plain-ruby object modelling junk.}
|
12
|
+
s.description = %q{We were doing PORO dev on Rails before it was hip.}
|
13
|
+
|
14
|
+
s.rubyforge_project = 'modelling'
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ['lib']
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
# s.add_development_dependency 'rspec'
|
23
|
+
s.add_runtime_dependency 'rspec'
|
24
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.require
|
3
|
+
$: << '.'
|
4
|
+
require 'modelling'
|
5
|
+
|
6
|
+
class User
|
7
|
+
include Modelling
|
8
|
+
attributes :name, :age
|
9
|
+
collections :fav_colours, :biggest_gripes, :test => lambda { 3 }
|
10
|
+
end
|
11
|
+
|
12
|
+
class NonSpecificUser < User
|
13
|
+
end
|
14
|
+
|
15
|
+
class MyArray < Array; end
|
16
|
+
class MyHash < Hash; end
|
17
|
+
|
18
|
+
class Car
|
19
|
+
include Modelling
|
20
|
+
attributes :name => Proc.new { |car| String.new(car.class.to_s) }
|
21
|
+
collections :doors => MyArray
|
22
|
+
end
|
23
|
+
|
24
|
+
class SuperCar < Car
|
25
|
+
attributes :bhp => Proc.new { 400 }
|
26
|
+
end
|
27
|
+
|
28
|
+
class Site
|
29
|
+
include Modelling
|
30
|
+
maps :users => MyHash
|
31
|
+
end
|
32
|
+
|
33
|
+
class Bike
|
34
|
+
include Modelling
|
35
|
+
attributes :manufacturer
|
36
|
+
maps :stickers
|
37
|
+
structs :features
|
38
|
+
end
|
39
|
+
|
40
|
+
class LambdaTest
|
41
|
+
include Modelling
|
42
|
+
attributes :lambda => lambda { "boo" }
|
43
|
+
end
|
44
|
+
|
45
|
+
describe Modelling do
|
46
|
+
|
47
|
+
specify 'user has name' do
|
48
|
+
user = User.new
|
49
|
+
user.name = 'Ryan'
|
50
|
+
user.name.should eq 'Ryan'
|
51
|
+
end
|
52
|
+
|
53
|
+
specify 'user has age' do
|
54
|
+
user = User.new
|
55
|
+
user.age = 24
|
56
|
+
user.age.should eq 24
|
57
|
+
end
|
58
|
+
|
59
|
+
specify 'user has fav colours collection' do
|
60
|
+
user = User.new
|
61
|
+
user.fav_colours = [:green, :yellow]
|
62
|
+
user.fav_colours.should eq [:green, :yellow]
|
63
|
+
end
|
64
|
+
|
65
|
+
specify 'user has biggest gripes collection' do
|
66
|
+
user = User.new
|
67
|
+
user.biggest_gripes = [:mediocrity, :CUB_beer]
|
68
|
+
user.biggest_gripes.should eq [:mediocrity, :CUB_beer]
|
69
|
+
end
|
70
|
+
|
71
|
+
specify 'attributes are initialized as nil' do
|
72
|
+
user = User.new
|
73
|
+
user.name.should be_nil
|
74
|
+
user.age.should be_nil
|
75
|
+
end
|
76
|
+
|
77
|
+
specify 'collections are initialized as empty array' do
|
78
|
+
user = User.new
|
79
|
+
user.fav_colours.should be_empty
|
80
|
+
user.biggest_gripes.should be_empty
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'can initialize with attributes' do
|
84
|
+
user = User.new(:name => 'Ryan')
|
85
|
+
user.name.should eq 'Ryan'
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'can initialize with collections' do
|
89
|
+
user = User.new(:biggest_gripes => [:mediocrity, :CUB_beer])
|
90
|
+
user.biggest_gripes.should eq [:mediocrity, :CUB_beer]
|
91
|
+
end
|
92
|
+
|
93
|
+
specify 'car has doors and all is good' do
|
94
|
+
car = Car.new
|
95
|
+
car.doors.should be_empty
|
96
|
+
end
|
97
|
+
|
98
|
+
specify 'bike does not bunk on having no collection' do
|
99
|
+
bike = Bike.new(:manufacturer => 'Kawasaki')
|
100
|
+
# if nothing is rased, we fixed the bug
|
101
|
+
end
|
102
|
+
|
103
|
+
specify 'bike has map of stickers' do
|
104
|
+
Bike.new.stickers.should be_empty
|
105
|
+
end
|
106
|
+
|
107
|
+
specify 'cars doors is a my array' do
|
108
|
+
Car.new.doors.should be_instance_of MyArray
|
109
|
+
end
|
110
|
+
|
111
|
+
specify 'sites users is a my hash' do
|
112
|
+
Site.new.users.should be_instance_of MyHash
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'can initialize with proc and get reference to new instance' do
|
116
|
+
car = Car.new
|
117
|
+
car.name.should eq String.new(car.class.to_s)
|
118
|
+
end
|
119
|
+
|
120
|
+
specify 'bike has features' do
|
121
|
+
bike = Bike.new
|
122
|
+
bike.features.should be_instance_of OpenStruct
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'doesnt fail when lambdas with no args are used' do
|
126
|
+
LambdaTest.new.lambda.should eq 'boo'
|
127
|
+
end
|
128
|
+
|
129
|
+
context 'inheritence' do
|
130
|
+
let(:car) { Car.new }
|
131
|
+
let(:super_car) { SuperCar.new }
|
132
|
+
|
133
|
+
it 'inherits attributes' do
|
134
|
+
super_car.doors.should be_instance_of MyArray
|
135
|
+
super_car.name.should eq "SuperCar"
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'allows extra attributes' do
|
139
|
+
super_car.bhp.should eq 400
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'doesnt mess up the super classes members' do
|
143
|
+
car.should_not respond_to(:bhp)
|
144
|
+
car.should respond_to(:doors)
|
145
|
+
car.should respond_to(:name)
|
146
|
+
end
|
147
|
+
|
148
|
+
specify 'non specific user inherits attributes' do
|
149
|
+
user = NonSpecificUser.new(:name => 'Ryan you cocktard')
|
150
|
+
user.name.should eq 'Ryan you cocktard'
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: modelling
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Ryan Allen
|
14
|
+
- Steve Hodgkiss
|
15
|
+
- Mark Turnley
|
16
|
+
- John Barton
|
17
|
+
autorequire:
|
18
|
+
bindir: bin
|
19
|
+
cert_chain: []
|
20
|
+
|
21
|
+
date: 2012-02-03 00:00:00 Z
|
22
|
+
dependencies:
|
23
|
+
- !ruby/object:Gem::Dependency
|
24
|
+
name: rspec
|
25
|
+
prerelease: false
|
26
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
27
|
+
none: false
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
hash: 3
|
32
|
+
segments:
|
33
|
+
- 0
|
34
|
+
version: "0"
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
description: We were doing PORO dev on Rails before it was hip.
|
38
|
+
email:
|
39
|
+
- ryan@eden.cc
|
40
|
+
- steve@hodgkiss.me
|
41
|
+
- ravagedcarrot@gmail.com
|
42
|
+
- jrbarton@gmail.com
|
43
|
+
executables: []
|
44
|
+
|
45
|
+
extensions: []
|
46
|
+
|
47
|
+
extra_rdoc_files: []
|
48
|
+
|
49
|
+
files:
|
50
|
+
- .gitignore
|
51
|
+
- .rspec
|
52
|
+
- Gemfile
|
53
|
+
- MIT-LICENSE
|
54
|
+
- README
|
55
|
+
- Rakefile
|
56
|
+
- lib/modelling.rb
|
57
|
+
- lib/modelling/version.rb
|
58
|
+
- modelling.gemspec
|
59
|
+
- spec/modelling_spec.rb
|
60
|
+
homepage: http://github.com/ryan-allen/modelling
|
61
|
+
licenses: []
|
62
|
+
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
hash: 3
|
74
|
+
segments:
|
75
|
+
- 0
|
76
|
+
version: "0"
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
hash: 3
|
83
|
+
segments:
|
84
|
+
- 0
|
85
|
+
version: "0"
|
86
|
+
requirements: []
|
87
|
+
|
88
|
+
rubyforge_project: modelling
|
89
|
+
rubygems_version: 1.8.10
|
90
|
+
signing_key:
|
91
|
+
specification_version: 3
|
92
|
+
summary: Wraps some common-ish plain-ruby object modelling junk.
|
93
|
+
test_files:
|
94
|
+
- spec/modelling_spec.rb
|