GithubHelp home page GithubHelp logo

method_source's People

Contributors

adamsanderson avatar banister avatar byroot avatar conradirwin avatar jacknagel avatar jasonkarns avatar junaruga avatar koic avatar kyrylo avatar mensfeld avatar mlarraz avatar nisusam avatar petems avatar petergoldstein avatar rafbm avatar redgetan avatar rf- avatar stas avatar voxik avatar yhirano55 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

method_source's Issues

How can I use method_source to get the source of a Rails scope?

I have a Rails scope:

  scope :positive_amount,                    -> { where("amount > 0") }

But when I do:

Transaction.method(:positive_amount).source.display

I get:

            singleton_class.send(:define_method, name) do |*args|
              scope = all
              scope = scope._exec_scope(*args, &body)
              scope = scope.extending(extension) if extension
              scope
            end

Not the -> { where("amount > 0") } that I'd like.

Any ideas?

Shouldn't `source` method work with `eval` ?

This is a test case to demonstrate what I mentioned in the title:

#!/usr/bin/env ruby
# test.rb

class TestClass
  source = <<-EOF
  def test_m
    "test_m here"
  end
  EOF

  eval(source)
end
[1] pry(main)> require './test.rb'                                                                                                                                                                                                                                      
=> true
[2] pry(main)> TestClass.new.test_m                                                                                                                                                                                                                                     
=> "test_m here"
[3] pry(main)> m  = TestClass.new.method :test_m                                                                                                                                                                                                                        
=> #<Method: TestClass#test_m>

[4] pry(main)> m.source                                                                                                                                                                                                                                                 
MethodSource::SourceNotFoundError: Could not load source for : No such file or directory @ rb_sysopen - (eval)
from /home/yanying/.rvm/gems/ruby-2.1.5/gems/method_source-0.8.2/lib/method_source.rb:55:in `rescue in lines_for'

[5] pry(main)> m.source_location                                                                                                                                                                                                                                        
=> ["(eval)", 1]
[6] pry(main)>      

Licence missing in the rubygems version and in the gemspec

The "method_source" gem seems not to have a license at all. Unless a license that specifies otherwise is included, nobody else can use, copy, distribute, or modify that library without being at risk of take-downs, shake-downs, or litigation.

I know, that this gem has a license on github, however it's missing one at rubygems and in a gemspec.

Version 1.0.0 causes downstream breakages (pry-rails)

Running the "rails generate" command when pry-rails is installed leads to the error "undefined method `create_command' for #Pry::CommandSet:0x00000000057ccb08". The "rails generate" command works if pry-rails is not installed.

The pry-rails gem was last updated on December 30, 2018. The pry gem (dependency of pry-rails) was last updated on November 12, 2018. The coderay gem (dependency of pry) was last updated on September 3, 2017.

The Gemfile.lock file shows that version 1.0.0 (latest one) of the method_source gem is in use. This version of the gem was published today, March 19, 2020.

Multiline method call with block raises MethodSource::SourceNotFoundError

Related to #22

For blocks, source_location reports the line where the block opens as its line number. If the block is part of a multiline expression, this can be partway through the expression.

Maybe expression_at needs to start looking backwards (at line numbers prior to the provided one) if the first line has a syntax error?

class A
  attr_reader :blk

  def initialize(_stuff, &blk)
    @blk = blk
  end
end

a = A.new("a" => 1, "b" => 2) do
  "Block!"
end

b = A.new("a" => 1,
          "b" => 2) do # line 14
  "Block!"
end

puts a.blk.source #=> works
puts b.blk.source_location #=> a.rb 14
puts b.blk.source #=> MethodSource::SourceNotFoundError
MethodSource::SourceNotFoundError: Could not parse source for #<Proc:0x00007f85ebcb34a0 ~/a.rb:14>: (eval):2: syntax error, unexpected =>, expecting end-of-input
          "b" => 2) do
              ^~

