final 0.1.0
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/CHANGES +2 -0
- data/MANIFEST +7 -0
- data/README +62 -0
- data/Rakefile +27 -0
- data/final.gemspec +24 -0
- data/lib/final.rb +38 -0
- data/test/test_final.rb +44 -0
- metadata +90 -0
data/CHANGES
ADDED
data/MANIFEST
ADDED
data/README
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
= Description
|
2
|
+
A Ruby module that can make your classes final. A final class cannot
|
3
|
+
be subclassed, and its instance methods cannot be redefined.
|
4
|
+
|
5
|
+
= Synopsis
|
6
|
+
require 'final'
|
7
|
+
|
8
|
+
class Alpha
|
9
|
+
include Final
|
10
|
+
|
11
|
+
def foo
|
12
|
+
puts "Hello"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Error! You cannot subclass Alpha.
|
17
|
+
class Beta < Alpha; end
|
18
|
+
|
19
|
+
# Error! You cannot redefine an existing method.
|
20
|
+
class Alpha
|
21
|
+
def foo
|
22
|
+
puts "Hi"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Not an error. You can add new methods to an existing class.
|
27
|
+
class Alpha
|
28
|
+
def bar
|
29
|
+
puts "World"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
= Developer's Notes
|
34
|
+
This library is merely a proof of concept that was published as the result
|
35
|
+
of a tweet made by James Edward Gray II, where he commented that Ruby core
|
36
|
+
classes should never be subclassed. Knowing that static languages like Java
|
37
|
+
implement the "final" keyword as a way to prevent subclassing, I wanted to
|
38
|
+
see if something similar could be done for Ruby. Turns out it can.
|
39
|
+
|
40
|
+
I do not, however, necessarily advocate actually using this library in
|
41
|
+
production code. Freezing a class this way is contrary to the very nature
|
42
|
+
of Ruby and dynamic languages in general. If you're going to use it, you had
|
43
|
+
better have a very good reason for doing so!
|
44
|
+
|
45
|
+
= Caveats
|
46
|
+
Only instance methods are prevented from redefinition. Singleton methods are
|
47
|
+
not final. This change may happen in a future release.
|
48
|
+
|
49
|
+
= License
|
50
|
+
Artistic 2.0
|
51
|
+
|
52
|
+
= Copyright
|
53
|
+
(C) 2003-2011 Daniel J. Berger
|
54
|
+
All Rights Reserved.`
|
55
|
+
|
56
|
+
= Warranty
|
57
|
+
This package is provided "as is" and without any express or
|
58
|
+
implied warranties, including, without limitation, the implied
|
59
|
+
warranties of merchantability and fitness for a particular purpose.
|
60
|
+
|
61
|
+
= Author
|
62
|
+
Daniel J. Berger
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/clean'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
CLEAN.include('**/*.gem', '**/*.rbc')
|
6
|
+
|
7
|
+
namespace :gem do
|
8
|
+
desc 'Build the final gem'
|
9
|
+
task :create => [:clean] do
|
10
|
+
spec = eval(IO.read('final.gemspec'))
|
11
|
+
Gem::Builder.new(spec).build
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "Install the final gem"
|
15
|
+
task :install => [:create] do
|
16
|
+
file = Dir["*.gem"].first
|
17
|
+
sh "gem install #{file}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Rake::TestTask.new do |t|
|
22
|
+
task :test => :clean
|
23
|
+
t.warning = true
|
24
|
+
t.verbose = true
|
25
|
+
end
|
26
|
+
|
27
|
+
task :default => :test
|
data/final.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = 'final'
|
5
|
+
gem.version = '0.1.0'
|
6
|
+
gem.license = 'Artistic 2.0'
|
7
|
+
gem.author = 'Daniel J. Berger'
|
8
|
+
gem.email = 'djberg96@gmail.com'
|
9
|
+
gem.homepage = 'http://www.rubyforge.org/projects/shards'
|
10
|
+
gem.summary = "An implementation of 'final' for Ruby"
|
11
|
+
gem.test_files = Dir['test/test*']
|
12
|
+
gem.has_rdoc = true
|
13
|
+
gem.files = Dir['**/*'].reject{ |f| f.include?('git') }
|
14
|
+
|
15
|
+
gem.rubyforge_project = 'shards'
|
16
|
+
gem.extra_rdoc_files = ['README', 'CHANGES', 'MANIFEST']
|
17
|
+
|
18
|
+
gem.description = <<-EOF
|
19
|
+
The final library enables you to declare your classes as final. This
|
20
|
+
prevents your class from being subclassed or having its methods redefined.
|
21
|
+
EOF
|
22
|
+
|
23
|
+
gem.add_development_dependency('test-unit', '>= 2.2.0')
|
24
|
+
end
|
data/lib/final.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# Make your class final. Once final'ized, it cannot be subclassed and
|
2
|
+
# its methods cannot be redefined.
|
3
|
+
#
|
4
|
+
module Final
|
5
|
+
# Error raised if any of the Final module's rules are violated.
|
6
|
+
class Error < RuntimeError; end
|
7
|
+
|
8
|
+
# The version of the final library.
|
9
|
+
VERSION = '0.1.0'
|
10
|
+
|
11
|
+
def self.included(mod)
|
12
|
+
# Store already defined methods.
|
13
|
+
mod.instance_eval("@final_methods = []")
|
14
|
+
|
15
|
+
# Internal accessor used in the method_added definition.
|
16
|
+
def mod.final_methods
|
17
|
+
@final_methods
|
18
|
+
end
|
19
|
+
|
20
|
+
# Prevent subclassing, except implicity subclassing from Object.
|
21
|
+
def mod.inherited(sub)
|
22
|
+
raise Error, "cannot subclass final class #{self}" unless self == Object
|
23
|
+
end
|
24
|
+
|
25
|
+
# Prevent methods from being redefined.
|
26
|
+
#--
|
27
|
+
# There's still going to be a method redefinition warning. Gosh, it
|
28
|
+
# sure would be nice if we could disable warnings.
|
29
|
+
#
|
30
|
+
def mod.method_added(sym)
|
31
|
+
if final_methods.include?(sym)
|
32
|
+
raise Error, "method '#{sym}' already defined"
|
33
|
+
else
|
34
|
+
final_methods << sym
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/test/test_final.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
########################################################################
|
2
|
+
# test_final.rb
|
3
|
+
#
|
4
|
+
# Test suite for the final library.
|
5
|
+
########################################################################
|
6
|
+
require 'rubygems'
|
7
|
+
gem 'test-unit'
|
8
|
+
require 'test/unit'
|
9
|
+
require 'final'
|
10
|
+
|
11
|
+
class TC_Final < Test::Unit::TestCase
|
12
|
+
class Foo
|
13
|
+
include Final
|
14
|
+
def bar; end
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup
|
18
|
+
@foo = Foo.new
|
19
|
+
end
|
20
|
+
|
21
|
+
test "version constant returns expected version number" do
|
22
|
+
assert_equal('0.1.0', Final::VERSION)
|
23
|
+
end
|
24
|
+
|
25
|
+
test "final class cannot be subclassed" do
|
26
|
+
assert_raise(Final::Error){ class Bar < Foo; end }
|
27
|
+
end
|
28
|
+
|
29
|
+
test "final class cannot have instance methods redefined" do
|
30
|
+
assert_raise(Final::Error){ class Foo; def bar; end; end }
|
31
|
+
end
|
32
|
+
|
33
|
+
test "adding a new method is not an error" do
|
34
|
+
assert_nothing_raised{ class Foo; def baz; end; end }
|
35
|
+
end
|
36
|
+
|
37
|
+
test "finalized classes do not interfere with each other" do
|
38
|
+
assert_nothing_raised{ class Foo2; include Final; def bar; end; end }
|
39
|
+
end
|
40
|
+
|
41
|
+
def teardown
|
42
|
+
@foo = nil
|
43
|
+
end
|
44
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: final
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Daniel J. Berger
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-04-09 00:00:00 -06:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: test-unit
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 7
|
30
|
+
segments:
|
31
|
+
- 2
|
32
|
+
- 2
|
33
|
+
- 0
|
34
|
+
version: 2.2.0
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
description: " The final library enables you to declare your classes as final. This\n prevents your class from being subclassed or having its methods redefined.\n"
|
38
|
+
email: djberg96@gmail.com
|
39
|
+
executables: []
|
40
|
+
|
41
|
+
extensions: []
|
42
|
+
|
43
|
+
extra_rdoc_files:
|
44
|
+
- README
|
45
|
+
- CHANGES
|
46
|
+
- MANIFEST
|
47
|
+
files:
|
48
|
+
- CHANGES
|
49
|
+
- final.gemspec
|
50
|
+
- lib/final.rb
|
51
|
+
- MANIFEST
|
52
|
+
- Rakefile
|
53
|
+
- README
|
54
|
+
- test/test_final.rb
|
55
|
+
has_rdoc: true
|
56
|
+
homepage: http://www.rubyforge.org/projects/shards
|
57
|
+
licenses:
|
58
|
+
- Artistic 2.0
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
hash: 3
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
version: "0"
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
hash: 3
|
79
|
+
segments:
|
80
|
+
- 0
|
81
|
+
version: "0"
|
82
|
+
requirements: []
|
83
|
+
|
84
|
+
rubyforge_project: shards
|
85
|
+
rubygems_version: 1.3.7
|
86
|
+
signing_key:
|
87
|
+
specification_version: 3
|
88
|
+
summary: An implementation of 'final' for Ruby
|
89
|
+
test_files:
|
90
|
+
- test/test_final.rb
|