ruby_interface 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d9b8ffa291917e451bb903aef5276f566b7294f1
4
- data.tar.gz: 87b777bdd6a33288a62a1e15675ea6e816fdf3f9
3
+ metadata.gz: f41f6e65467a433d39952f9717be64151a61308e
4
+ data.tar.gz: 5e8512ab821859645be604fed1f62e7752d8e492
5
5
  SHA512:
6
- metadata.gz: 1ea17d4036a0ebd057ec6d9d632f8dbcc8f568e94db4ce5cbb152e0bb36ea4626a3cb2e597c260ee48489f99ed34370046b686a5bee762afac3611ff625cfdc7
7
- data.tar.gz: 1ffdcb7225e371e5cf7300ef7b1e7e88ba50929962a8cfdca61ecde77f6b0be13489cca1dee61c3c7885556443d6ad9550be33ae454fc7fe032c762ab4d52f5e
6
+ metadata.gz: d6ac29d101b8044e60b3ed0b90198a364d7b4de02629e7747eb0ec225ac01d3f8cc00ba8c9d0436e1dd89ca424b084d95e4f28e3f4d73f02f0a9fc8f20ec377c
7
+ data.tar.gz: 445d05ccd6176873ef5a14e7cb6271800d1844e79db1d85da22d10671bf267ad65002ba61191585fabd66c639070235919e39cb964864e9f72221d4feea0227d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # 0.3.0
2
+ - Since https://bugs.ruby-lang.org/issues/12696 was resolved with a doc change:
3
+ - Enabled checks whenever anonymous classes are defined with a block by
4
+ - Instrumenting `Class::new` with a `anonymous_class_defined` hook
5
+ - Made sure `class_eval` could still be used if block was not used
6
+ - Added better backtrace to see where the failing class is (instead of library code)
7
+
1
8
  # 0.2.0
2
9
  - Update handling for anonymous classes:
3
10
  - Fix tracepoint never being disabled
@@ -5,4 +12,4 @@
5
12
  - Make sure that any existing inherited hooks don't get overriden.
6
13
 
7
14
  # 0.1.0
8
- - Initial release
15
+ - Initial release
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby_interface (0.2.0)
4
+ ruby_interface (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  When a class is interpreted and does not have the required methods, it throws an error.
4
4
 
5
- Caveats:
6
- - Creating anonymous classes requires the user to use `class_eval` to define methods.
5
+ Since classes in ruby are never closed, the check for required methods will run
6
+ *only* on the *first* definition of the class body.
7
7
 
8
8
  ## Installation
9
9
 
@@ -35,9 +35,7 @@ end
35
35
  class Enterprise < SpaceShip
36
36
  end
37
37
  # => NotImplementedError, 'Expected Enterprise to define #engine, #computer'
38
- ```
39
-
40
- If a class is redefined, the presence of required methods will not be checked.
38
+ ```
41
39
 
42
40
  ### Anonymous classes
43
41
 
@@ -48,15 +46,19 @@ class SpaceShip
48
46
  defines :engine, :computer
49
47
  end
50
48
 
49
+ # If the block is passed in to Class::new
50
+ space_ship = Class.new(SpaceShip) { }
51
+ # => NotImplementedError, 'Expected Enterprise to define #engine, #computer'
52
+
53
+ # If the block is not passed in to Class::new
51
54
  space_ship = Class.new(SpaceShip)
52
55
  space_ship.class_eval { }
56
+
53
57
  # => NotImplementedError, 'Expected Enterprise to define #engine, #computer'
58
+ # NOTE: After the first `class_eval`, the rest of the calls to it will not check whether methods are defined.
59
+ # NOTE: `class_exec` may also be used instead of `class_eval`
54
60
  ```
55
61
 
56
- After the first `class_eval`, the rest of the calls to it will not check whether methods are defined.
57
- Unfortunately, right now, `class_eval` has to be called to check whether the methods are defined,
58
- pending resolution of https://bugs.ruby-lang.org/issues/12696.
59
-
60
62
  ## Development
61
63
 
62
64
  After checking out the repo, run `bin/setup` to install dependencies.
data/lib/class.rb ADDED
@@ -0,0 +1,11 @@
1
+ class Class
2
+ class << self
3
+ alias_method :old_new, :new
4
+
5
+ def new(*args, &block)
6
+ klass = old_new *args, &block
7
+ klass.anonymous_class_defined if block_given? && klass.respond_to?(:anonymous_class_defined)
8
+ klass
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  module RubyInterface
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -1,4 +1,5 @@
1
1
  require 'ruby_interface/version'
2
+ require 'class'
2
3
  require 'awesome_print'
3
4
 
4
5
  module RubyInterface
@@ -26,15 +27,41 @@ module RubyInterface
26
27
 
27
28
  return if missing_methods.empty?
28
29
 
30
+ raise_class_definition_error child, missing_methods
31
+ end
32
+
33
+ private
34
+
35
+ def raise_class_definition_error(child, missing_methods)
29
36
  message = "Expected #{child.name || 'anonymous class'} to define "
30
37
  message << missing_methods.map { |method| "##{method}" }.join(', ')
38
+
31
39
  raise NotImplementedError, message
40
+ rescue NotImplementedError => exception
41
+ directory = File.dirname(__FILE__)
42
+ better_backtrace = exception.backtrace.drop_while do |element|
43
+ element.include? directory
44
+ end
45
+ exception.set_backtrace better_backtrace
46
+ raise exception
32
47
  end
33
48
 
34
- private
35
-
36
49
  def anonymous_class_definition(child)
50
+ define_class_hook child
51
+ redefine_eval_and_exec child
52
+ end
53
+
54
+ def define_class_hook(child)
37
55
  klass = self
56
+ child.define_singleton_method :anonymous_class_defined do
57
+ klass.track_required_methods(child)
58
+ revert_eval_and_exec
59
+ end
60
+ end
61
+
62
+ def redefine_eval_and_exec(child)
63
+ klass = self
64
+
38
65
  [:class_exec, :class_eval].each do |method|
39
66
  old_method = "_old_#{method}".to_sym
40
67
  child.singleton_class.send(:alias_method, old_method, method)
@@ -42,11 +69,18 @@ module RubyInterface
42
69
  child.define_singleton_method method do |*args, &block|
43
70
  send old_method, *args, &block
44
71
  klass.track_required_methods(child)
45
- child.singleton_class.send(:alias_method, method, old_method)
72
+ revert_eval_and_exec
46
73
  end
47
74
  end
48
75
  end
49
76
 
77
+ def revert_eval_and_exec
78
+ [:class_exec, :class_eval].each do |method|
79
+ old_method = "_old_#{method}".to_sym
80
+ singleton_class.send(:alias_method, method, old_method)
81
+ end
82
+ end
83
+
50
84
  def named_class_definition(child)
51
85
  trace = TracePoint.new(:end) do |point|
52
86
  next unless point.self == child
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_interface
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kirill Klimuk
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-08-23 00:00:00.000000000 Z
11
+ date: 2016-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -86,6 +86,7 @@ files:
86
86
  - Rakefile
87
87
  - bin/console
88
88
  - bin/setup
89
+ - lib/class.rb
89
90
  - lib/ruby_interface.rb
90
91
  - lib/ruby_interface/version.rb
91
92
  - ruby_interface.gemspec