from ~/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/method_source-1.0.0/lib/method_source.rb:29:in `rescue in source_helper'
Caused by SyntaxError: (eval):2: syntax error, unexpected =>, expecting end-of-input
          "b" => 2) do
              ^~

from ~/.rbenv/versions/2.7.3/lib/ruby/gems/2.7.0/gems/method_source-1.0.0/lib/method_source/code_helpers.rb:71:in `eval'

Add source code URI to rubygems.org

The gem metadata on rubygems.org doesn't set the source code URI, so there's no easy way to jump from the gem to its source repo. I had to search on GitHub and then compare the list of release tags to the set of versions on rubygems.org to verify I found the correct repo.

If someone with permissions could add the value to rubygems.org, that would be greatly appreciated.

SourceNotFoundError on blocks defined in larger expressions

I had low expectations of method_source helping me, so I might be way out of bounds here.

I'm writing a little gem called arg-that which provides a method that takes a block. To give better test messages, I was hoping to use method_source (or similar) to print the content of the arg_that blocks.

Here's a little example:

    expect(
      :a => 1,
      :b => 99
    ).to eqish(
      :a => 1,
      :b => arg_that {|arg| arg > 98 && arg < 100 }
    )

In the above, calling #inspect on the block passed to arg_that will raise the following:

Failure/Error: Unable to find matching line from backtrace
     MethodSource::SourceNotFoundError:
       Could not parse source for #<Proc:0x007ffc8bc73898@/Volumes/Macintosh HD/Users/justin/code/vagrants/oss/projects/ruby/arg_that/spec/arg_that_spec.rb:42>: (eval):2: syntax error, unexpected tASSOC, expecting $end
                 :b => arg_that {|arg| arg > 98 && arg < 100 }
                      ^
     # ./lib/arg_that/that_arg.rb:13:in `inspect'
     # ./lib/arg_that/eqish.rb:19:in `block (2 levels) in <top (required)>'

method_source doesn't work with Prism

Because

GENERIC_REGEXPS = [
/unexpected (\$end|end-of-file|end-of-input|END_OF_FILE)/, # mri, jruby, ruby-2.0, ironruby
/embedded document meets end of file/, # =begin
/unterminated (quoted string|string|regexp|list) meets end of file/, # "quoted string" is ironruby
/can't find string ".*" anywhere before EOF/, # rbx and jruby
/missing 'end' for/, /expecting kWHEN/ # rbx
]

and Prism uses different SyntaxError messages.

I wonder, does method_source need to filter SyntaxError messages or could it just keep adding inputs until no more SyntaxError (or EOF)?

Ref: ruby/prism#2734

lambda nested in array of hashes causes Proc#source to raise MethodSource::SourceNotFoundError

Issue

Ran into this issue while writing parameterized tests that include an optional block argument. Simplest implementation of the problem:

require "method_source"

a = [
  {block: ->(e) { e << %i[a c] }},
]

puts a.first[:block].source

Output:

/Users/me/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/method_source-1.0.0/lib/method_source.rb:29:in `rescue in source_helper': Could not parse source for #<Proc:0x0000000100999ef8 ./source.rb:4 (lambda)>: (eval):2: syntax error, unexpected ',', expecting end-of-input (MethodSource::SourceNotFoundError)
...block: ->(e) { e << %i[a c] }},
...                              ^
	from /Users/me/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/method_source-1.0.0/lib/method_source.rb:23:in `source_helper'
	from /Users/me/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/method_source-1.0.0/lib/method_source.rb:110:in `source'
	from ./source.rb:7:in `<main>'
