casting 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 87f6f44b2f7597c28ab23eb2d02fd06479558ac075f3b6646cd583ea98e5f945
4
- data.tar.gz: cdf55addbcdbaf848d4991fe8562b5bbeb50ea9b3fde1548f210ebb76d761186
3
+ metadata.gz: 2d5d851d5d006809d4704472259cd76692d864a6a2ddfc6ae829cfd65c15fe45
4
+ data.tar.gz: 14e30f699b3cf4a7b488a18f013bd843ee5bcbc8e6d00595205fef5ae0d77a05
5
5
  SHA512:
6
- metadata.gz: 71d6eac4b57b571f8c593606f645477402dd9557081ac684c9c5117257e111916b17ec830836cfe9063acae7572aef5b96e20d5219558bf466769119a95b42a1
7
- data.tar.gz: b81bf24dc30d94cbb520eb302f5173efe2f64b19b062a128071c30833df2f92c4a643b47717fa3092afdc2f20cc15a78631b2fa0fab1a80a8e6e9ed18c7bddc9
6
+ metadata.gz: 11e74b4ff72316f9bf5ee307b6f4f00603f09263127f59b8a53c22bb30d91b24f6e8bed3e60113b7e22da5903024a86acc170650d665e46e2cb7e0f5fad4b518
7
+ data.tar.gz: c8d5513c9d70e879be06fc440f345b709a0ba84be8daaa941b3f034e9c1f21aa6aa614b87a24aa30e52cb0147c334adf497afef661ecaf7867df4009782b1b2d
data/README.md CHANGED
@@ -345,6 +345,47 @@ object.uncast
345
345
 
346
346
  You'll be able to leave your objects as if they were never touched by the module where you defined your behavior.
347
347
 
348
+ ## It doesn't work!
349
+
350
+ You might be trying to override existing methods. Casting can help you apply behavior to an object using `delegate_missing_methods` but that depends on the methods being missing. In other words, if you have an `as_json` method that you want to change with a module, you won't be able to just `cast_as(MyJsonModule)` and have the `as_json` method from it be picked up because that will never hit `method_missing`.
351
+
352
+ If you want to override an existing method, you must do so explicitly.
353
+
354
+ This will _not_ work:
355
+
356
+ ```ruby
357
+ module MyJsonModule
358
+ def as_json
359
+ super.merge({ extra: 'details' })
360
+ end
361
+ end
362
+ some_object.cast_as(MyJsonModule)
363
+ some_object.as_json
364
+ ```
365
+
366
+ Instead, you'll need to explicitly override existing methods:
367
+
368
+ ```ruby
369
+ some_object.cast(:as_json, MyJsonModule)
370
+ ```
371
+
372
+ ## How can I speed it up?
373
+
374
+ Are you looping over lots of objects and want see better performance?
375
+
376
+ If you want to make things a bit faster, you can prepare the method delegation ahead of time and change the client object.
377
+
378
+ ```ruby
379
+ prepared_delegation = some_object.delegation(:some_delegated_method).to(MySpecialModule)
380
+ # Some looping code
381
+ big_list_of_objects.each do |object|
382
+ prepared_delegation.client = object
383
+ prepared_delegation.call
384
+ end
385
+ ```
386
+
387
+ Preparing the delegated method like this will probably speed things up for you but be sure to verify for yourself.
388
+
348
389
  ## Installation
349
390
 
350
391
  If you are using Bundler, add this line to your application's Gemfile:
data/Rakefile CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
- require 'rake/testtask'
3
+ require "rake/testtask"
4
4
 
5
5
  Rake::TestTask.new do |t|
6
- t.libs << 'test'
7
- t.test_files = FileList['test/*_test.rb']
6
+ t.libs << "test"
7
+ t.test_files = FileList["test/*_test.rb"]
8
8
  t.ruby_opts = ["-w"]
9
9
  t.verbose = true
10
10
  end
11
11
 
12
- task :default => :test
12
+ task default: :test
@@ -1,22 +1,21 @@
1
- require 'casting/delegation'
2
- require 'casting/missing_method_client'
3
- require 'casting/missing_method_client_class'
1
+ require "casting/delegation"
2
+ require "casting/missing_method_client"
3
+ require "casting/missing_method_client_class"
4
4
 
