charlatan 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --order random
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in proxy_object.gemspec
4
+ gemspec
5
+
6
+ gem 'devtools', git: 'https://github.com/rom-rb/devtools', branch: 'master'
7
+
8
+ # Added by devtools
9
+ eval_gemfile 'Gemfile.devtools'
data/Gemfile.devtools ADDED
@@ -0,0 +1,55 @@
1
+ # encoding: utf-8
2
+
3
+ group :development do
4
+ gem 'rake', '~> 10.1.0'
5
+ gem 'rspec', '~> 2.14.1'
6
+ gem 'yard', '~> 0.8.7'
7
+ end
8
+
9
+ group :yard do
10
+ gem 'kramdown', '~> 1.2.0'
11
+ end
12
+
13
+ group :guard do
14
+ gem 'guard', '~> 1.8.1'
15
+ gem 'guard-bundler', '~> 1.0.0'
16
+ gem 'guard-rspec', '~> 3.0.2'
17
+ gem 'guard-rubocop', '~> 0.2.0'
18
+ gem 'guard-mutant', '~> 0.0.1'
19
+
20
+ # file system change event handling
21
+ gem 'listen', '~> 1.3.0'
22
+ gem 'rb-fchange', '~> 0.0.6', require: false
23
+ gem 'rb-fsevent', '~> 0.9.3', require: false
24
+ gem 'rb-inotify', '~> 0.9.0', require: false
25
+
26
+ # notification handling
27
+ gem 'libnotify', '~> 0.8.0', require: false
28
+ gem 'rb-notifu', '~> 0.0.4', require: false
29
+ gem 'terminal-notifier-guard', '~> 1.5.3', require: false
30
+ end
31
+
32
+ group :metrics do
33
+ gem 'coveralls', '~> 0.6.7'
34
+ gem 'flay', '~> 2.4.0'
35
+ gem 'flog', '~> 4.1.1'
36
+ gem 'reek', '~> 1.3.2'
37
+ gem 'rubocop', '~> 0.12.0'
38
+ gem 'simplecov', '~> 0.7.1'
39
+ gem 'yardstick', '~> 0.9.7', git: 'https://github.com/dkubb/yardstick.git'
40
+
41
+ platforms :ruby_19, :ruby_20 do
42
+ gem 'mutant', git: 'https://github.com/mbj/mutant.git'
43
+ gem 'yard-spellcheck', '~> 0.1.5'
44
+ end
45
+ end
46
+
47
+ group :benchmarks do
48
+ gem 'rbench', '~> 0.2.3'
49
+ end
50
+
51
+ platform :jruby do
52
+ group :jruby do
53
+ gem 'jruby-openssl', '~> 0.8.5'
54
+ end
55
+ end
data/Guardfile ADDED
@@ -0,0 +1,19 @@
1
+ guard :rspec do
2
+ #run all specs if configuration is modified
3
+ watch('Guardfile') { 'spec' }
4
+ watch('Gemfile.lock') { 'spec' }
5
+ watch('spec/spec_helper.rb') { 'spec' }
6
+
7
+ # run all specs if supporting files files are modified
8
+ watch(%r{\Aspec/(?:lib|support|shared)/.+\.rb\z}) { 'spec' }
9
+
10
+ # run unit specs if associated lib code is modified
11
+ watch(%r{\Alib/(.+)\.rb\z}) { |m| Dir["spec/unit/#{m[1]}"] }
12
+ watch(%r{\Alib/(.+)/support/(.+)\.rb\z}) { |m| Dir["spec/unit/#{m[1]}/#{m[2]}"] }
13
+ watch("lib/#{File.basename(File.expand_path('../', __FILE__))}.rb") { 'spec' }
14
+
15
+ # run a spec if it is modified
16
+ watch(%r{\Aspec/(?:unit|integration)/.+_spec\.rb\z})
17
+
18
+ notification :tmux, :display_message => true
19
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Piotr Solnica
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # Charlatan
2
+
3
+ Turn any object into a proxy delegating to another object. Like that:
4
+
5
+ ``` ruby
6
+ require 'charlatan'
7
+
8
+ class ArrayProxy
9
+ include Charlatan.new(:array)
10
+ end
11
+
12
+ array_proxy = ArrayProxy.new([])
13
+
14
+ array_proxy << 'Hello'
15
+ array_proxy << 'World'
16
+
17
+ array_proxy.size # => 2
18
+ array_proxy.join(' ') # => 'Hello World'
19
+
20
+ # always wraps responses with the proxy class
21
+ other = array_proxy + ['Oh Hai']
22
+ other.class # => ArrayProxy
23
+ ```
24
+
25
+ ## Why?
26
+
27
+ I dunno, you tell me. I just like it. Which I cannot say about SimpleDelegator.
28
+
29
+ ### TODO: explain why
30
+
31
+ ## Installation
32
+
33
+ Add this line to your application's Gemfile:
34
+
35
+ gem 'charlatan'
36
+
37
+ And then execute:
38
+
39
+ $ bundle
40
+
41
+ Or install it yourself as:
42
+
43
+ $ gem install charlatan
44
+
45
+ ## Contributing
46
+
47
+ 1. Fork it
48
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
49
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
50
+ 4. Push to the branch (`git push origin my-new-feature`)
51
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ # Added by devtools
4
+ require 'devtools'
5
+ Devtools.init_rake_tasks
data/charlatan.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'charlatan/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "charlatan"
8
+ spec.version = Charlatan::VERSION
9
+ spec.authors = ["Piotr Solnica"]
10
+ spec.email = ["piotr.solnica@gmail.com"]
11
+ spec.description = %q{Neat delegation for ruby objects}
12
+ spec.summary = spec.summary
13
+ spec.homepage = "https://github.com/solnic/charlatan"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ end
@@ -0,0 +1,2 @@
1
+ ---
2
+ unit_test_timeout: 0.1
data/config/flay.yml ADDED
@@ -0,0 +1,3 @@
1
+ ---
2
+ threshold: 0
3
+ total_score: 0
data/config/flog.yml ADDED
@@ -0,0 +1,2 @@
1
+ ---
2
+ threshold: 0
data/config/mutant.yml ADDED
@@ -0,0 +1,3 @@
1
+ ---
2
+ name: charlatan
3
+ namespace: Charlatan
data/config/reek.yml ADDED
@@ -0,0 +1,103 @@
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: true
29
+ exclude: []
30
+ LongParameterList:
31
+ enabled: true
32
+ exclude: []
33
+ max_params: 2
34
+ overrides:
35
+ initialize:
36
+ max_params: 3
37
+ LongYieldList:
38
+ enabled: true
39
+ exclude: []
40
+ max_params: 2
41
+ NestedIterators:
42
+ enabled: true
43
+ exclude: []
44
+ max_allowed_nesting: 1
45
+ ignore_iterators: []
46
+ NilCheck:
47
+ enabled: true
48
+ exclude: []
49
+ RepeatedConditional:
50
+ enabled: true
51
+ exclude: []
52
+ max_ifs: 1
53
+ TooManyInstanceVariables:
54
+ enabled: true
55
+ exclude: []
56
+ max_instance_variables: 3
57
+ TooManyMethods:
58
+ enabled: true
59
+ exclude: []
60
+ max_methods: 10
61
+ TooManyStatements:
62
+ enabled: true
63
+ exclude:
64
+ - each
65
+ max_statements: 2
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
@@ -0,0 +1,58 @@
1
+ AllCops:
2
+ Includes:
3
+ - '**/*.rake'
4
+ - 'Gemfile'
5
+ - 'Gemfile.devtools'
6
+ Excludes:
7
+ - '**/vendor/**'
8
+ - '**/benchmarks/**'
9
+
10
+ # Avoid parameter lists longer than five parameters.
11
+ ParameterLists:
12
+ Max: 3
13
+ CountKeywordArgs: true
14
+
15
+ # Avoid more than `Max` levels of nesting.
16
+ BlockNesting:
17
+ Max: 3
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
+ AccessControl:
34
+ Enabled: false
35
+
36
+ # Limit line length
37
+ LineLength:
38
+ Max: 79
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
@@ -0,0 +1,2 @@
1
+ ---
2
+ threshold: 100
data/lib/charlatan.rb ADDED
@@ -0,0 +1,62 @@
1
+ require "charlatan/version"
2
+
3
+ # Charlatan turns your object into a proxy which will forward all method missing
4
+ # calls to object it's wrapping
5
+ #
6
+ # @example
7
+ #
8
+ # class UserPresenter
9
+ # include Charlatan.new(:user)
10
+ # end
11
+ #
12
+ # user = OpenStruct.new(:name => "Jane")
13
+ # presenter = UserPresenter.new(user)
14
+ #
15
+ # user.name # => "Jane"
16
+ # presenter.user == user # => true
17
+ #
18
+ class Charlatan < Module
19
+ attr_reader :name
20
+
21
+ def initialize(name, options = {})
22
+ attr_reader name
23
+ ivar = "@#{name}"
24
+
25
+ define_method(:initialize) do |*args, &block|
26
+ instance_variable_set(ivar, args.first)
27
+ @__proxy_args = args[1..args.size] || []
28
+ end
29
+
30
+ define_method(:__proxy_target__) do
31
+ instance_variable_get(ivar)
32
+ end
33
+
34
+ define_method(:__proxy_kind__) do
35
+ options[:kind] || __proxy_target__.class
36
+ end
37
+
38
+ include Methods
39
+ end
40
+
41
+ module Methods
42
+ def respond_to_missing?(method_name, include_private)
43
+ __proxy_target__.respond_to?(method_name, include_private)
44
+ end
45
+
46
+ def method_missing(method_name, *args, &block)
47
+ if __proxy_target__.respond_to?(method_name)
48
+ response = __proxy_target__.public_send(method_name, *args, &block)
49
+
50
+ if response.equal?(__proxy_target__)
51
+ self
52
+ elsif response.kind_of?(__proxy_kind__)
53
+ self.class.new(*[response]+@__proxy_args)
54
+ else
55
+ response
56
+ end
57
+ else
58
+ super(method_name, *args, &block)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,3 @@
1
+ class Charlatan < Module
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+
3
+ # SimpleCov MUST be started before require 'rom-relation'
4
+ #
5
+ if ENV['COVERAGE'] == 'true'
6
+ require 'simplecov'
7
+ require 'coveralls'
8
+
9
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
10
+ SimpleCov::Formatter::HTMLFormatter,
11
+ Coveralls::SimpleCov::Formatter
12
+ ]
13
+
14
+ SimpleCov.start do
15
+ command_name 'spec:unit'
16
+
17
+ add_filter 'config'
18
+ add_filter 'spec'
19
+ end
20
+ end
21
+
22
+ require 'charlatan'
23
+ require 'devtools/spec_helper'
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+
3
+ describe Charlatan do
4
+ let(:other) { [1, 2, 3] }
5
+ let(:charlatan) { klass.new(other, 'stuff') }
6
+
7
+ let(:klass) do
8
+ Class.new {
9
+ include Charlatan.new(:other)
10
+
11
+ def initialize(other, extra, &block)
12
+ super(other, extra, &block)
13
+ end
14
+ }
15
+ end
16
+
17
+ it 'adds reader method for target object' do
18
+ expect(charlatan.other).to be(other)
19
+ end
20
+
21
+ it 'forwards method calls to target that returns other objects' do
22
+ expect(charlatan.size).to be(3)
23
+ end
24
+
25
+ it 'forwards method calls to target that returns equal object and returns self' do
26
+ expect(charlatan.concat([])).to equal(charlatan)
27
+ end
28
+
29
+ it 'forwards method calls to target that returns a new instance of itself' do
30
+ expect((charlatan + [4]).other).to eql(klass.new([1, 2, 3, 4], 'stuff').other)
31
+ end
32
+
33
+ it 'forwards method calls with a block' do
34
+ expect(charlatan.map(&:to_s).other).to eql(%w(1 2 3))
35
+ end
36
+
37
+ it 'does not mutate original args from the constructor' do
38
+ 2.times do
39
+ expect((charlatan + [4]).other).to eql(klass.new([1, 2, 3, 4], 'stuff').other)
40
+ end
41
+ end
42
+
43
+ it 'responds to methods defined on the target object' do
44
+ expect(charlatan).to respond_to(:concat)
45
+ end
46
+
47
+ it 'does not respond to private methods defined on the target object' do
48
+ expect(charlatan).not_to respond_to(:Integer)
49
+ end
50
+
51
+ it 'does not respond to unknown method names' do
52
+ expect(charlatan).not_to respond_to(:no_idea_what_you_want)
53
+ end
54
+
55
+ it 'raises no method error when method is not defined' do
56
+ expect { charlatan.not_here}.to raise_error(NoMethodError)
57
+ end
58
+
59
+ it 'forwards multiple levels down to the last target' do
60
+ charlatan1 = Class.new { include Charlatan.new(:arr) }
61
+ charlatan2 = Class.new { include Charlatan.new(:charlatan1) }
62
+ charlatan3 = Class.new { include Charlatan.new(:charlatan2) }
63
+
64
+ object = charlatan3.new(charlatan2.new(charlatan1.new([1, 2, 3])))
65
+
66
+ expect(object.size).to be(3)
67
+ end
68
+
69
+ context 'when target kind is provided' do
70
+ subject(:charlatan) { klass.new(other_class.new) }
71
+
72
+ let(:klass) do
73
+ Class.new { include Charlatan.new(:other, :kind => Enumerable) }
74
+ end
75
+
76
+ let(:other_class) do
77
+ Class.new {
78
+ include Enumerable
79
+
80
+ def to_set
81
+ Set[1, 2]
82
+ end
83
+ }
84
+ end
85
+
86
+ it 'forwards to target and returns charlatan for return value that matches the kind' do
87
+ expect(charlatan.to_set).to be_instance_of(charlatan.class)
88
+ end
89
+ end
90
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: charlatan
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Piotr Solnica
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-12-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Neat delegation for ruby objects
47
+ email:
48
+ - piotr.solnica@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - .rspec
55
+ - Gemfile
56
+ - Gemfile.devtools
57
+ - Guardfile
58
+ - LICENSE.txt
59
+ - README.md
60
+ - Rakefile
61
+ - charlatan.gemspec
62
+ - config/devtools.yml
63
+ - config/flay.yml
64
+ - config/flog.yml
65
+ - config/mutant.yml
66
+ - config/reek.yml
67
+ - config/rubocop.yml
68
+ - config/yardstick.yml
69
+ - lib/charlatan.rb
70
+ - lib/charlatan/version.rb
71
+ - spec/spec_helper.rb
72
+ - spec/unit/charlatan_spec.rb
73
+ homepage: https://github.com/solnic/charlatan
74
+ licenses:
75
+ - MIT
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 1.8.23
95
+ signing_key:
96
+ specification_version: 3
97
+ summary: ''
98
+ test_files:
99
+ - spec/spec_helper.rb
100
+ - spec/unit/charlatan_spec.rb
101
+ has_rdoc: