attach_function 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 184c80c899df350d373fb5f60ddbc995a379a8f2
4
+ data.tar.gz: f02923c5fd1d28b228034f713d5742fde4c4d400
5
+ SHA512:
6
+ metadata.gz: dcbd1c3e00246c42483b3084afc9d8d799af97fcff6771e884d7c79cd422da282d906971a85d83199604330b069671aa4d572c825095aa964d738cd5a65cf7dd
7
+ data.tar.gz: 473b3497787bfd6bf5ae9b4362e3f9981018c0a9ddd09cd682bf495144ca8fff4a49f4dec3b66cc79c26acd225b2b6cf580ee0229a894a3e247910fab5141333
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.0
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,53 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec feature)
6
+
7
+ ## Uncomment to clear the screen before every task
8
+ # clearing :on
9
+
10
+ ## Guard internally checks for changes in the Guardfile and exits.
11
+ ## If you want Guard to automatically start up again, run guard in a
12
+ ## shell loop, e.g.:
13
+ ##
14
+ ## $ while bundle exec guard; do echo "Restarting Guard..."; done
15
+ ##
16
+ ## Note: if you are using the `directories` clause above and you are not
17
+ ## watching the project directory ('.'), the you will want to move the Guardfile
18
+ ## to a watched dir and symlink it back, e.g.
19
+ #
20
+ # $ mkdir config
21
+ # $ mv Guardfile config/
22
+ # $ ln -s config/Guardfile .
23
+ #
24
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
25
+
26
+ # Note: The cmd option is now required due to the increasing number of ways
27
+ # rspec may be run, below are examples of the most common uses.
28
+ # * bundler: 'bundle exec rspec'
29
+ # * bundler binstubs: 'bin/rspec'
30
+ # * spring: 'bin/rspec' (This will use spring if running and you have
31
+ # installed the spring binstubs per the docs)
32
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
33
+ # * 'just' rspec: 'rspec'
34
+
35
+ guard :rspec, cmd: "bundle exec rspec" do
36
+ watch('spec/spec_helper.rb') { "spec" }
37
+ watch(%r{^spec/.+_spec\.rb})
38
+ watch(%r{^lib/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
39
+ end
40
+
41
+
42
+
43
+ guard :bundler do
44
+ require 'guard/bundler'
45
+ require 'guard/bundler/verify'
46
+ helper = Guard::Bundler::Verify.new
47
+
48
+ files = ['Gemfile']
49
+ files += Dir['*.gemspec'] if files.any? { |f| helper.uses_gemspec?(f) }
50
+
51
+ # Assume files are symlinked from somewhere
52
+ files.each { |file| watch(helper.real_path(file)) }
53
+ end
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # AttachFunction
2
+
3
+ AttachFunction defines new methods that are partial applications of a target function where the first argument to the target function gets fixed to self.
4
+
5
+ The name of the newly defined method will be the basename of the target function (the module path gets stripped) or a user-specified name.
6
+ The receiver of relatively specified parameter functions will be the current module, if a user-specified name is given and
7
+ the user-specified name is not the same as the basename of the target function, or the enclosing module, if no-user specified name is provided or
8
+ if the user-specified name is the same as the basename of the parameter.
9
+
10
+ ## Usage Example:
11
+
12
+ require 'attach_function'
13
+
14
+ module Math
15
+
16
+ #This will contain the method versions of the functions defined in the Math module
17
+ module MethodVersions
18
+ #Get the attach_function macro
19
+ extend AttachFunction
20
+ #Apply it to all methods of the Math module that aren't Object methods
21
+ (Math.methods - Object.methods).each do |m|
22
+ puts "Attaching #{m}"
23
+ attach_function m
24
+ end
25
+ end
26
+ end
27
+
28
+ #Now we include the Math::MethodVersions in Numeric
29
+ Numeric.include(Math::MethodVersions)
30
+ #And now we can do this:
31
+ p "3.14.sin = #{3.14.sin}"
32
+ p "10.log = #{10.log10}"
33
+ p "4.sqrt = #{4.sqrt}"
34
+
35
+ #The functionality that has been added to Numeric in this way is contained in the Math::MethodVersions mixin.
36
+ #I consider this much nicer than rudely monkepatching methods right onto a core class.
37
+ #This way, library users can see (in pry, for example, or via introspection) where a certain added method came from, and possibly filter it out.
38
+ #(Ruby doesn't currently support unmixing).
39
+
40
+
41
+ See the specs and the example folder for more examples.
42
+ ## Installation
43
+
44
+ Add this line to your application's Gemfile:
45
+
46
+ ```ruby
47
+ gem 'attach_function'
48
+ ```
49
+
50
+ And then execute:
51
+
52
+ $ bundle
53
+
54
+ Or install it yourself as:
55
+
56
+ $ gem install attach_function
57
+
58
+ ## Usage
59
+
60
+ User it in any way you like.
61
+
62
+ ## Contributing
63
+
64
+ 1. Fork it ( https://github.com/[my-github-username]/i_rewriter/fork )
65
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
66
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
67
+ 4. Push to the branch (`git push origin my-new-feature`)
68
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ require 'ostruct'
6
+
7
+ gem = OpenStruct.new
8
+ gem.name = File.basename(File.dirname(__FILE__))
9
+ require "#{gem.name}/version"
10
+ gem.module = AttachFunction
11
+
12
+ Gem::Specification.new do |spec|
13
+
14
+ spec.name = gem.name
15
+ spec.version = (gem.module)::VERSION
16
+ spec.summary = %q{True in-place file-editing}
17
+ spec.description = %q{True in-place file-editing (i.e, inode(input) == inode(output)) like with sed, awk, or `ruby i -{p|n}e`, but truly in-place}
18
+
19
+ spec.authors = ["Petr Skocik"]
20
+ spec.email = ["pskocik@gmail.com"]
21
+ spec.homepage = "https://github.com/pjump/#{spec.name}.git"
22
+ spec.licenses = %w[gplv2]
23
+
24
+ spec.files = `git ls-files -z`.split("\x0")
25
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
26
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.add_development_dependency "bundler", "~> 1.7"
30
+ spec.add_development_dependency "rake", "~> 10.0"
31
+ spec.add_development_dependency "rspec"
32
+ spec.add_development_dependency "guard", "2.12"
33
+ spec.add_development_dependency "guard-rspec"
34
+ spec.add_development_dependency "guard-bundler"
35
+ end
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ require 'attach_function'
5
+
6
+ module Math
7
+ module MethodVersions
8
+ extend AttachFunction
9
+ (Math.methods - Object.methods).each do |m|
10
+ puts "Attaching #{m}"
11
+ attach_function m
12
+ end
13
+ end
14
+ end
15
+
16
+ Numeric.include(Math::MethodVersions)
17
+ p "3.14.sin = #{3.14.sin}"
18
+ p "10.log = #{10.log10}"
19
+ p "4.sqrt = #{4.sqrt}"
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ module AttachFunction
4
+ #
5
+ #attach_function defines new methods that are partial applications of a target function where the first argument to the target function gets fixed to self.
6
+ #
7
+ #The name of the newly defined method will be the basename of the target function (the module path gets stripped) or a user-specified name.
8
+ #The receiver of relatively specified parameter functions will be the current module, if a user-specified name is given and
9
+ #the user-specified name is not the same as the basename of the target function, or the enclosing module, if no-user specified name is provided or
10
+ #if the user-specified name is the same as the basename of the parameter.
11
+ #
12
+ #Usage:
13
+ #
14
+ #module MyModule
15
+ # def function_method1(arg1, arg2); end
16
+ # def function_method2(arg1); end
17
+ #
18
+ # module MethodVersions
19
+ # extend Attachmethod
20
+ # attach_function :function_method1
21
+ # attach_function :function_method2
22
+ # end
23
+ #
24
+ #See the specs and the example folder for more examples.
25
+
26
+ def _receiver_and_message(method_name, function_symbol)
27
+ #Parse the function symbol to get the receiver and the function's basename
28
+ if function_symbol.match(/(.*)(?:::|\.)([^:.]*)$/)
29
+ receiver, function_method = $1, $2
30
+ receiver = receiver != "" && eval(receiver) || Object
31
+ else
32
+ #No explicit receiver
33
+ function_method = function_symbol
34
+ #If no receiver is specified, send it to self
35
+ receiver = self
36
+ #Can't send to self if method_name == function_method (or it will recurse and crash):
37
+ # send it to the outer module/class, if there's no outer module, send it to Object
38
+ if (method_name ||= function_method) == function_method
39
+ receiver = self.name
40
+ outer_module = receiver.rpartition('::').first
41
+ receiver = outer_module == "" ? Object : eval(outer_module)
42
+ end
43
+ end
44
+
45
+ return [ receiver, function_method ]
46
+ end
47
+
48
+ def attach_function(method_name = nil, function_symbol)
49
+ receiver, message = _receiver_and_message(method_name, function_symbol)
50
+
51
+ #If no method_name is given, use the basename of the function being attached
52
+ method_name ||= message
53
+
54
+ #The core functionality
55
+ define_method method_name do |*args|
56
+ receiver.send(message, self, *args)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,3 @@
1
+ module AttachFunction
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+
3
+ module Foo
4
+ def self.add_foo(string)
5
+ string + " foo"
6
+ end
7
+
8
+ #module MethodVersions
9
+ #extend AttachFunction
10
+ #attach_function :add_foo
11
+ #end
12
+ end
13
+ module Three
14
+ def self.add_three(number)
15
+ number + 3
16
+ end
17
+ #module MethodVersions
18
+ #extend AttachFunction
19
+ #attach_function :add_three
20
+ #end
21
+ end
22
+
23
+ module A; end
24
+ module ::A; module B; end; end
25
+
26
+ describe AttachFunction do
27
+ it 'has a version number' do
28
+ expect(AttachFunction::VERSION).not_to be nil
29
+ end
30
+ it "should define an instance method named 'attach_function'" do
31
+ expect(subject.instance_methods(false)).to include(:attach_function)
32
+ end
33
+
34
+ describe "resolving receivers" do
35
+ {
36
+ '::A.m' => [::A, 'm'],
37
+ '::A::m' => [::A, 'm'],
38
+ 'A.m' => [A, 'm'],
39
+ 'A::m' => [A, 'm'],
40
+ '::A::B.m' => [::A::B, 'm'],
41
+ '::A::B::m' => [::A::B, 'm'],
42
+ '::A::B.m' => [::A::B, 'm'],
43
+ '::A::B::m' => [::A::B, 'm'],
44
+ }.each_pair do |k,answers|
45
+ it "should resolve #{k} to #{answers.inspect}" do
46
+ extend AttachFunction
47
+ expect(_receiver_and_message('attach_here',k)).to eq(answers)
48
+ end
49
+ end
50
+
51
+ context "same name and target in the same scope" do
52
+ enclosing_scope = self
53
+ #Don't know how to test this since it creates an unnamed scope
54
+ xit "resolves relatively to the enclosing scope" do
55
+ this_scope = self
56
+ extend AttachFunction
57
+ expect(this_scope).not_to eq(enclosing_scope)
58
+ expect(_receiver_and_message(nil,'m')).to eq([ enclosing_scope, 'm'])
59
+ end
60
+ end
61
+
62
+
63
+ class MyString < String; end
64
+ class MyInt < Integer; end
65
+
66
+ let(:my_string) { MyString.new("my string") }
67
+ let(:my_int) { MyInt.new(7) }
68
+
69
+ it "works with absolute names from a different scope" do
70
+ module AddFooMethod; end
71
+ expect {
72
+ module AddFooMethod
73
+ extend AttachFunction
74
+ attach_function nil,"::Foo::add_foo"
75
+ end
76
+ }.not_to raise_error
77
+ my_string.extend(AddFooMethod)
78
+ expect(my_string).to respond_to(:add_foo)
79
+ expect(my_string.add_foo).to eq("my string foo")
80
+ end
81
+ context "same scope" do
82
+ class TestClass
83
+ def add_bar(obj)
84
+ puts "#{obj} + bar"
85
+ end
86
+ end
87
+ it "works with the same scope as long as we're using a different name" do
88
+ TestClass.extend AttachFunction
89
+ expect(TestClass.new).not_to respond_to(:add_bar_to_self)
90
+ class TestClass
91
+ attach_function :add_bar_to_self, :add_bar
92
+ end
93
+ expect(TestClass.new).to respond_to(:add_bar_to_self)
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,3 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ gem_name = File.basename(File.expand_path('../../', __FILE__))
3
+ require gem_name
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: attach_function
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Petr Skocik
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-27 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.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
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: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: '2.12'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: '2.12'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: True in-place file-editing (i.e, inode(input) == inode(output)) like
98
+ with sed, awk, or `ruby i -{p|n}e`, but truly in-place
99
+ email:
100
+ - pskocik@gmail.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - ".rspec"
107
+ - ".travis.yml"
108
+ - Gemfile
109
+ - Guardfile
110
+ - README.md
111
+ - Rakefile
112
+ - attach_function.gemspec
113
+ - examples/enclosing_scope.rb
114
+ - lib/attach_function.rb
115
+ - lib/attach_function/version.rb
116
+ - spec/attach_function_spec.rb
117
+ - spec/spec_helper.rb
118
+ homepage: https://github.com/pjump/attach_function.git
119
+ licenses:
120
+ - gplv2
121
+ metadata: {}
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubyforge_project:
138
+ rubygems_version: 2.2.1
139
+ signing_key:
140
+ specification_version: 4
141
+ summary: True in-place file-editing
142
+ test_files:
143
+ - spec/attach_function_spec.rb
144
+ - spec/spec_helper.rb