├── .ruby-gemset ├── config ├── flog.yml ├── yardstick.yml ├── devtools.yml ├── flay.yml ├── mutant.yml ├── rubocop.yml └── reek.yml ├── .rspec ├── Rakefile ├── lib ├── procto │ └── version.rb └── procto.rb ├── Gemfile ├── spec ├── shared │ └── procto_behavior.rb ├── unit │ └── procto │ │ ├── to_proc_spec.rb │ │ └── call_spec.rb └── spec_helper.rb ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── procto.gemspec ├── LICENSE └── README.md /.ruby-gemset: -------------------------------------------------------------------------------- 1 | procto 2 | -------------------------------------------------------------------------------- /config/flog.yml: -------------------------------------------------------------------------------- 1 | --- 2 | threshold: 5.5 3 | -------------------------------------------------------------------------------- /config/yardstick.yml: -------------------------------------------------------------------------------- 1 | --- 2 | threshold: 100 3 | -------------------------------------------------------------------------------- /config/devtools.yml: -------------------------------------------------------------------------------- 1 | --- 2 | unit_test_timeout: 0.1 3 | -------------------------------------------------------------------------------- /config/flay.yml: -------------------------------------------------------------------------------- 1 | --- 2 | threshold: 2 3 | total_score: 2 4 | -------------------------------------------------------------------------------- /config/mutant.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: procto 3 | namespace: Procto 4 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --profile 3 | --order random 4 | --format progress 5 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'rake' 4 | require 'devtools' 5 | 6 | Devtools.init_rake_tasks 7 | -------------------------------------------------------------------------------- /lib/procto/version.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | class Procto < Module 4 | # Gem version 5 | VERSION = '0.0.4'.freeze 6 | end 7 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | source 'https://rubygems.org' 4 | 5 | gemspec 6 | 7 | group :development do 8 | gem 'devtools', git: 'https://github.com/mbj/devtools.git' 9 | end 10 | 11 | group :test do 12 | gem 'coveralls', '~> 0.8.9' 13 | end 14 | -------------------------------------------------------------------------------- /spec/shared/procto_behavior.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | shared_context 'procto' do 4 | let(:text) { 'world' } 5 | let(:expected) { "Hello #{text}" } 6 | 7 | it 'returns the correct value' do 8 | expect(subject).to eql(expected) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## MAC OS 2 | .DS_Store 3 | 4 | ## TEXTMATE 5 | *.tmproj 6 | tmtags 7 | 8 | ## EMACS 9 | *~ 10 | \#* 11 | .\#* 12 | 13 | ## VIM 14 | *.swp 15 | 16 | ## Rubinius 17 | *.rbc 18 | .rbx 19 | 20 | ## PROJECT::GENERAL 21 | *.gem 22 | coverage 23 | profiling 24 | turbulence 25 | rdoc 26 | pkg 27 | tmp 28 | doc 29 | log 30 | .yardoc 31 | measurements 32 | 33 | ## BUNDLER 34 | .bundle 35 | Gemfile.lock 36 | 37 | ## PROJECT::SPECIFIC 38 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | before_install: gem install bundler 3 | bundler_args: --without yard guard benchmarks 4 | script: "bundle exec rake ci" 5 | rvm: 6 | - 2.3.0 7 | - ruby-head 8 | - rbx-19mode 9 | matrix: 10 | include: 11 | - rvm: jruby-19mode 12 | env: JRUBY_OPTS="$JRUBY_OPTS --debug" 13 | - rvm: jruby-head 14 | env: JRUBY_OPTS="$JRUBY_OPTS --debug" 15 | allow_failures: 16 | - rvm: rbx-19mode 17 | - rvm: jruby-19mode 18 | - rvm: jruby-head 19 | -------------------------------------------------------------------------------- /spec/unit/procto/to_proc_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | 5 | describe Procto, '.to_proc' do 6 | include_context 'procto' 7 | 8 | subject { proc.call(text) } 9 | 10 | let(:proc) { klass.to_proc } 11 | 12 | let(:klass) do 13 | Class.new do 14 | include Procto.call 15 | 16 | def initialize(text) 17 | @text = text 18 | end 19 | 20 | def call 21 | "Hello #{@text}" 22 | end 23 | end 24 | end 25 | 26 | it 'exposes a lambda proc' do 27 | expect(proc).to be_a_lambda 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | if ENV['COVERAGE'] == 'true' 4 | require 'simplecov' 5 | require 'coveralls' 6 | 7 | SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ 8 | SimpleCov::Formatter::HTMLFormatter, 9 | Coveralls::SimpleCov::Formatter 10 | ] 11 | 12 | SimpleCov.start do 13 | command_name 'spec:unit' 14 | add_filter 'config' 15 | add_filter 'spec' 16 | minimum_coverage 100 17 | end 18 | end 19 | 20 | require 'procto' 21 | require 'devtools/spec_helper' 22 | 23 | RSpec.configure do |config| 24 | config.expect_with :rspec do |c| 25 | c.syntax = :expect 26 | end 27 | config.mock_with :rspec do |c| 28 | c.syntax = :expect 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ------------ 3 | 4 | * If you want your code merged into the mainline, please discuss the proposed changes with me before doing any work on it. 5 | * Fork the project. 6 | * Make your feature addition or bug fix. 7 | * Follow this [style guide](https://github.com/dkubb/styleguide). 8 | * Add specs for it. This is important so I don't break it in a future version unintentionally. Tests must cover all branches within the code, and code must be fully covered. 9 | * Commit, do not mess with Rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull) 10 | * Run "rake ci". This must pass and not show any regressions in the metrics for the code to be merged. 11 | * Send me a pull request. Bonus points for topic branches. 12 | -------------------------------------------------------------------------------- /procto.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require File.expand_path('../lib/procto/version', __FILE__) 4 | 5 | Gem::Specification.new do |gem| 6 | gem.name = "procto" 7 | gem.version = Procto::VERSION.dup 8 | gem.authors = [ 'Martin Gamsjaeger (snusnu)' ] 9 | gem.email = [ 'gamsnjaga@gmail.com' ] 10 | gem.description = 'Turns your object into a method object' 11 | gem.summary = 'Defines Foo.call(*args) which invokes Foo.new(*args).bar ' 12 | gem.homepage = 'https://github.com/snusnu/procto' 13 | 14 | gem.require_paths = [ 'lib' ] 15 | gem.files = `git ls-files`.split("\n") 16 | gem.test_files = `git ls-files -- {spec}/*`.split("\n") 17 | gem.extra_rdoc_files = %w[LICENSE README.md CONTRIBUTING.md] 18 | gem.license = 'MIT' 19 | 20 | gem.add_development_dependency 'bundler', '~> 1' 21 | end 22 | -------------------------------------------------------------------------------- /spec/unit/procto/call_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | 5 | describe Procto, '.call' do 6 | subject { klass.call(text) } 7 | 8 | context 'with no name' do 9 | include_context 'procto' 10 | 11 | let(:klass) do 12 | Class.new do 13 | include Procto.call 14 | 15 | def initialize(text) 16 | @text = text 17 | end 18 | 19 | def call 20 | "Hello #{@text}" 21 | end 22 | end 23 | end 24 | end 25 | 26 | context 'with a name' do 27 | include_context 'procto' 28 | 29 | let(:name) { double('name', to_sym: :print) } 30 | 31 | let(:klass) do 32 | method_name = name 33 | Class.new do 34 | include Procto.call(method_name) 35 | 36 | def initialize(text) 37 | @text = text 38 | end 39 | 40 | def print 41 | "Hello #{@text}" 42 | end 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Martin Gamsjaeger (snusnu) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # procto 2 | 3 | [![Gem Version](https://badge.fury.io/rb/procto.png)][gem] 4 | [![Build Status](https://secure.travis-ci.org/snusnu/procto.png?branch=master)][travis] 5 | [![Dependency Status](https://gemnasium.com/snusnu/procto.png)][gemnasium] 6 | [![Code Climate](https://codeclimate.com/github/snusnu/procto.png)][codeclimate] 7 | [![Coverage Status](https://coveralls.io/repos/snusnu/procto/badge.png?branch=master)][coveralls] 8 | 9 | [gem]: https://rubygems.org/gems/procto 10 | [travis]: https://travis-ci.org/snusnu/procto 11 | [gemnasium]: https://gemnasium.com/snusnu/procto 12 | [codeclimate]: https://codeclimate.com/github/snusnu/procto 13 | [coveralls]: https://coveralls.io/r/snusnu/procto 14 | 15 | ## Usage 16 | 17 | ```ruby 18 | require 'procto' 19 | 20 | class Greeter 21 | include Procto.call 22 | 23 | def initialize(text) 24 | @text = text 25 | end 26 | 27 | def call 28 | "Hello #{@text}" 29 | end 30 | end 31 | 32 | Greeter.call('world') # => "Hello world" 33 | 34 | class Printer 35 | include Procto.call(:print) 36 | 37 | def initialize(text) 38 | @text = text 39 | end 40 | 41 | def print 42 | "Hello #{@text}" 43 | end 44 | end 45 | 46 | Printer.call('world') # => "Hello world" 47 | ``` 48 | 49 | ## Credits 50 | 51 | * [snusnu](https://github.com/snusnu) 52 | * [mbj](https://github.com/mbj) 53 | 54 | ## Contributing 55 | 56 | See [CONTRIBUTING.md](CONTRIBUTING.md) for details. 57 | 58 | ## Copyright 59 | 60 | Copyright © 2013 Martin Gamsjaeger (snusnu). See [LICENSE](LICENSE) for details. 61 | -------------------------------------------------------------------------------- /config/rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | Include: 3 | - '**/*.rake' 4 | - 'Gemfile' 5 | - 'Gemfile.devtools' 6 | Exclude: 7 | - '**/vendor/**' 8 | - '**/benchmarks/**' 9 | 10 | # Avoid parameter lists longer than five parameters. 11 | ParameterLists: 12 | Max: 1 13 | CountKeywordArgs: true 14 | 15 | # Avoid more than `Max` levels of nesting. 16 | BlockNesting: 17 | Max: 1 18 | 19 | # Align with the style guide. 20 | CollectionMethods: 21 | PreferredMethods: 22 | collect: 'map' 23 | inject: 'reduce' 24 | find: 'detect' 25 | find_all: 'select' 26 | 27 | # Do not force public/protected/private keyword to be indented at the same 28 | # level as the def keyword. My personal preference is to outdent these keywords 29 | # because I think when scanning code it makes it easier to identify the 30 | # sections of code and visually separate them. When the keyword is at the same 31 | # level I think it sort of blends in with the def keywords and makes it harder 32 | # to scan the code and see where the sections are. 33 | AccessModifierIndentation: 34 | Enabled: false 35 | 36 | # Limit line length 37 | LineLength: 38 | Max: 80 39 | 40 | # Disable documentation checking until a class needs to be documented once 41 | Documentation: 42 | Enabled: false 43 | 44 | # Do not favor modifier if/unless usage when you have a single-line body 45 | IfUnlessModifier: 46 | Enabled: false 47 | 48 | # Allow case equality operator (in limited use within the specs) 49 | CaseEquality: 50 | Enabled: false 51 | 52 | # Constants do not always have to use SCREAMING_SNAKE_CASE 53 | ConstantName: 54 | Enabled: false 55 | 56 | # Not all trivial readers/writers can be defined with attr_* methods 57 | TrivialAccessors: 58 | Enabled: false 59 | 60 | # Do not favor aligned parameters in method calls 61 | AlignParameters: 62 | Enabled: false 63 | 64 | HashSyntax: 65 | Enabled: false 66 | 67 | SpaceInsideBrackets: 68 | Enabled: false 69 | 70 | Lambda: 71 | Enabled: false # i personally like the look of multiline ->(arg) {} lambdas 72 | 73 | AndOr: 74 | Enabled: false # we agree to use and/or for control flow 75 | 76 | # Allow code to be aligned more nicely 77 | SpaceBeforeFirstArg: 78 | Enabled: false 79 | -------------------------------------------------------------------------------- /config/reek.yml: -------------------------------------------------------------------------------- 1 | --- 2 | Attribute: 3 | enabled: true 4 | exclude: [] 5 | BooleanParameter: 6 | enabled: true 7 | exclude: [] 8 | ClassVariable: 9 | enabled: true 10 | exclude: [] 11 | ControlParameter: 12 | enabled: true 13 | exclude: [] 14 | DataClump: 15 | enabled: true 16 | exclude: [] 17 | max_copies: 2 18 | min_clump_size: 2 19 | DuplicateMethodCall: 20 | enabled: true 21 | exclude: [] 22 | max_calls: 1 23 | allow_calls: [] 24 | FeatureEnvy: 25 | enabled: true 26 | exclude: [] 27 | IrresponsibleModule: 28 | enabled: false 29 | exclude: [] 30 | LongParameterList: 31 | enabled: true 32 | exclude: [] 33 | max_params: 1 34 | overrides: 35 | initialize: 36 | max_params: 2 37 | LongYieldList: 38 | enabled: true 39 | exclude: [] 40 | max_params: 1 41 | NestedIterators: 42 | enabled: true 43 | exclude: 44 | - Lupo#included # 2 45 | max_allowed_nesting: 1 46 | ignore_iterators: [] 47 | NilCheck: 48 | enabled: true 49 | exclude: [] 50 | RepeatedConditional: 51 | enabled: true 52 | exclude: [] 53 | max_ifs: 1 54 | TooManyInstanceVariables: 55 | enabled: true 56 | exclude: [] 57 | max_instance_variables: 1 58 | TooManyMethods: 59 | enabled: true 60 | exclude: [] 61 | max_methods: 3 62 | TooManyStatements: 63 | enabled: true 64 | exclude: [] 65 | max_statements: 4 66 | UncommunicativeMethodName: 67 | enabled: true 68 | exclude: [] 69 | reject: 70 | - !ruby/regexp /^[a-z]$/ 71 | - !ruby/regexp /[0-9]$/ 72 | - !ruby/regexp /[A-Z]/ 73 | accept: [] 74 | UncommunicativeModuleName: 75 | enabled: true 76 | exclude: [] 77 | reject: 78 | - !ruby/regexp /^.$/ 79 | - !ruby/regexp /[0-9]$/ 80 | accept: [] 81 | UncommunicativeParameterName: 82 | enabled: true 83 | exclude: [] 84 | reject: 85 | - !ruby/regexp /^.$/ 86 | - !ruby/regexp /[0-9]$/ 87 | - !ruby/regexp /[A-Z]/ 88 | accept: [] 89 | UncommunicativeVariableName: 90 | enabled: true 91 | exclude: [] 92 | reject: 93 | - !ruby/regexp /^.$/ 94 | - !ruby/regexp /[0-9]$/ 95 | - !ruby/regexp /[A-Z]/ 96 | accept: [] 97 | UnusedParameters: 98 | enabled: true 99 | exclude: [] 100 | UtilityFunction: 101 | enabled: true 102 | exclude: [] 103 | max_helper_calls: 0 104 | -------------------------------------------------------------------------------- /lib/procto.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | class Procto < Module 4 | # The default name of the instance method to be called 5 | DEFAULT_NAME = :call 6 | 7 | private_class_method :new 8 | 9 | # Return a module that turns the host into a method object 10 | # 11 | # @example without a name 12 | # 13 | # class Greeter 14 | # include Procto.call 15 | # 16 | # def initialize(text) 17 | # @text = text 18 | # end 19 | # 20 | # def call 21 | # "Hello #{@text}" 22 | # end 23 | # end 24 | # 25 | # Greeter.call('world') # => "Hello world" 26 | # 27 | # @example with a name 28 | # 29 | # class Printer 30 | # include Procto.call(:print) 31 | # 32 | # def initialize(text) 33 | # @text = text 34 | # end 35 | # 36 | # def print 37 | # "Hello #{@text}" 38 | # end 39 | # end 40 | # 41 | # Printer.call('world') # => "Hello world" 42 | # 43 | # @param [#to_sym] name 44 | # the name of the instance method to call 45 | # 46 | # @return [Procto] 47 | # 48 | # @api public 49 | def self.call(name = DEFAULT_NAME) 50 | new(name.to_sym) 51 | end 52 | 53 | # Initialize a new instance 54 | # 55 | # @param [Symbol] name 56 | # the name of the instance method to call 57 | # 58 | # @return [undefined] 59 | # 60 | # @api private 61 | def initialize(name) 62 | @block = ->(*args) { new(*args).public_send(name) } 63 | end 64 | 65 | private 66 | 67 | # Define the .call method on +host+ 68 | # 69 | # @param [Object] host 70 | # the hosting object 71 | # 72 | # @return [undefined] 73 | # 74 | # @api private 75 | def included(host) 76 | host.instance_exec(@block) do |block| 77 | define_singleton_method(:call, &block) 78 | end 79 | 80 | host.extend(ClassMethods) 81 | end 82 | 83 | # Procto module for adding .to_proc to host class 84 | module ClassMethods 85 | # Return the `call` singleton method as a lambda 86 | # 87 | # @example using a class as a proc 88 | # 89 | # class Shouter 90 | # include Procto.call 91 | # 92 | # def initialize(text) 93 | # @text = text 94 | # end 95 | # 96 | # def call 97 | # "#{@text.upcase}!" 98 | # end 99 | # end 100 | # 101 | # Shouter.to_proc.call('hello') # => "HELLO!" 102 | # %w[foo bar].map(&Shouter) # => ["FOO!", "BAR!"] 103 | # 104 | # @return [Proc] 105 | # 106 | # @api public 107 | def to_proc 108 | public_method(:call).to_proc 109 | end 110 | end 111 | private_constant(:ClassMethods) 112 | end # Procto 113 | --------------------------------------------------------------------------------