5
5
  module Casting
6
6
  module Client
7
-
8
7
  def self.included(base)
9
8
  def base.delegate_missing_methods(*which)
10
9
  Casting::Client.set_delegation_strategy(self, *which.reverse)
11
10
  end
12
11
 
13
- unless base.method_defined?('delegate')
12
+ unless base.method_defined?(:delegate)
14
13
  add_delegate_method_to(base)
15
14
  end
16
15
  end
17
16
 
18
17
  def self.extended(base)
19
- unless base.respond_to?('delegate')
18
+ unless base.respond_to?(:delegate)
20
19
  add_delegate_method_to(base.singleton_class)
21
20
  end
22
21
  end
@@ -31,28 +30,28 @@ module Casting
31
30
  end
32
31
 
33
32
  def delegate_missing_methods(*which)
34
- Casting::Client.set_delegation_strategy(self.singleton_class, *which.reverse)
33
+ Casting::Client.set_delegation_strategy(singleton_class, *which.reverse)
35
34
  end
36
35
 
37
36
  private
38
37
 
39
38
  def validate_attendant(attendant)
40
39
  if attendant == self
41
- raise Casting::InvalidAttendant.new('client can not delegate to itself')
40
+ raise Casting::InvalidAttendant.new("client can not delegate to itself")
42
41
  end
43
42
  end
44
43
 
45
44
  def self.set_delegation_strategy(base, *which)
46
45
  which = [:instance] if which.empty?
47
- which.map!{|selection|
48
- selection == :instance && selection = self.method(:set_method_missing_client)
49
- selection == :class && selection = self.method(:set_method_missing_client_class)
46
+ which.map! { |selection|
47
+ selection == :instance && selection = method(:set_method_missing_client)
48
+ selection == :class && selection = method(:set_method_missing_client_class)
50
49
  selection
51
- }.map{|meth| meth.call(base) }
50
+ }.map { |meth| meth.call(base) }
52
51
  end
53
52
 
54
53
  def self.add_delegate_method_to(base)
55
- base.class_eval{ alias_method :delegate, :cast }
54
+ base.class_eval { alias_method :delegate, :cast }
56
55
  end
57
56
 
58
57
  def self.set_method_missing_client(base)
@@ -29,7 +29,6 @@
29
29
  #
30
30
  module Casting
31
31
  module Context
32
-
33
32
  def self.extended(base)
34
33
  base.send(:include, InstanceMethods)
35
34
  end
@@ -38,26 +37,21 @@ module Casting
38
37
  attr_reader(*setup_args)
39
38
  private(*setup_args)
40
39
 
41
- if block
42
- define_method(:__custom_initialize, &block)
43
- else
44
- define_method(:__custom_initialize) do; end
45
- end
40
+ define_method(:__custom_initialize, &(block || proc {}))
46
41
 
47
42
  mod = Module.new
