ruby_interface 0.2.0 → 0.3.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.
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