casting 0.7.1 → 0.7.2
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 +4 -4
- data/README.md +5 -19
- data/lib/casting/context.rb +125 -0
- data/lib/casting/delegation.rb +1 -1
- data/lib/casting/missing_method_client.rb +6 -1
- data/lib/casting/null.rb +7 -7
- data/lib/casting/prepared_delegation.rb +8 -5
- data/lib/casting/version.rb +1 -1
- data/test/test_helper.rb +2 -9
- metadata +11 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c87234df3ca2237a6d2e9d0d6de0f4beb8808cae
|
4
|
+
data.tar.gz: 114ccf9e9984a2323288b66982465ef7068a4dd0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc11494446dad4f1b1cd367d9fbd485ad78cf961884348344d076ff9456e1e8235af7be0d8547c1db84c0f953d79428da99d1a649702aa61d7b397d00b776fb9
|
7
|
+
data.tar.gz: e3d49bea1aaa35a9c08220cd3c07407fcf34657cdd02934ea2f9bd37adb999e3130e37690eebd21b44c884e5a542381b135f207eaaffe3919583877bee70fee4
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://travis-ci.org/saturnflyer/casting)
|
4
4
|
[](https://codeclimate.com/github/saturnflyer/casting)
|
5
|
-
[](https://codeclimate.com/github/saturnflyer/casting/coverage)
|
6
6
|
[](http://badge.fury.io/rb/casting)
|
7
7
|
|
8
8
|
## Add behavior to your objects without using extend
|
@@ -18,7 +18,7 @@ Here's a quick example that you might try in a Rails project:
|
|
18
18
|
# implement a module that contains information for the request response
|
19
19
|
# and apply it to an object in your system.
|
20
20
|
def show
|
21
|
-
|
21
|
+
@user = user.cast_as(UserRepresenter)
|
22
22
|
end
|
23
23
|
```
|
24
24
|
|
@@ -293,22 +293,14 @@ Person.instance_method(:hello) #=> #<UnboundMethod: Person#hello>
|
|
293
293
|
But if you attempt to use that `UnboundMethod` on an object that is not a `Person` you'll get
|
294
294
|
an error about a type mismatch.
|
295
295
|
|
296
|
-
Casting will bind an
|
296
|
+
Casting will bind an UnboundMethod method to a client object and execute the method as though it is
|
297
297
|
defined on the client object. Any reference to `self` from the method block will refer to the
|
298
298
|
client object.
|
299
299
|
|
300
|
-
|
301
|
-
|
302
|
-
According to [http://rubyspec.org](http://rubyspec.org) the behavior in MRI in 1.9 that allows this to happen is incorrect. In MRI (and JRuby) 1.9 you may unbind methods from an object that has been extended with a module, and bind them to another object of the same type that has *not* been extended with that module.
|
303
|
-
|
304
|
-
Casting uses this as a way to trick the interpreter into using the method where we want it and avoid forever extending the object of concern.
|
305
|
-
|
306
|
-
This changed in Ruby 2.0 and does not work. What does work (and is so much better) in 2.0 is that you may take any method from a module and apply it to any object. This means that Casting doesn't have to perform any tricks to temporarily apply behavior to an object.
|
307
|
-
|
308
|
-
For example, this fails in 1.9, but works in 2.0:
|
300
|
+
Rather than define methods on classes, you may take any method from a module and apply it to any object regardless of its class.
|
309
301
|
|
310
302
|
```ruby
|
311
|
-
GreetingModule.instance_method(:
|
303
|
+
GreetingModule.instance_method(:hello).bind(actor).call
|
312
304
|
```
|
313
305
|
|
314
306
|
Casting provides a convenience for doing this.
|
@@ -350,12 +342,6 @@ If you are using Bundler, add this line to your application's Gemfile:
|
|
350
342
|
gem 'casting'
|
351
343
|
```
|
352
344
|
|
353
|
-
If you're using Ruby 1.9, be sure to use a version lower than 0.7
|
354
|
-
|
355
|
-
```ruby
|
356
|
-
gem 'casting', '~> 0.6.9'
|
357
|
-
```
|
358
|
-
|
359
345
|
And then execute:
|
360
346
|
|
361
347
|
$ bundle
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# This is an experimental implementation of allowing contextual use of behaviors.
|
2
|
+
#
|
3
|
+
# This relies on versions of Ruby supporting refinements.
|
4
|
+
#
|
5
|
+
# You *must* include the following module and use it for refinements, and
|
6
|
+
# you *must* set the current context for the thread:
|
7
|
+
#
|
8
|
+
# class SomeContext
|
9
|
+
# using Casting::Context
|
10
|
+
# include Casting::Context
|
11
|
+
#
|
12
|
+
# initialize(:some, :thing)
|
13
|
+
# # doing that defines your constructor but would cause it too look for
|
14
|
+
# # modules named Some and Thing
|
15
|
+
# module Some; end
|
16
|
+
# module Thing; end
|
17
|
+
#
|
18
|
+
# # if you want different module names (why would you?) then you'd need
|
19
|
+
# # to do all this:
|
20
|
+
# def initialize(some, thing)
|
21
|
+
# @assignments = []
|
22
|
+
# assign [some, SomeRole], [thing, OtherRole]
|
23
|
+
# Thread.current[:context] = self
|
24
|
+
# end
|
25
|
+
# attr_reader :some, :thing, :assignments
|
26
|
+
#
|
27
|
+
# module SomeRole; end
|
28
|
+
# module OtherRole; end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# In order to use this the objects sent into the context contstructor *must*
|
32
|
+
# `include Casting::Client` so that the `cast` method is available to them
|
33
|
+
#
|
34
|
+
module Casting
|
35
|
+
module Context
|
36
|
+
|
37
|
+
def self.extended(base)
|
38
|
+
base.send(:include, InstanceMethods)
|
39
|
+
end
|
40
|
+
|
41
|
+
def initialize(*setup_args)
|
42
|
+
attr_reader(*setup_args)
|
43
|
+
private(*setup_args)
|
44
|
+
|
45
|
+
mod = Module.new
|
46
|
+
line = __LINE__; string = %<
|
47
|
+
def initialize(#{setup_args.map{|name| "#{name}:" }.join(',')})
|
48
|
+
@assignments = []
|
49
|
+
#{setup_args.map do |name|
|
50
|
+
["assign(",name,", '",name,"')"].join
|
51
|
+
end.join("\n")}
|
52
|
+
Thread.current[:context] = self
|
53
|
+
end
|
54
|
+
attr_reader :assignments
|
55
|
+
>
|
56
|
+
mod.class_eval string, __FILE__, line
|
57
|
+
const_set('Initializer', mod)
|
58
|
+
include mod
|
59
|
+
end
|
60
|
+
|
61
|
+
module InstanceMethods
|
62
|
+
def context
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
# Keep track of objects and their behaviors
|
67
|
+
def assign(object, role_name)
|
68
|
+
instance_variable_set("@#{role_name}", object)
|
69
|
+
self.assignments << [object, self.role_for(role_name)]
|
70
|
+
end
|
71
|
+
|
72
|
+
def contains?(obj)
|
73
|
+
assignments.map(&:first).include?(obj)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Execute the behavior from the role on the specifed object
|
77
|
+
def dispatch(object, method_name, *args, &block)
|
78
|
+
object.cast(method_name, context.role_implementing(object, method_name), *args, &block)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Find the first assigned role which implements a response for the given method name
|
82
|
+
def role_implementing(object, method_name)
|
83
|
+
assigned_roles(object).find{|role| role.method_defined?(method_name) } || raise(NoMethodError, "unknown method '#{method_name}' expected for #{object}")
|
84
|
+
end
|
85
|
+
|
86
|
+
# Get the roles for the given object
|
87
|
+
def assigned_roles(object)
|
88
|
+
assignments.select{|pair|
|
89
|
+
pair.first == object
|
90
|
+
}.map(&:last)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Get the behavior module for the named role.
|
94
|
+
# This role constant for special_person is SpecialPerson.
|
95
|
+
def role_for(name)
|
96
|
+
role_name = name.to_s.gsub(/(?:^|_)([a-z])/) { $1.upcase }
|
97
|
+
self.class.const_get(role_name)
|
98
|
+
rescue NameError
|
99
|
+
Module
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
refine Object do
|
104
|
+
def context
|
105
|
+
Thread.current[:context]
|
106
|
+
end
|
107
|
+
|
108
|
+
def context=(obj)
|
109
|
+
Thread.current[:context] = obj
|
110
|
+
end
|
111
|
+
|
112
|
+
# Get the object playing a particular role
|
113
|
+
def role(role_name)
|
114
|
+
context.send(role_name)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Execute the named method on the object plaing the name role
|
118
|
+
def tell(role_name, method_name, *args, &block)
|
119
|
+
if context == self || context.contains?(self)
|
120
|
+
context.dispatch(role(role_name), method_name, *args, &block)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/lib/casting/delegation.rb
CHANGED
@@ -7,7 +7,7 @@ module Casting
|
|
7
7
|
private :prepared_delegation
|
8
8
|
|
9
9
|
def initialize(delegated_method_name, client)
|
10
|
-
@prepared_delegation = PreparedDelegation.new(:
|
10
|
+
@prepared_delegation = PreparedDelegation.new(delegated_method_name: delegated_method_name, client: client)
|
11
11
|
end
|
12
12
|
|
13
13
|
def client
|
data/lib/casting/null.rb
CHANGED
@@ -1,24 +1,24 @@
|
|
1
1
|
module Casting
|
2
2
|
module Null
|
3
|
-
def self.instance_method(
|
3
|
+
def self.instance_method(_)
|
4
4
|
Empty.instance_method(:null)
|
5
5
|
end
|
6
|
-
def self.method_defined?(
|
6
|
+
def self.method_defined?(_)
|
7
7
|
true
|
8
8
|
end
|
9
9
|
end
|
10
10
|
module Blank
|
11
|
-
def self.instance_method(
|
11
|
+
def self.instance_method(_)
|
12
12
|
Empty.instance_method(:blank)
|
13
13
|
end
|
14
|
-
def self.method_defined?(
|
14
|
+
def self.method_defined?(_)
|
15
15
|
true
|
16
16
|
end
|
17
17
|
end
|
18
18
|
module Empty
|
19
|
-
def null(
|
20
|
-
def blank(
|
19
|
+
def null(*, &_block); end
|
20
|
+
def blank(*, &_block)
|
21
21
|
""
|
22
22
|
end
|
23
23
|
end
|
24
|
-
end
|
24
|
+
end
|
@@ -10,15 +10,15 @@ module Casting
|
|
10
10
|
|
11
11
|
class PreparedDelegation
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
private :delegated_method_name, :attendant, :arguments, :block
|
13
|
+
attr_accessor :client, :delegated_method_name, :attendant, :arguments, :block
|
14
|
+
private :block
|
16
15
|
|
17
|
-
def initialize(settings)
|
16
|
+
def initialize(**settings, &block)
|
18
17
|
@delegated_method_name = settings[:delegated_method_name]
|
19
18
|
@client = settings[:client]
|
20
19
|
@attendant = settings[:attendant]
|
21
20
|
@arguments = settings[:arguments]
|
21
|
+
@block = block
|
22
22
|
end
|
23
23
|
|
24
24
|
def to(object_or_module)
|
@@ -59,7 +59,10 @@ module Casting
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def method_module
|
62
|
-
|
62
|
+
mod = delegated_method.owner
|
63
|
+
unless mod.is_a?(Class)
|
64
|
+
mod
|
65
|
+
end
|
63
66
|
end
|
64
67
|
|
65
68
|
def delegated_method
|
data/lib/casting/version.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -1,12 +1,5 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
add_filter 'test'
|
4
|
-
end
|
5
|
-
|
6
|
-
require 'coveralls'
|
7
|
-
if ENV['COVERALLS']
|
8
|
-
Coveralls.wear!
|
9
|
-
end
|
1
|
+
require "codeclimate-test-reporter"
|
2
|
+
CodeClimate::TestReporter.start
|
10
3
|
|
11
4
|
require 'minitest/autorun'
|
12
5
|
require 'casting'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: casting
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jim Gay
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-07-28 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |-
|
14
14
|
Casting assists in method delegation which preserves the binding of 'self' to the object receiving a message.
|
@@ -20,8 +20,12 @@ executables: []
|
|
20
20
|
extensions: []
|
21
21
|
extra_rdoc_files: []
|
22
22
|
files:
|
23
|
+
- LICENSE
|
24
|
+
- README.md
|
25
|
+
- Rakefile
|
23
26
|
- lib/casting.rb
|
24
27
|
- lib/casting/client.rb
|
28
|
+
- lib/casting/context.rb
|
25
29
|
- lib/casting/delegation.rb
|
26
30
|
- lib/casting/method_consolidator.rb
|
27
31
|
- lib/casting/missing_method_client.rb
|
@@ -30,10 +34,6 @@ files:
|
|
30
34
|
- lib/casting/prepared_delegation.rb
|
31
35
|
- lib/casting/super_delegate.rb
|
32
36
|
- lib/casting/version.rb
|
33
|
-
- LICENSE
|
34
|
-
- Rakefile
|
35
|
-
- README.md
|
36
|
-
- test/test_helper.rb
|
37
37
|
- test/casting_19_test.rb
|
38
38
|
- test/casting_20_test.rb
|
39
39
|
- test/casting_test.rb
|
@@ -42,9 +42,10 @@ files:
|
|
42
42
|
- test/delegation_test.rb
|
43
43
|
- test/method_consolidator_test.rb
|
44
44
|
- test/missing_method_client_test.rb
|
45
|
-
- test/null_module_test.rb
|
46
45
|
- test/module_cleanup_test.rb
|
46
|
+
- test/null_module_test.rb
|
47
47
|
- test/super_test.rb
|
48
|
+
- test/test_helper.rb
|
48
49
|
homepage: http://github.com/saturnflyer/casting
|
49
50
|
licenses:
|
50
51
|
- MIT
|
@@ -55,17 +56,17 @@ require_paths:
|
|
55
56
|
- lib
|
56
57
|
required_ruby_version: !ruby/object:Gem::Requirement
|
57
58
|
requirements:
|
58
|
-
- -
|
59
|
+
- - ">="
|
59
60
|
- !ruby/object:Gem::Version
|
60
61
|
version: '0'
|
61
62
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
63
|
requirements:
|
63
|
-
- -
|
64
|
+
- - ">="
|
64
65
|
- !ruby/object:Gem::Version
|
65
66
|
version: '0'
|
66
67
|
requirements: []
|
67
68
|
rubyforge_project:
|
68
|
-
rubygems_version: 2.
|
69
|
+
rubygems_version: 2.6.6
|
69
70
|
signing_key:
|
70
71
|
specification_version: 4
|
71
72
|
summary: Proper method delegation.
|