48
- line = __LINE__; string = %<
49
- def initialize(#{setup_args.map{|name| "#{name}:" }.join(',')})
43
+ mod.class_eval <<~INIT, __FILE__, __LINE__ + 1
44
+ def initialize(#{setup_args.map { |name| "#{name}:" }.join(",")})
50
45
  @assignments = []
51
46
  #{setup_args.map do |name|
52
- ["assign(",name,", '",name,"')"].join
47
+ ["assign(", name, ", '", name, "')"].join
53
48
  end.join("\n")}
54
49
  __custom_initialize
55
50
  Thread.current[:context] = self
56
51
  end
57
52
  attr_reader :assignments
58
- >
59
- mod.class_eval string, __FILE__, line
60
- const_set('Initializer', mod)
53
+ INIT
54
+ const_set(:Initializer, mod)
61
55
  include mod
62
56
  end
63
57
 
@@ -73,7 +67,7 @@ module Casting
73
67
  # Keep track of objects and their behaviors
74
68
  def assign(object, role_name)
75
69
  instance_variable_set("@#{role_name}", object)
76
- self.assignments << [object, self.role_for(role_name)]
70
+ assignments << [object, role_for(role_name)]
77
71
  end
78
72
 
79
73
  def contains?(obj)
@@ -91,12 +85,12 @@ module Casting
91
85
 
92
86
  # Find the first assigned role which implements a response for the given method name
93
87
  def role_implementing(object, method_name)
94
- assigned_roles(object).find{|role| role.method_defined?(method_name) } || raise(NoMethodError, "unknown method '#{method_name}' expected for #{object}")
88
+ assigned_roles(object).find { |role| role.method_defined?(method_name) } || raise(NoMethodError, "unknown method '#{method_name}' expected for #{object}")
95
89
  end
96
90
 
97
91
  # Get the roles for the given object
98
92
  def assigned_roles(object)
99
- assignments.select{|pair|
93
+ assignments.select { |pair|
100
94
  pair.first == object
101
95
  }.map(&:last)
102
96
  end
@@ -1,5 +1,4 @@
1
1
  module Casting
2
-
3
2
  class MissingAttendant < StandardError
4
3
  def message
5
4
  "You must set your attendant object using `to'."
@@ -9,7 +8,6 @@ module Casting
9
8
  class InvalidAttendant < StandardError; end
10
9
 
11
10
  class Delegation
12
-
13
11
  def self.prepare(delegated_method_name, client, &block)
14
12
  new(delegated_method_name: delegated_method_name, client: client, &block)
15
13
  end
@@ -46,41 +44,42 @@ module Casting
46
44
  def call(*args, **kwargs, &block)
47
45
  raise MissingAttendant.new unless attendant
48
46
 
49
- call_args = if args && !args.empty?
50
- args
51
- elsif @arguments && !@arguments.empty?
52
- @arguments
53
- end
54
- call_kwargs = if kwargs && !kwargs.empty?
55
- kwargs
56
- elsif @keyword_arguments && !@keyword_arguments.empty?
57
- @keyword_arguments
58
- end
59
-
60
- call_block = block || @block
61
- if call_args
62
- if call_kwargs
63
- bound_method.call(*call_args, **call_kwargs, &call_block)
64
- else
65
- bound_method.call(*call_args, &call_block)
66
- end
47
+ call_args = positional_arguments(args)
48
+ call_kwargs = keyword_arguments(kwargs)
49
+ call_block = block_argument(&block)
50
+
51
+ case
52
+ when call_args && call_kwargs
53
+ bound_method.call(*call_args, **call_kwargs, &call_block)
54
+ when call_args
55
+ bound_method.call(*call_args, &call_block)
56
+ when call_kwargs
57
+ bound_method.call(**call_kwargs, &call_block)
67
58
  else
68
- if call_kwargs
69
- bound_method.call(**call_kwargs, &call_block)
70
- else
71
- bound_method.call(&call_block)
72
- end
59
+ bound_method.call(&call_block)
73
60
  end
74
61
  end
75
62
 
76
63
  private
77
64
 
65
+ def block_argument(&block)
66
+ block || @block
67
+ end
68
+
69
+ def positional_arguments(options)
70
+ return options unless options.empty?
71
+ @arguments
72
+ end
73
+
74
+ def keyword_arguments(options)
75
+ return options unless options.empty?
76
+ @keyword_arguments
77
+ end
78
+
78
79
  def bound_method
79
- begin
80
- delegated_method.bind(client)
81
- rescue TypeError
82
- raise TypeError.new("`to' argument must be a module or an object with #{delegated_method_name} defined in a module")
83
- end
80
+ delegated_method.bind(client)
81
+ rescue TypeError
82
+ raise TypeError.new("`to' argument must be a module or an object with #{delegated_method_name} defined in a module")
84
83
  end
85
84
 
86
85
  def method_module
@@ -92,13 +91,12 @@ module Casting
92
91
 
93
92
  def delegated_method
94
93
  if Module === attendant
95
- attendant.instance_method(delegated_method_name)
94
+ attendant
96
95
  else
97
- attendant.method(delegated_method_name).owner.instance_method(delegated_method_name)
98
- end
96
+ attendant.method(delegated_method_name).owner
97
+ end.instance_method(delegated_method_name)
99
98
  rescue NameError => e
100
- raise InvalidAttendant.new(e.message)
99
+ raise InvalidAttendant, e.message
101
100
  end
102
-
103
101
  end
104
102
  end
data/lib/casting/enum.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  module Casting
2
2
  module Enum
3
+ extend self
4
+
3
5
  def enum(collection, *behaviors)
4
- enum = Enumerator.new do |yielder|
6
+ Enumerator.new do |yielder|
5
7
  collection.each do |item|
6
8
  yielder.yield(item.cast_as(*behaviors))
7
9
  end
@@ -1,19 +1,19 @@
1
1
  module Casting
2
2
  module MethodConsolidator
3
- def methods(all=true)
3
+ def methods(all = true)
4
4
  (super + delegated_methods(all)).uniq
5
5
  end
6
6
 
7
- def public_methods(include_super=true)
7
+ def public_methods(include_super = true)
8
8
  (super + delegated_public_methods(include_super)).uniq
9
9
  end
10
10
 
11
- def protected_methods(include_super=true)
11
+ def protected_methods(include_super = true)
12
12
  (super + delegated_protected_methods(include_super)).uniq
13
13
  end
14
14
 
15
- def private_methods(include_super=true)
15
+ def private_methods(include_super = true)
16
16
  (super + delegated_private_methods(include_super)).uniq
17
17
  end
18
18
  end
19
- end
19
+ end
@@ -1,8 +1,7 @@
1
- require 'casting/method_consolidator'
1
+ require "casting/method_consolidator"
2
2
 
3
3
  module Casting
4
4
  module MissingMethodClient
5
-
6
5
  def cast_as(*attendants)
7
6
  attendants.each do |attendant|
8
7
  validate_attendant(attendant)
@@ -12,7 +11,7 @@ module Casting
12
11
  self
13
12
  end
14
13
 
15
- def uncast(count=1)
14
+ def uncast(count = 1)
16
15
  count.times do
17
16
  attendant = __delegates__.shift
18
17
  attendant.uncast_object(self) if attendant.respond_to?(:uncast_object)
@@ -20,26 +19,26 @@ module Casting
20
19
  self
21
20
  end
22
21
 
23
- def delegated_methods(all=true)
24
- __delegates__.flat_map{|attendant|
22
+ def delegated_methods(all = true)
23
+ __delegates__.flat_map { |attendant|
25
24
  attendant_methods(attendant, all)
26
25
  }
27
26
  end
28
27
 
29
- def delegated_public_methods(include_super=true)
30
- __delegates__.flat_map{|attendant|
28
+ def delegated_public_methods(include_super = true)
29
+ __delegates__.flat_map { |attendant|
31
30
  attendant_public_methods(attendant, include_super)
32
31
  }
33
32
  end
34
33
 
35
- def delegated_protected_methods(include_super=true)
36
- __delegates__.flat_map{|attendant|
34
+ def delegated_protected_methods(include_super = true)
35
+ __delegates__.flat_map { |attendant|
37
36
  attendant_protected_methods(attendant, include_super)
38
37
  }
39
38
  end
40
39
 
41
- def delegated_private_methods(include_super=true)
42
- __delegates__.flat_map{|attendant|
40
+ def delegated_private_methods(include_super = true)
41
+ __delegates__.flat_map { |attendant|
43
42
  attendant_private_methods(attendant, include_super)
44
43
  }
45
44
  end
@@ -66,19 +65,19 @@ module Casting
66
65
  end
67
66
 
68
67
  def method_delegate(meth)
69
- __delegates__.find{|attendant|
68
+ __delegates__.find { |attendant|
70
69
  attendant.respond_to?(:method_defined?) && attendant.method_defined?(meth) ||
71
- attendant_methods(attendant).include?(meth)
70
+ attendant_methods(attendant).include?(meth)
72
71
  }
73
72
  end
74
73
 
75
- def attendant_methods(attendant, all=true)
74
+ def attendant_methods(attendant, all = true)
76
75
  collection = attendant_public_methods(attendant) + attendant_protected_methods(attendant)
77
76
  collection += attendant_private_methods(attendant) if all
78
77
  collection
79
78
  end
80
79
 
81
- def attendant_public_methods(attendant, include_super=true)
80
+ def attendant_public_methods(attendant, include_super = true)
82
81
  if Module === attendant
83
82
  attendant.public_instance_methods(include_super)
84
83
  else
@@ -86,7 +85,7 @@ module Casting
86
85
  end
87
86
  end
88
87
 
89
- def attendant_protected_methods(attendant, include_super=true)
88
+ def attendant_protected_methods(attendant, include_super = true)
90
89
  if Module === attendant
91
90
  attendant.protected_instance_methods(include_super)
92
91
  else
@@ -94,7 +93,7 @@ module Casting
94
93
  end
95
94
  end
96
95
 
97
- def attendant_private_methods(attendant, include_super=true)
96
+ def attendant_private_methods(attendant, include_super = true)
98
97
  if Module === attendant
99
98
  attendant.private_instance_methods(include_super)
100
99
  else
@@ -1,6 +1,5 @@
1
1
  module Casting
2
2
  module MissingMethodClientClass
3
-
4
3
  def self.extended(base)
5
4
  base.send(:include, InstanceMethods)
6
5
  end
@@ -26,7 +25,7 @@ module Casting
26
25
  end
27
26
 
28
27
  def method_class_delegate(meth)
29
- __class_delegates__.find{|attendant|
28
+ __class_delegates__.find { |attendant|
30
29
  attendant.method_defined?(meth)
31
30
  }
32
31
  end
@@ -44,7 +43,7 @@ module Casting
44
43
 
45
44
  def __delegates__
46
45
  Thread.current[:class_delegates] ||= {}
47
- Thread.current[:class_delegates][self.name] ||= []
46
+ Thread.current[:class_delegates][name] ||= []
48
47
  end
49
48
  end
50
- end
49
+ end
data/lib/casting/null.rb CHANGED
@@ -3,22 +3,28 @@ module Casting
3
3
  def self.instance_method(*_)
4
4
  Empty.instance_method(:null)
5
5
  end
6
+
6
7
  def self.method_defined?(*_)
7
8
  true
8
9
  end
9
10
  end
11
+
10
12
  module Blank
11
13
  def self.instance_method(*_)
12
14
  Empty.instance_method(:blank)
13
15
  end
16
+
14
17
  def self.method_defined?(*_)
15
18
  true
16
19
  end
17
20
  end
21
+
18
22
  module Empty
19
- def null(*, &_block); end
23
+ def null(*, &_block)
24
+ end
25
+
20
26
  def blank(*, &_block)
21
27
  ""
22
28
  end
23
- end
29
+ end
24
30
  end
@@ -1,6 +1,5 @@
1
1
  module Casting
2
2
  module SuperDelegate
3
-
4
3
  # Call the method of the same name defined in the next delegate stored in your object
5
4
  #
6
5
  # Because Casting creates an alternative method lookup path using a collection of delegates,
@@ -19,7 +18,7 @@ module Casting
19
18
  #
20
19
  # module FormalGreeter
21
20
  # include Casting::Super
22
- #
21
+ #
23
22
  # def greet
24
23
  # "#{super_delegate}, how do you do?"
25
24
  # end
@@ -33,24 +32,24 @@ module Casting
33
32
  owner = (mod unless mod == :none) || method_delegate(method_name)
34
33
 
35
34
  super_delegate_method = unbound_method_from_next_delegate(method_name, owner)
36
- super_delegate_method.bind(self).call(*args, **kwargs, &block)
35
+ super_delegate_method.bind_call(self, *args, **kwargs, &block)
37
36
  rescue NameError
38
- raise NoMethodError.new("super_delegate: no delegate method `#{method_name}' for #{self.inspect} from #{owner}")
37
+ raise NoMethodError.new("super_delegate: no delegate method `#{method_name}' for #{inspect} from #{owner}")
39
38
  end
40
-
39
+
41
40
  def unbound_method_from_next_delegate(method_name, *skipped)
42
41
  method_delegate_skipping(method_name, *skipped).instance_method(method_name)
43
42
  end
44
-
43
+
45
44
  def method_delegate_skipping(meth, skipped)
46
45
  skipped_index = __delegates__.index(skipped)
47
- __delegates__[(skipped_index + 1)..__delegates__.length].find{|attendant|
46
+ __delegates__[(skipped_index + 1)..__delegates__.length].find { |attendant|
48
47
  attendant_methods(attendant).include?(meth)
49
48
  }
50
49
  end
51
50
 
52
51
  def calling_location(call_stack)
53
- call_stack.reject{|line|
52
+ call_stack.reject { |line|
54
53
  line.to_s.match? Regexp.union(casting_library_matcher, gem_home_matcher, debugging_matcher)
55
54
  }.first
56
55
  end
@@ -58,17 +57,17 @@ module Casting
58
57
  def name_of_calling_method(call_stack)
59
58
  calling_location(call_stack).label.to_sym
60
59
  end
61
-
60
+
62
61
  def casting_library_matcher
63
- Regexp.new(Dir.pwd.to_s + '/lib')
62
+ Regexp.new(Dir.pwd.to_s + "/lib")
64
63
  end
65
64
 
66
65
  def gem_home_matcher
67
- Regexp.new(ENV['GEM_HOME'])
66
+ Regexp.new(ENV["GEM_HOME"])
68
67
  end
69
68
 
70
69
  def debugging_matcher
71
- Regexp.new('internal:trace_point')
70
+ Regexp.new("internal:trace_point")
72
71
  end
73
72
  end
74
73
  end
@@ -1,3 +1,3 @@
1
1
  module Casting
2
- VERSION = '1.0.1'
2
+ VERSION = "1.0.2"
3
3
  end
data/lib/casting.rb CHANGED
@@ -1,12 +1,11 @@
1
- require 'casting/version'
2
- require 'casting/client'
3
- require 'casting/enum'
4
- require 'casting/super_delegate'
5
- require 'casting/null'
6
- require 'casting/context'
1
+ require "casting/version"
2
+ require "casting/client"
3
+ require "casting/enum"
4
+ require "casting/super_delegate"
5
+ require "casting/null"
6
+ require "casting/context"
7
7
 
8
8
  module Casting
9
-
10
9
  class InvalidClientError < StandardError; end
11
10
 
12
11
  def self.delegating(assignments)
@@ -31,5 +30,4 @@ module Casting
31
30
 
32
31
  object.uncast
33
32
  end
34
-
35
33
  end
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: 1.0.1
4
+ version: 1.0.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: 2022-07-17 00:00:00.000000000 Z
11
+ date: 2023-01-07 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.
@@ -34,19 +34,6 @@ files:
34
34
  - lib/casting/null.rb
35
35
  - lib/casting/super_delegate.rb
36
36
  - lib/casting/version.rb
37
- - test/casting_enum_test.rb
38
- - test/casting_test.rb
39
- - test/class_refinement_test.rb
40
- - test/client_test.rb
41
- - test/context_test.rb
42
- - test/delegation_test.rb
43
- - test/frozen_client_test.rb
44
- - test/method_consolidator_test.rb
45
- - test/missing_method_client_test.rb
46
- - test/module_cleanup_test.rb
47
- - test/null_module_test.rb
48
- - test/super_test.rb
49
- - test/test_helper.rb
50
37
  homepage: http://github.com/saturnflyer/casting
51
38
  licenses:
52
39
  - MIT
@@ -70,17 +57,4 @@ rubygems_version: 3.2.27
70
57
  signing_key:
71
58
  specification_version: 4
72
59
  summary: Proper method delegation.
73
- test_files:
74
- - test/test_helper.rb
75
- - test/casting_enum_test.rb
76
- - test/casting_test.rb
77
- - test/class_refinement_test.rb
78
- - test/client_test.rb
79
- - test/context_test.rb
80
- - test/delegation_test.rb
81
- - test/frozen_client_test.rb
82
- - test/method_consolidator_test.rb
83
- - test/missing_method_client_test.rb
84
- - test/module_cleanup_test.rb
85
- - test/null_module_test.rb
86
- - test/super_test.rb
60
+ test_files: []