/Users/me/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/method_source-1.0.0/lib/method_source/code_helpers.rb:71:in `eval': (eval):2: syntax error, unexpected ',', expecting end-of-input (SyntaxError)
...block: ->(e) { e << %i[a c] }},
...                              ^
	from /Users/me/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/method_source-1.0.0/lib/method_source/code_helpers.rb:71:in `block in complete_expression?'
	from /Users/me/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/method_source-1.0.0/lib/method_source/code_helpers.rb:70:in `catch'
	from /Users/me/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/method_source-1.0.0/lib/method_source/code_helpers.rb:70:in `complete_expression?'
	from /Users/me/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/method_source-1.0.0/lib/method_source/code_helpers.rb:97:in `block in extract_first_expression'
	from /Users/me/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/method_source-1.0.0/lib/method_source/code_helpers.rb:95:in `each'
	from /Users/me/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/method_source-1.0.0/lib/method_source/code_helpers.rb:95:in `extract_first_expression'
	from /Users/me/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/method_source-1.0.0/lib/method_source/code_helpers.rb:30:in `expression_at'
	from /Users/me/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/method_source-1.0.0/lib/method_source.rb:27:in `source_helper'
	from /Users/me/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/method_source-1.0.0/lib/method_source.rb:110:in `source'
	from ./source.rb:7:in `<main>'

Possible fix

I recognize this is a major change for the gem, and may not be possible due to the variety of ruby implementations this supports, but the parser gem does an excellent job turning any source file into an AST. The AST can easily be searched for proc/lambda/def calls, and the line numbers and source are easily accessible from any matching node.

This is the workaround I'm using for now to get Proc#source working for my case. I can adapt this and create a pull request for method_source if the maintainers feel that changing the parser is a good idea.

require "dry/core/cache"
require "parser/current"

module ProcSource
  extend Dry::Core::Cache
  extend AST::Sexp

  class SourceNotFound < StandardError; end

  # parse a ruby source file and return the AST; result is cached
  def self.parse(path)
    fetch_or_store(path) do
      source_buffer = Parser::Source::Buffer.new(path).read
      parser = Parser::CurrentRuby.new
      parser.diagnostics.all_errors_are_fatal = true
      parser.diagnostics.ignore_warnings      = true
      parser.parse(source_buffer)
    end
  end

  PROC_NODES = [
    s(:send, nil, :lambda),
    s(:send, nil, :proc),
    s(:send, s(:const, nil, :Proc), :new),
  ]

  module Helpers
    def source
      file, line = source_location
      root = ProcSource.parse(file)

      queue = [root]
      until queue.empty?
        node = queue.shift
        next unless node.is_a?(Parser::AST::Node)
        queue.unshift(*node.children)

        next unless node.type == :block
        next unless node.loc.line == line

        # verify the first child is a send node
        ch = node.children.first
        next unless ch.is_a?(Parser::AST::Node)
        next unless ch.type == :send

        # verify we're calling lambda, proc, or Proc.new
        next unless ProcSource::PROC_NODES.include?(ch)

        return node.loc.expression.source
      end

      raise SourceNotFound, "unable to find source for %p" % self
    end
  end

  Proc.prepend(Helpers)
end

Exclude Proc#source Enclosure

Hit a case where I need the Proc source without the enclosure. For example:

p = Proc.new do
  puts 'proc'
end

p.source currently gives me all three lines, but I really want:

puts 'proc'

There doesn't appear to be a way to do this. Possible to add?

method_source can raise a EOFError.

To reproduce:

require 'rubygems'
require 'method_source'

block     = proc { puts "Hello, world!" }
original = block.source

File.open('/tmp/foo.txt', 'a') { |f| f.write(original) }

buf     = File.read '/tmp/foo.txt'
block = eval(buf, binding, __FILE__, __LINE__)

block.source

If you change the mode from a to w, it works fine.
It looks like method_source loops past the end of the file (valid_expression? never returns true).

EOF error when using source on methods defined via class/instance eval

[40] pry(main)> ActiveRecord::Base.method(:before_save).source
EOFError: end of file reached
from /Users/reg/.rvm/gems/ruby-1.9.3-p125@pry_sbox/gems/method_source-0.7.1/lib/method_source.rb:44:in `readline'

/Users/reg/.rvm/gems/ruby-1.9.3-p125@pry_sbox/gems/activemodel-3.2.3/lib/active_model/callbacks.rb: 109

    def _define_before_model_callback(klass, callback) #:nodoc:
      klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
        def self.before_#{callback}(*args, &block)
          set_callback(:#{callback}, :before, *args, &block)
        end
      CALLBACK
    end

As you can see below, the # symbol will be interpreted as a comment so valid_expression? would never return true and readline just keeps going

       def self.before_#{callback}(*args, &block)
          set_callback(:#{callback}, :before, *args, &block)
        end

Problem would have been solved if syntax checking is done at the start of klass.class_eval line instead of def self.before_ as suggested by @banister.

method.source segfaults on arm64

I keep encountering this on multiple VMs on an aws a1.xlarge running the openSUSE Leap 15.1 ARM marketplace ami image.

(I am running into this running GitLab)

/opt/gitlab/embedded/lib/ruby/gems/2.6.0/gems/method_source-1.0.0/lib/method_source/code_helpers.rb:71: [BUG] Segmentation fault at 0x00000000000000dc
ruby 2.6.6p146 (2020-03-31 revision 67876) [aarch64-linux]

-- Control frame information -----------------------------------------------
c:0063 p:---- s:0355 e:000354 CFUNC  :eval
c:0062 p:0021 s:0350 e:000349 BLOCK  /opt/gitlab/embedded/lib/ruby/gems/2.6.0/gems/method_source-1.0.0/lib/method_source/code_helpers.rb:71 [FINISH]
c:0061 p:---- s:0347 e:000346 CFUNC  :catch
c:0060 p:0014 s:0342 e:000341 METHOD /opt/gitlab/embedded/lib/ruby/gems/2.6.0/gems/method_source-1.0.0/lib/method_source/code_helpers.rb:70
c:0059 p:0030 s:0336 e:000335 BLOCK  /opt/gitlab/embedded/lib/ruby/gems/2.6.0/gems/method_source-1.0.0/lib/method_source/code_helpers.rb:97 [FINISH]
c:0058 p:---- s:0332 e:000331 CFUNC  :each
c:0057 p:0039 s:0328 e:000327 METHOD /opt/gitlab/embedded/lib/ruby/gems/2.6.0/gems/method_source-1.0.0/lib/method_source/code_helpers.rb:95
c:0056 p:0077 s:0320 e:000319 METHOD /opt/gitlab/embedded/lib/ruby/gems/2.6.0/gems/method_source-1.0.0/lib/method_source/code_helpers.rb:30
c:0055 p:0060 s:0310 e:000309 METHOD /opt/gitlab/embedded/lib/ruby/gems/2.6.0/gems/method_source-1.0.0/lib/method_source.rb:27
c:0054 p:0031 s:0301 e:000300 METHOD /opt/gitlab/embedded/lib/ruby/gems/2.6.0/gems/method_source-1.0.0/lib/method_source.rb:110
c:0053 p:0051 s:0297 e:000296 BLOCK  /opt/gitlab/embedded/service/gitlab-rails/config/initializers/zz_metrics.rb:181

Called from the GitLab code here: https://gitlab.com/gitlab-org/gitlab/-/blob/718eaa448d40a69781415335ca4275578d5585e2/config/initializers/zz_metrics.rb#L181

GitLab bug tracking the issue is here: https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5645

No license information

As far as I can tell there is no license information for this code. Could you please add a license?

Blocks defined amid larger expressions will return the entire line

Given a line like this:

Then { expect(:foo).to eqish arg_that {|arg| arg.kind_of?(String) } }

Asking the block passed to arg_that its source will return a string of the entire line, not only what was passed to the block.

I imagine this is just a limitation of what #source_location tells you (e.g. no column information). Is there any other way to get at this? Would an AST approach perhaps work?

Require paths in test.rb should be expanded

In Ruby 1.9.3, there is no '.' in file search path, therefore the test suite needs to be executed by:

$ bacon -I. test/test.rb

But I consider the '-I.' superfluous and the correct approach should be to use the File.expand_path to obtain full path to require paths, e.g. the following command will do the change:

$ sed -i 's|direc = File.dirname(__FILE__)|direc = File.expand_path(File.dirname(__FILE__))|' test/test.rb

Thank you for consideration.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.