clean_model 0.0.5 → 0.0.6
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 +5 -5
- data/Gemfile +4 -4
- data/README.md +211 -211
- data/Rakefile +1 -1
- data/clean_model.gemspec +24 -25
- data/lib/clean_model/attribute.rb +55 -55
- data/lib/clean_model/base.rb +62 -62
- data/lib/clean_model/exceptions.rb +17 -29
- data/lib/clean_model/persistent.rb +75 -75
- data/lib/clean_model/remote.rb +75 -96
- data/lib/clean_model/version.rb +3 -3
- data/lib/clean_model.rb +11 -11
- data/spec/base_model_spec.rb +177 -177
- data/spec/persistent_model_spec.rb +111 -111
- data/spec/remote_models_spec.rb +147 -147
- data/spec/spec_helper.rb +7 -7
- data/spec/support/models/base_models.rb +36 -36
- data/spec/support/models/persistent_models.rb +9 -9
- data/spec/support/models/remote_models.rb +12 -12
- metadata +29 -20
data/spec/remote_models_spec.rb
CHANGED
@@ -1,148 +1,148 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
include RemoteModels
|
4
|
-
|
5
|
-
describe CleanModel::
|
6
|
-
|
7
|
-
context 'Successful operations' do
|
8
|
-
|
9
|
-
it 'Create' do
|
10
|
-
user = User.new first_name: 'John', last_name: 'Doe', email: 'john.doe@mail.com'
|
11
|
-
|
12
|
-
user.stub(:create) {
|
13
|
-
user.should_receive :create
|
14
|
-
|
15
|
-
stub_request(:post, 'http://localhost:9999/users/create.json').
|
16
|
-
with(body: {user: {first_name: 'John', last_name: 'Doe', email: 'john.doe@mail.com'}}).
|
17
|
-
to_return(body: {id: 1}.to_json)
|
18
|
-
|
19
|
-
user.save.should be_true
|
20
|
-
user.should be_persisted
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'Update' do
|
24
|
-
user = User.new id: 1, first_name: 'John', last_name: 'Doe', email: 'john.doe@mail.com'
|
25
|
-
|
26
|
-
user.stub(:update) {
|
27
|
-
user.should_receive :update
|
28
|
-
|
29
|
-
stub_request(:put, 'http://localhost:9999/users/1.json').
|
30
|
-
with(body: {user: {first_name: 'Jorge', last_name: 'Doe', email: 'john.doe@mail.com'}})
|
31
|
-
|
32
|
-
user.update_attributes(first_name: 'Jorge').should be_true
|
33
|
-
user.first_name.should eq 'Jorge'
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'Destroy' do
|
37
|
-
user = User.new id: 1, first_name: 'John', last_name: 'Doe', email: 'john.doe@mail.com'
|
38
|
-
|
39
|
-
user.stub(:delete) {
|
40
|
-
user.should_receive :delete
|
41
|
-
|
42
|
-
stub_request(:delete, 'http://localhost:9999/users/1.json')
|
43
|
-
|
44
|
-
user.destroy.should be_true
|
45
|
-
end
|
46
|
-
|
47
|
-
end
|
48
|
-
|
49
|
-
context 'Failed operations' do
|
50
|
-
|
51
|
-
it 'Save validation errors' do
|
52
|
-
user = User.new first_name: 'John', last_name: 'Doe'
|
53
|
-
|
54
|
-
user.stub(:create) {
|
55
|
-
|
56
|
-
stub_request(:post, 'http://localhost:9999/users/create.json').
|
57
|
-
to_return(status: 422, body: {email: ["can't be blank"]}.to_json)
|
58
|
-
|
59
|
-
user.save.should_not be_true
|
60
|
-
user.errors[:email].should have(1).items
|
61
|
-
end
|
62
|
-
|
63
|
-
it 'Save with unexpected error' do
|
64
|
-
user = User.new first_name: 'John', last_name: 'Doe', email: 'john.doe@mail.com'
|
65
|
-
|
66
|
-
user.stub(:create) {
|
67
|
-
|
68
|
-
stub_request(:post, 'http://localhost:9999/users/create.json').
|
69
|
-
to_return(status: 500, body: 'Internal Server Error')
|
70
|
-
|
71
|
-
user.save.should_not be_true
|
72
|
-
user.errors[:base].should have(1).items
|
73
|
-
end
|
74
|
-
|
75
|
-
it 'Save with timeout error' do
|
76
|
-
user = User.new first_name: 'John', last_name: 'Doe', email: 'john.doe@mail.com'
|
77
|
-
|
78
|
-
user.stub(:create) {
|
79
|
-
|
80
|
-
stub_request(:post, 'http://localhost:9999/users/create.json').to_timeout
|
81
|
-
|
82
|
-
user.save.should_not be_true
|
83
|
-
user.errors[:base].should have(1).items
|
84
|
-
end
|
85
|
-
|
86
|
-
it 'Destroy with unexpected error' do
|
87
|
-
user = User.new id: 1, first_name: 'John', last_name: 'Doe', email: 'john.doe@mail.com'
|
88
|
-
|
89
|
-
user.stub(:delete) {
|
90
|
-
|
91
|
-
stub_request(:delete, 'http://localhost:9999/users/1.json').
|
92
|
-
to_return(status: 500, body: 'Internal Server Error')
|
93
|
-
|
94
|
-
user.destroy.should_not be_true
|
95
|
-
user.errors[:base].should have(1).items
|
96
|
-
end
|
97
|
-
|
98
|
-
it 'Destroy with timeout error' do
|
99
|
-
user = User.new id: 1, first_name: 'John', last_name: 'Doe', email: 'john.doe@mail.com'
|
100
|
-
|
101
|
-
user.stub(:delete) {
|
102
|
-
|
103
|
-
stub_request(:delete, 'http://localhost:9999/users/1.json').to_timeout
|
104
|
-
|
105
|
-
user.destroy.should_not be_true
|
106
|
-
user.errors[:base].should have(1).items
|
107
|
-
end
|
108
|
-
|
109
|
-
end
|
110
|
-
|
111
|
-
context '
|
112
|
-
|
113
|
-
before :each do
|
114
|
-
User.stub(:find) do
|
115
|
-
User.
|
116
|
-
User.new JSON.parse(response.body)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
it 'Successful' do
|
122
|
-
stub_request(:get, 'http://localhost:9999/users/1.json').
|
123
|
-
to_return(body: {id: 1, first_name: 'John', last_name: 'Doe', email: 'john.doe@mail.com'}.to_json)
|
124
|
-
|
125
|
-
user = User.find(1)
|
126
|
-
|
127
|
-
user.id.should eq 1
|
128
|
-
user.first_name.should eq 'John'
|
129
|
-
user.last_name.should eq 'Doe'
|
130
|
-
user.email.should eq 'john.doe@mail.com'
|
131
|
-
end
|
132
|
-
|
133
|
-
it 'Invalid response' do
|
134
|
-
stub_request(:get, 'http://localhost:9999/users/1.json').
|
135
|
-
to_return(status: 500, body: 'Internal Server Error')
|
136
|
-
|
137
|
-
|
138
|
-
end
|
139
|
-
|
140
|
-
it 'Connection fail' do
|
141
|
-
stub_request(:get, 'http://localhost:9999/users/1.json').to_timeout
|
142
|
-
|
143
|
-
|
144
|
-
end
|
145
|
-
|
146
|
-
end
|
147
|
-
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
include RemoteModels
|
4
|
+
|
5
|
+
describe CleanModel::Remote do
|
6
|
+
|
7
|
+
context 'Successful operations' do
|
8
|
+
|
9
|
+
it 'Create' do
|
10
|
+
user = User.new first_name: 'John', last_name: 'Doe', email: 'john.doe@mail.com'
|
11
|
+
|
12
|
+
user.stub(:create) { User.connection.post('/users/create.json', body: user.send(:wrapped_attributes)) }
|
13
|
+
user.should_receive :create
|
14
|
+
|
15
|
+
stub_request(:post, 'http://localhost:9999/users/create.json').
|
16
|
+
with(body: {user: {first_name: 'John', last_name: 'Doe', email: 'john.doe@mail.com'}}).
|
17
|
+
to_return(body: {id: 1}.to_json)
|
18
|
+
|
19
|
+
user.save.should be_true
|
20
|
+
user.should be_persisted
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'Update' do
|
24
|
+
user = User.new id: 1, first_name: 'John', last_name: 'Doe', email: 'john.doe@mail.com'
|
25
|
+
|
26
|
+
user.stub(:update) { User.connection.put("/users/#{user.id}.json", body: user.send(:wrapped_attributes, except: :id)) }
|
27
|
+
user.should_receive :update
|
28
|
+
|
29
|
+
stub_request(:put, 'http://localhost:9999/users/1.json').
|
30
|
+
with(body: {user: {first_name: 'Jorge', last_name: 'Doe', email: 'john.doe@mail.com'}})
|
31
|
+
|
32
|
+
user.update_attributes(first_name: 'Jorge').should be_true
|
33
|
+
user.first_name.should eq 'Jorge'
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'Destroy' do
|
37
|
+
user = User.new id: 1, first_name: 'John', last_name: 'Doe', email: 'john.doe@mail.com'
|
38
|
+
|
39
|
+
user.stub(:delete) { User.connection.delete("/users/#{user.id}.json") }
|
40
|
+
user.should_receive :delete
|
41
|
+
|
42
|
+
stub_request(:delete, 'http://localhost:9999/users/1.json')
|
43
|
+
|
44
|
+
user.destroy.should be_true
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'Failed operations' do
|
50
|
+
|
51
|
+
it 'Save validation errors' do
|
52
|
+
user = User.new first_name: 'John', last_name: 'Doe'
|
53
|
+
|
54
|
+
user.stub(:create) { User.connection.post!('/users/create.json', user.send(:wrapped_attributes)) }
|
55
|
+
|
56
|
+
stub_request(:post, 'http://localhost:9999/users/create.json').
|
57
|
+
to_return(status: 422, body: {email: ["can't be blank"]}.to_json)
|
58
|
+
|
59
|
+
user.save.should_not be_true
|
60
|
+
user.errors[:email].should have(1).items
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'Save with unexpected error' do
|
64
|
+
user = User.new first_name: 'John', last_name: 'Doe', email: 'john.doe@mail.com'
|
65
|
+
|
66
|
+
user.stub(:create) { User.connection.post!('/users/create.json', body: user.send(:wrapped_attributes)) }
|
67
|
+
|
68
|
+
stub_request(:post, 'http://localhost:9999/users/create.json').
|
69
|
+
to_return(status: 500, body: 'Internal Server Error')
|
70
|
+
|
71
|
+
user.save.should_not be_true
|
72
|
+
user.errors[:base].should have(1).items
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'Save with timeout error' do
|
76
|
+
user = User.new first_name: 'John', last_name: 'Doe', email: 'john.doe@mail.com'
|
77
|
+
|
78
|
+
user.stub(:create) { User.connection.post!('/users/create.json', body: user.send(:wrapped_attributes)) }
|
79
|
+
|
80
|
+
stub_request(:post, 'http://localhost:9999/users/create.json').to_timeout
|
81
|
+
|
82
|
+
user.save.should_not be_true
|
83
|
+
user.errors[:base].should have(1).items
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'Destroy with unexpected error' do
|
87
|
+
user = User.new id: 1, first_name: 'John', last_name: 'Doe', email: 'john.doe@mail.com'
|
88
|
+
|
89
|
+
user.stub(:delete) { User.connection.delete!("/users/#{user.id}.json") }
|
90
|
+
|
91
|
+
stub_request(:delete, 'http://localhost:9999/users/1.json').
|
92
|
+
to_return(status: 500, body: 'Internal Server Error')
|
93
|
+
|
94
|
+
user.destroy.should_not be_true
|
95
|
+
user.errors[:base].should have(1).items
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'Destroy with timeout error' do
|
99
|
+
user = User.new id: 1, first_name: 'John', last_name: 'Doe', email: 'john.doe@mail.com'
|
100
|
+
|
101
|
+
user.stub(:delete) { User.connection.delete!("/users/#{user.id}.json") }
|
102
|
+
|
103
|
+
stub_request(:delete, 'http://localhost:9999/users/1.json').to_timeout
|
104
|
+
|
105
|
+
user.destroy.should_not be_true
|
106
|
+
user.errors[:base].should have(1).items
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'Safe requests' do
|
112
|
+
|
113
|
+
before :each do
|
114
|
+
User.stub(:find) do
|
115
|
+
User.connection.get '/users/1.json' do |response|
|
116
|
+
User.new JSON.parse(response.body)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'Successful' do
|
122
|
+
stub_request(:get, 'http://localhost:9999/users/1.json').
|
123
|
+
to_return(body: {id: 1, first_name: 'John', last_name: 'Doe', email: 'john.doe@mail.com'}.to_json)
|
124
|
+
|
125
|
+
user = User.find(1)
|
126
|
+
|
127
|
+
user.id.should eq 1
|
128
|
+
user.first_name.should eq 'John'
|
129
|
+
user.last_name.should eq 'Doe'
|
130
|
+
user.email.should eq 'john.doe@mail.com'
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'Invalid response' do
|
134
|
+
stub_request(:get, 'http://localhost:9999/users/1.json').
|
135
|
+
to_return(status: 500, body: 'Internal Server Error')
|
136
|
+
|
137
|
+
User.find(1).should be_nil
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'Connection fail' do
|
141
|
+
stub_request(:get, 'http://localhost:9999/users/1.json').to_timeout
|
142
|
+
|
143
|
+
User.find(1).should be_nil
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
148
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
require 'webmock/rspec'
|
2
|
-
require 'clean_model'
|
3
|
-
|
4
|
-
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
5
|
-
|
6
|
-
RSpec.configure do |config|
|
7
|
-
|
1
|
+
require 'webmock/rspec'
|
2
|
+
require 'clean_model'
|
3
|
+
|
4
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
5
|
+
|
6
|
+
RSpec.configure do |config|
|
7
|
+
|
8
8
|
end
|
@@ -1,37 +1,37 @@
|
|
1
|
-
module BaseModels
|
2
|
-
|
3
|
-
class Person
|
4
|
-
include CleanModel::Base
|
5
|
-
|
6
|
-
attribute :first_name
|
7
|
-
attribute :last_name
|
8
|
-
attribute :nationality, default: :argentina
|
9
|
-
attribute :age, default: -> { Time.now.year - 1979 }
|
10
|
-
|
11
|
-
validates_presence_of :first_name, :last_name
|
12
|
-
end
|
13
|
-
|
14
|
-
class Engine
|
15
|
-
include CleanModel::Base
|
16
|
-
|
17
|
-
attribute :power, class_name: :numeric
|
18
|
-
attribute :cylinders, class_name: :integer
|
19
|
-
attribute :valves, class_name: 'Integer'
|
20
|
-
end
|
21
|
-
|
22
|
-
class Car
|
23
|
-
include CleanModel::Base
|
24
|
-
|
25
|
-
attribute :brand
|
26
|
-
attribute :model
|
27
|
-
attribute :engine, class_name: 'BaseModels::Engine'
|
28
|
-
attribute :comfort, transformation: lambda { |v| v.is_a?(String) ? v.split(',').map(&:strip) : v }
|
29
|
-
end
|
30
|
-
|
31
|
-
class Factory
|
32
|
-
include CleanModel::Base
|
33
|
-
|
34
|
-
attribute :cars, collection: 'BaseModels::Car'
|
35
|
-
end
|
36
|
-
|
1
|
+
module BaseModels
|
2
|
+
|
3
|
+
class Person
|
4
|
+
include CleanModel::Base
|
5
|
+
|
6
|
+
attribute :first_name
|
7
|
+
attribute :last_name
|
8
|
+
attribute :nationality, default: :argentina
|
9
|
+
attribute :age, default: -> { Time.now.year - 1979 }
|
10
|
+
|
11
|
+
validates_presence_of :first_name, :last_name
|
12
|
+
end
|
13
|
+
|
14
|
+
class Engine
|
15
|
+
include CleanModel::Base
|
16
|
+
|
17
|
+
attribute :power, class_name: :numeric
|
18
|
+
attribute :cylinders, class_name: :integer
|
19
|
+
attribute :valves, class_name: 'Integer'
|
20
|
+
end
|
21
|
+
|
22
|
+
class Car
|
23
|
+
include CleanModel::Base
|
24
|
+
|
25
|
+
attribute :brand
|
26
|
+
attribute :model
|
27
|
+
attribute :engine, class_name: 'BaseModels::Engine'
|
28
|
+
attribute :comfort, transformation: lambda { |v| v.is_a?(String) ? v.split(',').map(&:strip) : v }
|
29
|
+
end
|
30
|
+
|
31
|
+
class Factory
|
32
|
+
include CleanModel::Base
|
33
|
+
|
34
|
+
attribute :cars, collection: 'BaseModels::Car'
|
35
|
+
end
|
36
|
+
|
37
37
|
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
module PersistentModels
|
2
|
-
|
3
|
-
class Post
|
4
|
-
include CleanModel::Persistent
|
5
|
-
|
6
|
-
attribute :subject
|
7
|
-
attribute :content
|
8
|
-
end
|
9
|
-
|
1
|
+
module PersistentModels
|
2
|
+
|
3
|
+
class Post
|
4
|
+
include CleanModel::Persistent
|
5
|
+
|
6
|
+
attribute :subject
|
7
|
+
attribute :content
|
8
|
+
end
|
9
|
+
|
10
10
|
end
|
@@ -1,13 +1,13 @@
|
|
1
|
-
module RemoteModels
|
2
|
-
|
3
|
-
class User
|
4
|
-
include CleanModel::Remote
|
5
|
-
|
6
|
-
connection host: 'localhost', port: 9999
|
7
|
-
|
8
|
-
attribute :first_name
|
9
|
-
attribute :last_name
|
10
|
-
attribute :email
|
11
|
-
end
|
12
|
-
|
1
|
+
module RemoteModels
|
2
|
+
|
3
|
+
class User
|
4
|
+
include CleanModel::Remote
|
5
|
+
|
6
|
+
connection host: 'localhost', port: 9999
|
7
|
+
|
8
|
+
attribute :first_name
|
9
|
+
attribute :last_name
|
10
|
+
attribute :email
|
11
|
+
end
|
12
|
+
|
13
13
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clean_model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-12-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
16
|
-
requirement:
|
15
|
+
name: activemodel
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,32 +21,31 @@ dependencies:
|
|
21
21
|
version: 3.0.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
25
|
-
- !ruby/object:Gem::Dependency
|
26
|
-
name: activemodel
|
27
|
-
requirement: &2924028 !ruby/object:Gem::Requirement
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
25
|
none: false
|
29
26
|
requirements:
|
30
27
|
- - ! '>='
|
31
28
|
- !ruby/object:Gem::Version
|
32
29
|
version: 3.0.0
|
33
|
-
type: :runtime
|
34
|
-
prerelease: false
|
35
|
-
version_requirements: *2924028
|
36
30
|
- !ruby/object:Gem::Dependency
|
37
31
|
name: web_client
|
38
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
39
33
|
none: false
|
40
34
|
requirements:
|
41
|
-
- - =
|
35
|
+
- - '='
|
42
36
|
- !ruby/object:Gem::Version
|
43
|
-
version: 0.0.
|
37
|
+
version: 0.0.4
|
44
38
|
type: :runtime
|
45
39
|
prerelease: false
|
46
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - '='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.0.4
|
47
46
|
- !ruby/object:Gem::Dependency
|
48
47
|
name: rspec
|
49
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
50
49
|
none: false
|
51
50
|
requirements:
|
52
51
|
- - ! '>='
|
@@ -54,10 +53,15 @@ dependencies:
|
|
54
53
|
version: '0'
|
55
54
|
type: :development
|
56
55
|
prerelease: false
|
57
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
58
62
|
- !ruby/object:Gem::Dependency
|
59
63
|
name: webmock
|
60
|
-
requirement:
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
61
65
|
none: false
|
62
66
|
requirements:
|
63
67
|
- - ! '>='
|
@@ -65,7 +69,12 @@ dependencies:
|
|
65
69
|
version: '0'
|
66
70
|
type: :development
|
67
71
|
prerelease: false
|
68
|
-
version_requirements:
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
69
78
|
description: Extensions for ActiveModel to implement multiple types of models
|
70
79
|
email:
|
71
80
|
- gabynaiman@gmail.com
|
@@ -112,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
121
|
version: '0'
|
113
122
|
requirements: []
|
114
123
|
rubyforge_project:
|
115
|
-
rubygems_version: 1.8.
|
124
|
+
rubygems_version: 1.8.24
|
116
125
|
signing_key:
|
117
126
|
specification_version: 3
|
118
127
|
summary: Extensions for ActiveModel to implement multiple types of models
|