super_model 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +68 -0
- data/Guardfile +11 -0
- data/LICENSE +20 -0
- data/README.md +31 -0
- data/Rakefile +59 -0
- data/VERSION +1 -0
- data/examples/associations_example.rb +37 -0
- data/examples/validations_example.rb +12 -0
- data/lib/super_model.rb +19 -0
- data/lib/super_model/associations.rb +47 -0
- data/lib/super_model/associations/belongs_to.rb +63 -0
- data/lib/super_model/associations/has_many.rb +32 -0
- data/lib/super_model/associations/has_one.rb +32 -0
- data/lib/super_model/error.rb +8 -0
- data/lib/super_model/error/hash_type.rb +21 -0
- data/lib/super_model/error/integer_type.rb +12 -0
- data/lib/super_model/error/invalid_plurality.rb +19 -0
- data/lib/super_model/error/proc_type.rb +12 -0
- data/lib/super_model/error/type.rb +52 -0
- data/lib/super_model/validations.rb +55 -0
- data/lib/super_model/validations/acceptance.rb +11 -0
- data/lib/super_model/validations/confirmation.rb +11 -0
- data/lib/super_model/validations/date.rb +11 -0
- data/lib/super_model/validations/date_time.rb +11 -0
- data/lib/super_model/validations/exclusion.rb +11 -0
- data/lib/super_model/validations/format.rb +11 -0
- data/lib/super_model/validations/inclusion.rb +11 -0
- data/lib/super_model/validations/length.rb +11 -0
- data/lib/super_model/validations/numericality.rb +11 -0
- data/lib/super_model/validations/presence.rb +11 -0
- data/lib/super_model/validations/time.rb +11 -0
- data/lib/super_model/validations/uniqueness.rb +11 -0
- data/lib/super_model/validator.rb +13 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/super_model/associations/belongs_to_spec.rb +30 -0
- data/super_model.gemspec +50 -0
- data/tmp/rspec_guard_result +1 -0
- metadata +216 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use --create 1.9.3
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
super_model (0.0.1pre)
|
5
|
+
active_support (~> 3.0.0)
|
6
|
+
version (~> 1.0.0)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: http://rubygems.org/
|
10
|
+
specs:
|
11
|
+
active_support (3.0.0)
|
12
|
+
activesupport (= 3.0.0)
|
13
|
+
activesupport (3.0.0)
|
14
|
+
coderay (1.0.8)
|
15
|
+
diff-lcs (1.1.3)
|
16
|
+
fuubar (1.1.0)
|
17
|
+
rspec (~> 2.0)
|
18
|
+
rspec-instafail (~> 0.2.0)
|
19
|
+
ruby-progressbar (~> 1.0.0)
|
20
|
+
github-markup (0.7.5)
|
21
|
+
guard (1.6.2)
|
22
|
+
listen (>= 0.6.0)
|
23
|
+
lumberjack (>= 1.0.2)
|
24
|
+
pry (>= 0.9.10)
|
25
|
+
terminal-table (>= 1.4.3)
|
26
|
+
thor (>= 0.14.6)
|
27
|
+
guard-rspec (2.4.1)
|
28
|
+
guard (>= 1.1)
|
29
|
+
rspec (~> 2.11)
|
30
|
+
guard-yard (2.0.1)
|
31
|
+
guard (>= 1.1.0)
|
32
|
+
yard (>= 0.7.0)
|
33
|
+
listen (0.7.2)
|
34
|
+
lumberjack (1.0.2)
|
35
|
+
method_source (0.8.1)
|
36
|
+
pry (0.9.12)
|
37
|
+
coderay (~> 1.0.5)
|
38
|
+
method_source (~> 0.8)
|
39
|
+
slop (~> 3.4)
|
40
|
+
rb-fsevent (0.9.3)
|
41
|
+
redcarpet (2.2.2)
|
42
|
+
rspec (2.12.0)
|
43
|
+
rspec-core (~> 2.12.0)
|
44
|
+
rspec-expectations (~> 2.12.0)
|
45
|
+
rspec-mocks (~> 2.12.0)
|
46
|
+
rspec-core (2.12.2)
|
47
|
+
rspec-expectations (2.12.1)
|
48
|
+
diff-lcs (~> 1.1.3)
|
49
|
+
rspec-instafail (0.2.4)
|
50
|
+
rspec-mocks (2.12.2)
|
51
|
+
ruby-progressbar (1.0.2)
|
52
|
+
slop (3.4.3)
|
53
|
+
terminal-table (1.4.5)
|
54
|
+
thor (0.17.0)
|
55
|
+
version (1.0.0)
|
56
|
+
yard (0.8.4.1)
|
57
|
+
|
58
|
+
PLATFORMS
|
59
|
+
ruby
|
60
|
+
|
61
|
+
DEPENDENCIES
|
62
|
+
fuubar (~> 1.1.0)
|
63
|
+
github-markup (~> 0.7.5)
|
64
|
+
guard-rspec (~> 2.4.1)
|
65
|
+
guard-yard (~> 2.0.1)
|
66
|
+
rb-fsevent (~> 0.9.3)
|
67
|
+
redcarpet (~> 2.2.2)
|
68
|
+
super_model!
|
data/Guardfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
guard 'yard' do
|
2
|
+
watch(%r{app/.+\.rb})
|
3
|
+
watch(%r{lib/.+\.rb})
|
4
|
+
watch(%r{ext/.+\.c})
|
5
|
+
end
|
6
|
+
|
7
|
+
guard 'rspec', cli: '-c -f Fuubar' do
|
8
|
+
watch(%r{^spec/.+_spec\.rb$})
|
9
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
10
|
+
watch('spec/spec_helper.rb') { "spec" }
|
11
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Ryan Scott Lewis <ryan@rynet.us>
|
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,31 @@
|
|
1
|
+
# SuperModel
|
2
|
+
|
3
|
+
* [Gem](https://rubygems.org/gems/super_model)
|
4
|
+
* [Source](https://github.com/RyanScottLewis/at)
|
5
|
+
* [Documentation](http://rubydoc.info/gems/service/frames)
|
6
|
+
|
7
|
+
A fully functional alternative to `ActiveModel`
|
8
|
+
|
9
|
+
## Install
|
10
|
+
|
11
|
+
### Bundler: `gem 'super_model'`
|
12
|
+
|
13
|
+
### RubyGems: `gem install super_mode`
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
## Contributing
|
18
|
+
|
19
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
20
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
21
|
+
* Fork the project
|
22
|
+
* Start a feature/bugfix branch
|
23
|
+
* Commit and push until you are happy with your contribution
|
24
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
25
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
26
|
+
|
27
|
+
## Copyright
|
28
|
+
|
29
|
+
Copyright © 2013 Ryan Scott Lewis <ryan@rynet.us>.
|
30
|
+
|
31
|
+
The MIT License (MIT) - See LICENSE for further details.
|
data/Rakefile
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
def require_task(path)
|
4
|
+
begin
|
5
|
+
require path
|
6
|
+
|
7
|
+
yield
|
8
|
+
rescue LoadError
|
9
|
+
puts '', "Could not load '#{path}'.", 'Try to `rake gem:spec` and `bundle install` and try again.', ''
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
spec = Gem::Specification.new do |s|
|
14
|
+
|
15
|
+
# Variables
|
16
|
+
s.name = 'super_model'
|
17
|
+
s.author = 'Ryan Scott Lewis'
|
18
|
+
s.email = 'ryan@rynet.us'
|
19
|
+
s.summary = ''
|
20
|
+
s.description = ''
|
21
|
+
|
22
|
+
# Dependencies
|
23
|
+
s.add_dependency 'version', '~> 1.0.0'
|
24
|
+
s.add_dependency 'active_support', '~> 3.0.0'
|
25
|
+
s.add_development_dependency 'guard-rspec', '~> 2.4.1'
|
26
|
+
s.add_development_dependency 'guard-yard', '~> 2.0.1'
|
27
|
+
s.add_development_dependency 'rb-fsevent', '~> 0.9.3'
|
28
|
+
s.add_development_dependency 'fuubar', '~> 1.1.0'
|
29
|
+
s.add_development_dependency 'redcarpet', '~> 2.2.2'
|
30
|
+
s.add_development_dependency 'github-markup', '~> 0.7.5'
|
31
|
+
|
32
|
+
# Pragmatically set variables
|
33
|
+
s.homepage = "http://github.com/RyanScottLewis/#{s.name}"
|
34
|
+
s.version = Pathname.glob('VERSION*').first.read
|
35
|
+
s.require_paths = ['lib']
|
36
|
+
s.files = `git ls-files`.lines.to_a.collect { |s| s.strip }
|
37
|
+
s.executables = `git ls-files -- bin/*`.lines.to_a.collect { |s| File.basename(s.strip) }
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
desc 'Generate the gemspec defined in this Rakefile'
|
42
|
+
task :gemspec do
|
43
|
+
Pathname.new("#{spec.name}.gemspec").open('w') { |f| f.write(spec.to_ruby) }
|
44
|
+
end
|
45
|
+
|
46
|
+
require_task 'rake/version_task' do
|
47
|
+
Rake::VersionTask.new do |t|
|
48
|
+
t.with_git_tag = true
|
49
|
+
t.with_gemspec = spec
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
require 'rubygems/package_task'
|
54
|
+
Gem::PackageTask.new(spec) do |t|
|
55
|
+
t.need_zip = false
|
56
|
+
t.need_tar = false
|
57
|
+
end
|
58
|
+
|
59
|
+
task :default => :gemspec
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'super_model'
|
3
|
+
|
4
|
+
class User < SuperModel
|
5
|
+
|
6
|
+
has_one :photo
|
7
|
+
has_many :photos
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
class Photo < SuperModel
|
12
|
+
|
13
|
+
belongs_to :user
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
p1 = Photo.new
|
19
|
+
p2 = Photo.new
|
20
|
+
|
21
|
+
u = User.new( photo: p1, photos: [p1, p2] )
|
22
|
+
|
23
|
+
p u.photo.user
|
24
|
+
|
25
|
+
p u.photo == p1
|
26
|
+
|
27
|
+
u.photo_id = p2.object_id # TODO: Wait.. this is wrong.. the BelongsTo association holds the ASSOCIATION_id methods....
|
28
|
+
|
29
|
+
p u.photo == p2
|
30
|
+
|
31
|
+
p u.photos
|
32
|
+
|
33
|
+
u.photos = [p1, p2]
|
34
|
+
|
35
|
+
p u.photos
|
36
|
+
|
37
|
+
p u.photo_ids
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'super_model'
|
3
|
+
|
4
|
+
# class User < SuperModel
|
5
|
+
#
|
6
|
+
# end
|
7
|
+
|
8
|
+
require 'super_model/validations/uniqueness'
|
9
|
+
require 'super_model/validations/length'
|
10
|
+
require 'super_model/validations/format'
|
11
|
+
|
12
|
+
p SuperModel::Validations.validators
|
data/lib/super_model.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'version'
|
2
|
+
require 'super_model/validations'
|
3
|
+
require 'super_model/associations'
|
4
|
+
|
5
|
+
# TODO: SuperModel DESCRIPTION HERE!
|
6
|
+
#
|
7
|
+
# Objects can subclass SuperModel.
|
8
|
+
class SuperModel
|
9
|
+
|
10
|
+
is_versioned
|
11
|
+
|
12
|
+
extend SuperModel::Associations
|
13
|
+
extend SuperModel::Validations
|
14
|
+
|
15
|
+
def initialize(attributes={})
|
16
|
+
attributes.each { |name, value| send("#{name}=", value) }
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'active_support/core_ext/string/inflections'
|
2
|
+
require 'super_model/associations/belongs_to'
|
3
|
+
require 'super_model/associations/has_many'
|
4
|
+
require 'super_model/associations/has_one'
|
5
|
+
require 'super_model/error/invalid_plurality'
|
6
|
+
|
7
|
+
class SuperModel
|
8
|
+
|
9
|
+
# SuperModel::Associations adds associations for standard Ruby objects and
|
10
|
+
# provides a base for ORMs to add their own association functionality.
|
11
|
+
#
|
12
|
+
# This module must be included in a Class.
|
13
|
+
module Associations
|
14
|
+
|
15
|
+
class << self
|
16
|
+
|
17
|
+
def sanitize_resource_name(resource_name, plurality)
|
18
|
+
Error::InvalidPlurality.check(plurality)
|
19
|
+
|
20
|
+
resource_name.to_s.send("#{ plurality }ize").underscore
|
21
|
+
end
|
22
|
+
|
23
|
+
def foreign_key_for(resource_name, plurality)
|
24
|
+
Error::InvalidPlurality.check(plurality)
|
25
|
+
|
26
|
+
"#{resource_name}_id".send("#{ plurality }ize")
|
27
|
+
end
|
28
|
+
|
29
|
+
def extend_with_associations(receiver)
|
30
|
+
receiver.extend(HasOne)
|
31
|
+
receiver.extend(HasMany)
|
32
|
+
receiver.extend(BelongsTo)
|
33
|
+
end
|
34
|
+
|
35
|
+
def included(receiver)
|
36
|
+
extend_with_associations(receiver)
|
37
|
+
end
|
38
|
+
|
39
|
+
def extended(receiver)
|
40
|
+
extend_with_associations(receiver)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'super_model/associations'
|
2
|
+
require 'super_model/error/hash_type'
|
3
|
+
require 'super_model/error/integer_type'
|
4
|
+
require 'super_model/error/proc_type'
|
5
|
+
|
6
|
+
class SuperModel
|
7
|
+
module Associations
|
8
|
+
|
9
|
+
module BelongsTo
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
def def_foreign_key_methods(options={})
|
14
|
+
Error::HashType.check( options, required_keys: [:class, :foreign_key] )
|
15
|
+
|
16
|
+
options[:class].instance_eval do
|
17
|
+
attr_reader options[:foreign_key]
|
18
|
+
|
19
|
+
define_method("#{ options[:foreign_key] }=") do |value|
|
20
|
+
instance_variable_set( "@#{ options[:foreign_key] }", Error::IntegerType.check(value) )
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def def_association_methods(options={})
|
26
|
+
Error::HashType.check( options, required_keys: [:class, :resource_name, :getter_proc, :setter_proc] )
|
27
|
+
Error::ProcType.check( options[:getter_proc] )
|
28
|
+
Error::ProcType.check( options[:setter_proc] )
|
29
|
+
|
30
|
+
options[:class].instance_eval do
|
31
|
+
define_method( options[:resource_name], &options[:getter_proc] )
|
32
|
+
define_method( "#{ options[:resource_name] }=", &options[:setter_proc] )
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def belongs_to(resource_name, options={})
|
39
|
+
options[:resource_name] ||= SuperModel::Associations.sanitize_resource_name( resource_name, :singular )
|
40
|
+
options[:foreign_key] ||= SuperModel::Associations.foreign_key_for( options[:resource_name], :singular )
|
41
|
+
|
42
|
+
BelongsTo.def_foreign_key_methods(
|
43
|
+
class: self,
|
44
|
+
foreign_key: options[:foreign_key]
|
45
|
+
)
|
46
|
+
|
47
|
+
BelongsTo.def_association_methods(
|
48
|
+
class: self,
|
49
|
+
resource_name: options[:resource_name],
|
50
|
+
getter_proc: Proc.new do
|
51
|
+
ObjectSpace._id2ref( instance_variable_get("@#{ options[:foreign_key] }") ) if instance_variable_defined?("@#{ options[:foreign_key] }")
|
52
|
+
end,
|
53
|
+
setter_proc: Proc.new do |value|
|
54
|
+
send( "#{ options[:foreign_key] }=", value.object_id )
|
55
|
+
end
|
56
|
+
)
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'super_model/associations'
|
2
|
+
|
3
|
+
class SuperModel
|
4
|
+
module Associations
|
5
|
+
|
6
|
+
module HasMany
|
7
|
+
|
8
|
+
def has_many(resource_name, options={})
|
9
|
+
|
10
|
+
options[:resource_name] ||= SuperModel::Associations.sanitize_resource_name( resource_name, :pluralize )
|
11
|
+
options[:foreign_key] ||= SuperModel::Associations.foreign_key_for( options[:resource_name], :pluralize )
|
12
|
+
|
13
|
+
SuperModel::Associations.def_foreign_key_methods( self, options[:foreign_key] )
|
14
|
+
|
15
|
+
SuperModel::Associations.def_association_methods(
|
16
|
+
class: self,
|
17
|
+
resource_name: options[:resource_name],
|
18
|
+
foreign_key: options[:foreign_key],
|
19
|
+
getter_proc: Proc.new do
|
20
|
+
instance_variable_get("@#{ options[:foreign_key] }").collect { |id| ObjectSpace._id2ref(id) } if instance_variable_defined?("@#{ options[:foreign_key] }")
|
21
|
+
end,
|
22
|
+
setter_proc: Proc.new do |collection|
|
23
|
+
send( "#{ options[:foreign_key] }=", collection.collect(&:object_id) )
|
24
|
+
end
|
25
|
+
)
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'super_model/associations'
|
2
|
+
|
3
|
+
class SuperModel
|
4
|
+
module Associations
|
5
|
+
|
6
|
+
module HasOne
|
7
|
+
|
8
|
+
def has_one(resource_name, options={})
|
9
|
+
|
10
|
+
options[:resource_name] ||= SuperModel::Associations.sanitize_resource_name( resource_name, :singularize )
|
11
|
+
options[:foreign_key] ||= SuperModel::Associations.foreign_key_for( options[:resource_name], :singularize )
|
12
|
+
|
13
|
+
SuperModel::Associations.def_foreign_key_methods( self, options[:foreign_key] )
|
14
|
+
|
15
|
+
SuperModel::Associations.def_association_methods(
|
16
|
+
class: self,
|
17
|
+
resource_name: options[:resource_name],
|
18
|
+
foreign_key: options[:foreign_key],
|
19
|
+
getter_proc: Proc.new do
|
20
|
+
ObjectSpace._id2ref( instance_variable_get("@#{ options[:foreign_key] }") ) if instance_variable_defined?("@#{ options[:foreign_key] }")
|
21
|
+
end,
|
22
|
+
setter_proc: Proc.new do |value|
|
23
|
+
send( "#{ options[:foreign_key] }=", value.object_id )
|
24
|
+
end
|
25
|
+
)
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'super_model/error/type'
|
2
|
+
|
3
|
+
class SuperModel
|
4
|
+
class Error
|
5
|
+
|
6
|
+
class HashType < Type
|
7
|
+
|
8
|
+
def self.check(object, options={})
|
9
|
+
super
|
10
|
+
|
11
|
+
raise self unless options[:required_keys].is_a?(Array) && options[:required_keys].all? { |key| object.has_key?(key) }
|
12
|
+
|
13
|
+
object
|
14
|
+
end
|
15
|
+
|
16
|
+
valid_types Hash
|
17
|
+
valid_methods :to_hash, :to_h
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'super_model/error/type'
|
2
|
+
|
3
|
+
class SuperModel
|
4
|
+
class Error
|
5
|
+
|
6
|
+
class InvalidPlurality < Error
|
7
|
+
|
8
|
+
def self.check(object)
|
9
|
+
raise self unless [:singular, :plural].include?(object)
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
'`plurality` must be either :singular or :plural'
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'super_model/error'
|
2
|
+
|
3
|
+
class SuperModel
|
4
|
+
class Error
|
5
|
+
|
6
|
+
class Type < Error
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def valid_types(*types)
|
11
|
+
@valid_types = types unless types.empty?
|
12
|
+
@valid_types ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
def valid_methods(*method_names)
|
16
|
+
@valid_methods = method_names unless method_names.empty?
|
17
|
+
@valid_methods ||= []
|
18
|
+
end
|
19
|
+
|
20
|
+
def check(object, options={})
|
21
|
+
type_assertion = valid_types.any? { |type| object.is_a?(type) } unless valid_types.empty?
|
22
|
+
method_assertion = valid_methods.any? { |type| object.respond_to?(type) } unless valid_methods.empty?
|
23
|
+
|
24
|
+
if type_assertion && method_assertion
|
25
|
+
raise self unless type_assertion || method_assertion
|
26
|
+
elsif type_assertion && !method_assertion
|
27
|
+
raise self unless type_assertion
|
28
|
+
elsif !type_assertion && method_assertion
|
29
|
+
raise self unless method_assertion
|
30
|
+
else
|
31
|
+
# TODO
|
32
|
+
end
|
33
|
+
|
34
|
+
object
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
messages = []
|
41
|
+
messages << self.class.valid_types.collect { |type| "be a #{type}" }
|
42
|
+
messages << self.class.valid_methods.collect { |method| "respond to #{method}" }
|
43
|
+
messages.flatten!
|
44
|
+
messages.last = "or #{messages.last}"
|
45
|
+
|
46
|
+
message = "must #{messages.join(', ')}"
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'super_model/validations/acceptance'
|
2
|
+
require 'super_model/validations/confirmation'
|
3
|
+
require 'super_model/validations/date'
|
4
|
+
require 'super_model/validations/date_time'
|
5
|
+
require 'super_model/validations/exclusion'
|
6
|
+
require 'super_model/validations/format'
|
7
|
+
require 'super_model/validations/inclusion'
|
8
|
+
require 'super_model/validations/length'
|
9
|
+
require 'super_model/validations/numericality'
|
10
|
+
require 'super_model/validations/presence'
|
11
|
+
require 'super_model/validations/time'
|
12
|
+
require 'super_model/validations/uniqueness'
|
13
|
+
|
14
|
+
class SuperModel
|
15
|
+
|
16
|
+
module Validations
|
17
|
+
|
18
|
+
class << self
|
19
|
+
|
20
|
+
def validators
|
21
|
+
@validators ||= []
|
22
|
+
end
|
23
|
+
|
24
|
+
def register(validator_class)
|
25
|
+
validators << validator_class unless validators.include?(validator_class)
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_validations(receiver)
|
29
|
+
receiver.extend(Acceptance::ClassMethods)
|
30
|
+
receiver.extend(Confirmation::ClassMethods)
|
31
|
+
receiver.extend(Date::ClassMethods)
|
32
|
+
receiver.extend(DateTime::ClassMethods)
|
33
|
+
receiver.extend(Exclusion::ClassMethods)
|
34
|
+
receiver.extend(Format::ClassMethods)
|
35
|
+
receiver.extend(Inclusion::ClassMethods)
|
36
|
+
receiver.extend(Length::ClassMethods)
|
37
|
+
receiver.extend(Numericality::ClassMethods)
|
38
|
+
receiver.extend(Presence::ClassMethods)
|
39
|
+
receiver.extend(Time::ClassMethods)
|
40
|
+
receiver.extend(Uniqueness::ClassMethods)
|
41
|
+
end
|
42
|
+
|
43
|
+
def included(receiver)
|
44
|
+
add_validations(receiver)
|
45
|
+
end
|
46
|
+
|
47
|
+
def extended(receiver)
|
48
|
+
add_validations(receiver)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
puts "=-=-=-="
|
4
|
+
puts "=-=-=-="
|
5
|
+
p SuperModel::VERSION
|
6
|
+
puts "=-=-=-="
|
7
|
+
puts "=-=-=-="
|
8
|
+
|
9
|
+
|
10
|
+
class User
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
class Photo < SuperModel
|
15
|
+
belongs_to :user
|
16
|
+
end
|
17
|
+
|
18
|
+
describe Photo do
|
19
|
+
|
20
|
+
let(:user) { User.new }
|
21
|
+
|
22
|
+
describe '#user' do
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#user_id' do
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
data/super_model.gemspec
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "super_model"
|
5
|
+
s.version = "0.0.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Ryan Scott Lewis"]
|
9
|
+
s.date = "2013-02-21"
|
10
|
+
s.description = ""
|
11
|
+
s.email = "ryan@rynet.us"
|
12
|
+
s.files = [".gitignore", ".rspec", ".rvmrc", "Gemfile", "Gemfile.lock", "Guardfile", "LICENSE", "README.md", "Rakefile", "VERSION", "examples/associations_example.rb", "examples/validations_example.rb", "lib/super_model.rb", "lib/super_model/associations.rb", "lib/super_model/associations/belongs_to.rb", "lib/super_model/associations/has_many.rb", "lib/super_model/associations/has_one.rb", "lib/super_model/error.rb", "lib/super_model/error/hash_type.rb", "lib/super_model/error/integer_type.rb", "lib/super_model/error/invalid_plurality.rb", "lib/super_model/error/proc_type.rb", "lib/super_model/error/type.rb", "lib/super_model/validations.rb", "lib/super_model/validations/acceptance.rb", "lib/super_model/validations/confirmation.rb", "lib/super_model/validations/date.rb", "lib/super_model/validations/date_time.rb", "lib/super_model/validations/exclusion.rb", "lib/super_model/validations/format.rb", "lib/super_model/validations/inclusion.rb", "lib/super_model/validations/length.rb", "lib/super_model/validations/numericality.rb", "lib/super_model/validations/presence.rb", "lib/super_model/validations/time.rb", "lib/super_model/validations/uniqueness.rb", "lib/super_model/validator.rb", "spec/spec_helper.rb", "spec/super_model/associations/belongs_to_spec.rb", "super_model.gemspec", "tmp/rspec_guard_result"]
|
13
|
+
s.homepage = "http://github.com/RyanScottLewis/super_model"
|
14
|
+
s.require_paths = ["lib"]
|
15
|
+
s.rubygems_version = "1.8.24"
|
16
|
+
s.summary = ""
|
17
|
+
|
18
|
+
if s.respond_to? :specification_version then
|
19
|
+
s.specification_version = 3
|
20
|
+
|
21
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
22
|
+
s.add_runtime_dependency(%q<version>, ["~> 1.0.0"])
|
23
|
+
s.add_runtime_dependency(%q<active_support>, ["~> 3.0.0"])
|
24
|
+
s.add_development_dependency(%q<guard-rspec>, ["~> 2.4.1"])
|
25
|
+
s.add_development_dependency(%q<guard-yard>, ["~> 2.0.1"])
|
26
|
+
s.add_development_dependency(%q<rb-fsevent>, ["~> 0.9.3"])
|
27
|
+
s.add_development_dependency(%q<fuubar>, ["~> 1.1.0"])
|
28
|
+
s.add_development_dependency(%q<redcarpet>, ["~> 2.2.2"])
|
29
|
+
s.add_development_dependency(%q<github-markup>, ["~> 0.7.5"])
|
30
|
+
else
|
31
|
+
s.add_dependency(%q<version>, ["~> 1.0.0"])
|
32
|
+
s.add_dependency(%q<active_support>, ["~> 3.0.0"])
|
33
|
+
s.add_dependency(%q<guard-rspec>, ["~> 2.4.1"])
|
34
|
+
s.add_dependency(%q<guard-yard>, ["~> 2.0.1"])
|
35
|
+
s.add_dependency(%q<rb-fsevent>, ["~> 0.9.3"])
|
36
|
+
s.add_dependency(%q<fuubar>, ["~> 1.1.0"])
|
37
|
+
s.add_dependency(%q<redcarpet>, ["~> 2.2.2"])
|
38
|
+
s.add_dependency(%q<github-markup>, ["~> 0.7.5"])
|
39
|
+
end
|
40
|
+
else
|
41
|
+
s.add_dependency(%q<version>, ["~> 1.0.0"])
|
42
|
+
s.add_dependency(%q<active_support>, ["~> 3.0.0"])
|
43
|
+
s.add_dependency(%q<guard-rspec>, ["~> 2.4.1"])
|
44
|
+
s.add_dependency(%q<guard-yard>, ["~> 2.0.1"])
|
45
|
+
s.add_dependency(%q<rb-fsevent>, ["~> 0.9.3"])
|
46
|
+
s.add_dependency(%q<fuubar>, ["~> 1.1.0"])
|
47
|
+
s.add_dependency(%q<redcarpet>, ["~> 2.2.2"])
|
48
|
+
s.add_dependency(%q<github-markup>, ["~> 0.7.5"])
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
|
metadata
ADDED
@@ -0,0 +1,216 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: super_model
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ryan Scott Lewis
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: version
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.0.0
|
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: 1.0.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: active_support
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 3.0.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 3.0.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: guard-rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 2.4.1
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.4.1
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: guard-yard
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.0.1
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 2.0.1
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rb-fsevent
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 0.9.3
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 0.9.3
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: fuubar
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 1.1.0
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 1.1.0
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: redcarpet
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 2.2.2
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 2.2.2
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: github-markup
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ~>
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: 0.7.5
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ~>
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: 0.7.5
|
142
|
+
description: ''
|
143
|
+
email: ryan@rynet.us
|
144
|
+
executables: []
|
145
|
+
extensions: []
|
146
|
+
extra_rdoc_files: []
|
147
|
+
files:
|
148
|
+
- .gitignore
|
149
|
+
- .rspec
|
150
|
+
- .rvmrc
|
151
|
+
- Gemfile
|
152
|
+
- Gemfile.lock
|
153
|
+
- Guardfile
|
154
|
+
- LICENSE
|
155
|
+
- README.md
|
156
|
+
- Rakefile
|
157
|
+
- VERSION
|
158
|
+
- examples/associations_example.rb
|
159
|
+
- examples/validations_example.rb
|
160
|
+
- lib/super_model.rb
|
161
|
+
- lib/super_model/associations.rb
|
162
|
+
- lib/super_model/associations/belongs_to.rb
|
163
|
+
- lib/super_model/associations/has_many.rb
|
164
|
+
- lib/super_model/associations/has_one.rb
|
165
|
+
- lib/super_model/error.rb
|
166
|
+
- lib/super_model/error/hash_type.rb
|
167
|
+
- lib/super_model/error/integer_type.rb
|
168
|
+
- lib/super_model/error/invalid_plurality.rb
|
169
|
+
- lib/super_model/error/proc_type.rb
|
170
|
+
- lib/super_model/error/type.rb
|
171
|
+
- lib/super_model/validations.rb
|
172
|
+
- lib/super_model/validations/acceptance.rb
|
173
|
+
- lib/super_model/validations/confirmation.rb
|
174
|
+
- lib/super_model/validations/date.rb
|
175
|
+
- lib/super_model/validations/date_time.rb
|
176
|
+
- lib/super_model/validations/exclusion.rb
|
177
|
+
- lib/super_model/validations/format.rb
|
178
|
+
- lib/super_model/validations/inclusion.rb
|
179
|
+
- lib/super_model/validations/length.rb
|
180
|
+
- lib/super_model/validations/numericality.rb
|
181
|
+
- lib/super_model/validations/presence.rb
|
182
|
+
- lib/super_model/validations/time.rb
|
183
|
+
- lib/super_model/validations/uniqueness.rb
|
184
|
+
- lib/super_model/validator.rb
|
185
|
+
- spec/spec_helper.rb
|
186
|
+
- spec/super_model/associations/belongs_to_spec.rb
|
187
|
+
- super_model.gemspec
|
188
|
+
- tmp/rspec_guard_result
|
189
|
+
homepage: http://github.com/RyanScottLewis/super_model
|
190
|
+
licenses: []
|
191
|
+
post_install_message:
|
192
|
+
rdoc_options: []
|
193
|
+
require_paths:
|
194
|
+
- lib
|
195
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
196
|
+
none: false
|
197
|
+
requirements:
|
198
|
+
- - ! '>='
|
199
|
+
- !ruby/object:Gem::Version
|
200
|
+
version: '0'
|
201
|
+
segments:
|
202
|
+
- 0
|
203
|
+
hash: -921199149
|
204
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
205
|
+
none: false
|
206
|
+
requirements:
|
207
|
+
- - ! '>='
|
208
|
+
- !ruby/object:Gem::Version
|
209
|
+
version: '0'
|
210
|
+
requirements: []
|
211
|
+
rubyforge_project:
|
212
|
+
rubygems_version: 1.8.24
|
213
|
+
signing_key:
|
214
|
+
specification_version: 3
|
215
|
+
summary: ''
|
216
|
+
test_files: []
|