finalist 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b3bd9a7da2ed1299669acc5b20b76439dc8f565c31fc2bae0915daa7153473eb
4
+ data.tar.gz: 316c0f335739c061c4d469a99ef23827ca87d53caa427bb6499585d4391d5dd3
5
+ SHA512:
6
+ metadata.gz: a5240bd27a734c12653d32afe824391d99f64ae06aac4e3254f1248d79287b031ccbd00bab12ae1cba4b60a7005862d855c3d86d9e34dd66f9f9a0837fe44bca
7
+ data.tar.gz: 98632cd634b0d6748c8b73591292ef9c63b979f5a1016185f9c5468227493218e89e1ca3130aac0208f873bc9ab6fabf1d5ee50144d627b1d2a005eecb565ff4
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,6 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.0
5
+ - 2.4.2
6
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in finalist.gemspec
6
+ gemspec
@@ -0,0 +1,37 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ finalist (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ byebug (9.1.0)
10
+ diff-lcs (1.3)
11
+ rake (10.5.0)
12
+ rspec (3.7.0)
13
+ rspec-core (~> 3.7.0)
14
+ rspec-expectations (~> 3.7.0)
15
+ rspec-mocks (~> 3.7.0)
16
+ rspec-core (3.7.1)
17
+ rspec-support (~> 3.7.0)
18
+ rspec-expectations (3.7.0)
19
+ diff-lcs (>= 1.2.0, < 2.0)
20
+ rspec-support (~> 3.7.0)
21
+ rspec-mocks (3.7.0)
22
+ diff-lcs (>= 1.2.0, < 2.0)
23
+ rspec-support (~> 3.7.0)
24
+ rspec-support (3.7.0)
25
+
26
+ PLATFORMS
27
+ ruby
28
+
29
+ DEPENDENCIES
30
+ bundler (~> 1.16)
31
+ byebug
32
+ finalist!
33
+ rake (~> 10.0)
34
+ rspec (~> 3.0)
35
+
36
+ BUNDLED WITH
37
+ 1.16.1
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 joker1007
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,200 @@
1
+ # Finalist
2
+ [![Build Status](https://travis-ci.org/joker1007/finalist.svg?branch=master)](https://travis-ci.org/joker1007/finalist)
3
+
4
+ Finalist adds `final` method modifier.
5
+ `final` forbids method override.
6
+
7
+ Simple case is following.
8
+
9
+ ```ruby
10
+ class A1
11
+ extend Finalist
12
+
13
+ final def foo
14
+ end
15
+ end
16
+
17
+ class A2 < A1
18
+ def foo
19
+ end
20
+ end
21
+ ```
22
+
23
+ This case raises `Finalist::OverrideFinalMethodError` at `def foo in A2 class`.
24
+
25
+ This gem supports other cases.
26
+ (see [finalist_spec.rb](https://github.com/joker1007/finalist/blob/master/spec/finalist_spec.rb))
27
+
28
+ ## Installation
29
+
30
+ Add this line to your application's Gemfile:
31
+
32
+ ```ruby
33
+ gem 'finalist'
34
+ ```
35
+
36
+ And then execute:
37
+
38
+ $ bundle
39
+
40
+ Or install it yourself as:
41
+
42
+ $ gem install finalist
43
+
44
+ ## Usage
45
+
46
+ A class or module extends `Finalist` module
47
+ And add `final` modifier to target method.
48
+ (`final` can accept symbol as method name.)
49
+
50
+ ### Examples
51
+
52
+ #### include module
53
+
54
+ ```ruby
55
+ module E1
56
+ extend Finalist
57
+
58
+ final def foo
59
+ end
60
+ end
61
+
62
+ module E2
63
+ include E1
64
+ end
65
+
66
+ module E3
67
+ include E2
68
+
69
+ def foo # => raise
70
+ end
71
+ end
72
+ ```
73
+
74
+ #### include module after override
75
+
76
+ ```ruby
77
+ module F1
78
+ extend Finalist
79
+
80
+ final def foo
81
+ end
82
+ end
83
+
84
+ class F2
85
+ def foo
86
+ end
87
+
88
+ include F1 # => raise
89
+ end
90
+ ```
91
+
92
+ #### class method
93
+
94
+ ```ruby
95
+ class J1
96
+ extend Finalist
97
+
98
+ class << self
99
+ final def foo
100
+ end
101
+ end
102
+ end
103
+
104
+ class J2 < J1
105
+ class << self
106
+ def foo # => raise
107
+ end
108
+ end
109
+ end
110
+ ```
111
+
112
+ #### extend object
113
+
114
+ ```ruby
115
+ module H1
116
+ extend Finalist
117
+
118
+ final def foo
119
+ end
120
+ end
121
+
122
+ a = "str"
123
+ a.extend(H1)
124
+ def a.foo # => raise
125
+ end
126
+ ```
127
+
128
+ #### extend object after override
129
+
130
+ ```ruby
131
+ module I1
132
+ extend Finalist
133
+
134
+ final def foo
135
+ end
136
+ end
137
+
138
+ a = "str"
139
+ def a.foo
140
+ end
141
+ a.extend(I1) # => raise
142
+ ```
143
+
144
+ #### class method by extend module
145
+
146
+ ```ruby
147
+ module K1
148
+ extend Finalist
149
+
150
+ final def foo
151
+ end
152
+ end
153
+
154
+ class K2
155
+ extend K1
156
+
157
+ class << self
158
+ def foo # => raise
159
+ end
160
+ end
161
+ end
162
+ ```
163
+
164
+ #### class method by extend module after override
165
+
166
+ ```ruby
167
+ module L1
168
+ extend Finalist
169
+
170
+ final def foo
171
+ end
172
+ end
173
+
174
+ class L2
175
+ class << self
176
+ def foo
177
+ end
178
+ end
179
+
180
+ extend L1 # => raise
181
+ end
182
+ ```
183
+
184
+ ## How is this implemented?
185
+
186
+ Use so many ruby hooks. `TracePoint` and `method_added` and `singleton_method_added` and `included` and `extended`.
187
+
188
+ ## Development
189
+
190
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
191
+
192
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
193
+
194
+ ## Contributing
195
+
196
+ Bug reports and pull requests are welcome on GitHub at https://github.com/joker1007/finalist.
197
+
198
+ ## License
199
+
200
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "finalist"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,28 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "finalist/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "finalist"
8
+ spec.version = Finalist::VERSION
9
+ spec.authors = ["joker1007"]
10
+ spec.email = ["kakyoin.hierophant@gmail.com"]
11
+
12
+ spec.summary = %q{Add final syntax which forbids method override}
13
+ spec.description = %q{Add final syntax which forbids method override}
14
+ spec.homepage = "https://github.com/joker1007/finalist"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.16"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.0"
27
+ spec.add_development_dependency "byebug"
28
+ end
@@ -0,0 +1,130 @@
1
+ require "finalist/version"
2
+ require "set"
3
+
4
+ module Finalist
5
+ class OverrideFinalMethodError < StandardError
6
+ attr_reader :override_class, :origin_class, :unbound_method, :detect_type
7
+
8
+ def initialize(message, klass, origin, meth, detect_type)
9
+ super(message)
10
+ @override_class = klass
11
+ @origin_class = origin
12
+ @unbound_method = meth
13
+ @detect_type = detect_type # for debug
14
+ end
15
+ end
16
+
17
+ def self.extended(base)
18
+ super
19
+ base.extend(SyntaxMethods)
20
+ base.singleton_class.extend(SyntaxMethods)
21
+ base.extend(ModuleMethods) if base.instance_of?(Module)
22
+ end
23
+
24
+ def self.finalized_methods
25
+ @finalized_methods ||= {}
26
+ end
27
+
28
+ module ModuleMethods
29
+ def included(base)
30
+ super
31
+
32
+ base.extend(Finalist)
33
+
34
+ tp = TracePoint.new(:end) do |ev|
35
+ if ev.self == base
36
+ base.ancestors.drop(1).each do |mod|
37
+ Finalist.finalized_methods[mod]&.each do |fmeth_name|
38
+ if meth = base.instance_method(fmeth_name)
39
+ super_method = meth.super_method
40
+ while super_method
41
+ if Finalist.finalized_methods[super_method.owner]&.member?(super_method.name)
42
+ tp.disable
43
+ base.instance_variable_set("@__finalist_tp", nil)
44
+ raise OverrideFinalMethodError.new("#{super_method} at #{super_method.source_location.join(":")} is overrided\n by #{meth} at #{meth.source_location.join(":")}", base, super_method.owner, meth, :trace_point)
45
+ end
46
+
47
+ super_method = super_method.super_method
48
+ end
49
+ end
50
+ end
51
+ end
52
+ tp.disable
53
+ end
54
+ end
55
+ tp.enable
56
+ base.instance_variable_set("@__finalist_tp", tp)
57
+ end
58
+
59
+ def extended(base)
60
+ def base.singleton_method_added(symbol)
61
+ super
62
+
63
+ meth = singleton_class.instance_method(symbol)
64
+ super_method = meth.super_method
65
+ while super_method
66
+ if Finalist.finalized_methods[super_method.owner]&.member?(super_method.name)
67
+ @__finalist_tp&.disable
68
+ @__finalist_tp = nil
69
+ raise OverrideFinalMethodError.new("#{super_method} at #{super_method.source_location.join(":")} is overrided\n by #{meth} at #{meth.source_location.join(":")}", singleton_class, super_method.owner, meth, :extended_singleton_method_added)
70
+ end
71
+ super_method = super_method.super_method
72
+ end
73
+ end
74
+
75
+ base.singleton_class.ancestors.drop(1).each do |mod|
76
+ Finalist.finalized_methods[mod]&.each do |fmeth_name|
77
+ if meth = base.singleton_class.instance_method(fmeth_name)
78
+ super_method = meth.super_method
79
+ while super_method
80
+ if Finalist.finalized_methods[super_method.owner]&.member?(super_method.name)
81
+ base.instance_variable_get("@__finalist_tp")&.disable
82
+ base.instance_variable_set("@__finalist_tp", nil)
83
+
84
+ raise OverrideFinalMethodError.new("#{super_method} at #{super_method.source_location.join(":")} is overrided\n by #{meth} at #{meth.source_location.join(":")}", base.singleton_class, super_method.owner, meth, :extended)
85
+ end
86
+ super_method = super_method.super_method
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ module SyntaxMethods
95
+ def final(symbol)
96
+ method_set = Finalist.finalized_methods[self] ||= Set.new
97
+ method_set.add(symbol)
98
+ end
99
+ end
100
+
101
+ def method_added(symbol)
102
+ super
103
+
104
+ meth = instance_method(symbol)
105
+ super_method = meth.super_method
106
+ while super_method
107
+ if Finalist.finalized_methods[super_method.owner]&.member?(super_method.name)
108
+ @__finalist_tp&.disable
109
+ @__finalist_tp = nil
110
+ raise OverrideFinalMethodError.new("#{super_method} at #{super_method.source_location.join(":")} is overrided\n by #{meth} at #{meth.source_location.join(":")}", self, super_method.owner, meth, :method_added)
111
+ end
112
+ super_method = super_method.super_method
113
+ end
114
+ end
115
+
116
+ def singleton_method_added(symbol)
117
+ super
118
+
119
+ meth = singleton_class.instance_method(symbol)
120
+ super_method = meth.super_method
121
+ while super_method
122
+ if Finalist.finalized_methods[super_method.owner]&.member?(super_method.name)
123
+ @__finalist_tp&.disable
124
+ @__finalist_tp = nil
125
+ raise OverrideFinalMethodError.new("#{super_method} at #{super_method.source_location.join(":")} is overrided\n by #{meth} at #{meth.source_location.join(":")}", self, super_method.owner, meth, :singleton_method_added)
126
+ end
127
+ super_method = super_method.super_method
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,3 @@
1
+ module Finalist
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: finalist
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - joker1007
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-01-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: byebug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Add final syntax which forbids method override
70
+ email:
71
+ - kakyoin.hierophant@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - ".travis.yml"
79
+ - Gemfile
80
+ - Gemfile.lock
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - bin/console
85
+ - bin/setup
86
+ - finalist.gemspec
87
+ - lib/finalist.rb
88
+ - lib/finalist/version.rb
89
+ homepage: https://github.com/joker1007/finalist
90
+ licenses:
91
+ - MIT
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.7.4
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Add final syntax which forbids method override
113
+ test_files: []