cow_proxy 0.2.2 → 0.3.1

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: 5c1dc57faf10fbc39c1d95e9a43da5d5cbde931cb46be01d479c192949ae607c
4
- data.tar.gz: 336d11aa720b1e41408d9849c922584dc3867f3c838d7c695be157b6ffd489b4
3
+ metadata.gz: cfbd6c5980cadda3b7c607c1ce3abd0a29d7d59094a3a8c58e6c3d0ba1d2f887
4
+ data.tar.gz: 2b64b690e0a63f964a75efbc8a9f7aac30ed151747ec756802d6d7c059214148
5
5
  SHA512:
6
- metadata.gz: 15335c75aadb3656ec1c88b7b2afabce03b4d6a5fe3e018317a655bccda2a914a8261e8f98719c433c7c43d584e70d8c48ef63c6d61e980d506387b72b8ec6f0
7
- data.tar.gz: 3cb31d2fa247cafeb2c530dc931f3ebe2907a2b22ecfcf4f0a460b9b13a6a0c533d6e4967a88c75f7381f8c5d342aa52fce9aa14b6323487f076ed21fdc0ced5
6
+ metadata.gz: 71b0c69acbad373ce1b7344474423bfa15e4c008a2b8c73ef256af9aa0b6893dd99e0efef4aa491c01be39e71846015f8486c8ed595f849edfef1b5a588ea7eb
7
+ data.tar.gz: da7b070003fb7602dd7032d59d7278b18607088f68d0f8432908d6c40c68aa2a40e31f9c693e876182af21ef9e1316ef25bb92a10295b56fe1053db65888f8c2
data/README.md CHANGED
@@ -27,7 +27,7 @@ CowProxy.wrap(obj)
27
27
 
28
28
  It doesn't need to be a frozen object, it can be frozen later or only have references to frozen objects, but no object will be duplicated until some change is requested on frozen object.
29
29
 
30
- To create a CowProxy class for custom class, create a new class which inherits from CowProxy::WrapClass(CustomClass):
30
+ To create a CowProxy class for custom class, create a new class which inherits from `CowProxy::WrapClass(CustomClass)`:
31
31
 
32
32
  ```ruby
33
33
  module YourModule
@@ -40,7 +40,7 @@ obj.freeze
40
40
  proxy = CowProxy.wrap(obj)
41
41
  ```
42
42
 
43
- You can create proxy in CowProxy module too:
43
+ You can create proxy in `CowProxy` module too:
44
44
 
45
45
  ```ruby
46
46
  module CowProxy
@@ -49,9 +49,9 @@ module CowProxy
49
49
  end
50
50
  ```
51
51
 
52
- If your custom class has some getters with arguments, such as [] method of Array or Hash, you will have to define it in your Proxy so it wraps returned values and memoizes them, and override _copy_on_write to set memoized proxies to duplicated object. Wrapped object can be accessed from proxy with \__getobj\__ method. You can see an example in CowProxy::Indexable module, which is used for Array and Hash classes.
52
+ If your custom class has some getters with arguments, such as `[]` method of `Array` or `Hash`, you will have to define it in your Proxy so it wraps returned values and memoizes them, and override `__copy_on_write__` to set memoized proxies to duplicated object. Wrapped object can be accessed from proxy with `__getobj__` method. You can see an example in `CowProxy::Indexable` module, which is used for `Array` and `Hash` classes.
53
53
 
54
- If your custom class inherits from a class with CowProxy class, you don't need to create an own class, unless you need to override some method. But you must inherit from WrapClass(CustomClass) so your new proxy class is registered:
54
+ If your custom class inherits from a class with `CowProxy` class, you don't need to create an own class, unless you need to override some method. But you must inherit from `WrapClass(CustomClass)` so your new proxy class is registered:
55
55
 
56
56
  ```ruby
57
57
  module CowProxy
@@ -75,7 +75,7 @@ module CowProxy
75
75
  @custom_var = {}
76
76
  end
77
77
 
78
- def _copy_on_write(*)
78
+ def __copy_on_write__(*)
79
79
  super.tap do
80
80
  if @custom_var
81
81
  @custom_var.each do |k, v|
data/lib/cow_proxy.rb CHANGED
@@ -10,14 +10,10 @@
10
10
  # obj = CustomClass.new
11
11
  # obj.freeze
12
12
  # proxy = CowProxy.wrap(obj)
13
-
14
13
  module CowProxy
15
14
  autoload :Enumerable, 'cow_proxy/enumerable.rb'
16
15
  autoload :Indexable, 'cow_proxy/indexable.rb'
17
16
  class << self
18
- # @!visibility private
19
- @@wrapper_classes = {}
20
-
21
17
  # Create new proxy class for klass, with copy on write enabled.
22
18
  #
23
19
  # In other case CowProxy will wrap objects of klass without copy on write
@@ -30,8 +26,8 @@ module CowProxy
30
26
  # end
31
27
  #
32
28
  # @return new proxy class, so it can be used to create a class which inherits from it
33
- def WrapClass(klass)
34
- _WrapClass(klass)
29
+ def WrapClass(klass) # rubocop:disable Naming/MethodName
30
+ _wrap_class(klass)
35
31
  end
36
32
 
37
33
  # Register proxy to be used when wrapping an object of klass.
@@ -43,8 +39,10 @@ module CowProxy
43
39
  #
44
40
  # @return proxy_klass
45
41
  def register_proxy(klass, proxy_klass)
46
- debug { "register proxy for #{klass} with #{proxy_klass}#{" < #{proxy_klass.superclass}" if proxy_klass}" } unless @@wrapper_classes[klass]
47
- @@wrapper_classes[klass] ||= proxy_klass
42
+ return if @wrapper_classes&.dig(klass)
43
+ debug { "register proxy for #{klass} with #{proxy_klass}#{" < #{proxy_klass.superclass}" if proxy_klass}" }
44
+ @wrapper_classes ||= {}
45
+ @wrapper_classes[klass] = proxy_klass
48
46
  end
49
47
 
50
48
  # Returns a proxy wrapping obj, using registered class for obj's class.
@@ -67,10 +65,10 @@ module CowProxy
67
65
  # if none is registered
68
66
  def wrapper_class(obj)
69
67
  # only classes with defined wrapper and Structs has COW enabled by default
70
- if @@wrapper_classes.has_key?(obj.class)
71
- @@wrapper_classes[obj.class]
68
+ if @wrapper_classes&.has_key?(obj.class)
69
+ @wrapper_classes[obj.class]
72
70
  else
73
- _WrapClass(obj.class, obj.class < Struct, true)
71
+ _wrap_class(obj.class, obj.class < ::Struct, true)
74
72
  end
75
73
  end
76
74
 
@@ -87,38 +85,56 @@ module CowProxy
87
85
  end
88
86
 
89
87
  private
90
- def _WrapClass(klass, cow = true, register = false)
91
- proxy_superclass = get_proxy_klass_for(klass.superclass) || Base
92
- debug { "create new proxy class for #{klass}#{" from #{proxy_superclass}" if proxy_superclass}" }
88
+
89
+ def _wrap_class(klass, cow = true, register = false)
90
+ proxy_superclass = get_proxy_klass_for(klass.superclass)
91
+ debug { "create new proxy class for #{klass} from #{proxy_superclass} with#{'out' unless cow} cow" }
93
92
  proxy_klass = Class.new(proxy_superclass) do |k|
94
93
  k.wrapped_class = klass
95
94
  end
96
95
  register_proxy klass, proxy_klass if register
97
- methods = klass.instance_methods
98
- methods -= [:__copy_on_write__, :__wrap__, :__wrapped_value__, :__wrapped_method__, :__getobj__, :enum_for, :send, :===, :frozen?]
99
- methods -= proxy_superclass.wrapped_class.instance_methods if proxy_superclass.wrapped_class
100
- methods -= [:inspect] if ENV['DEBUG']
96
+ define_case_equality klass
101
97
 
98
+ methods = methods_to_wrap(klass, proxy_superclass)
102
99
  proxy_klass.module_eval do
103
100
  methods.each do |method|
104
101
  define_method method, proxy_klass.wrapping_block(method, cow)
105
102
  end
106
103
  end
107
- proxy_klass.define_singleton_method :public_instance_methods do |all=true|
104
+ proxy_klass.define_singleton_method :public_instance_methods do |all = true|
108
105
  super(all) - klass.protected_instance_methods
109
106
  end
110
- proxy_klass.define_singleton_method :protected_instance_methods do |all=true|
107
+ proxy_klass.define_singleton_method :protected_instance_methods do |all = true|
111
108
  super(all) | klass.protected_instance_methods
112
109
  end
113
110
  proxy_klass
114
111
  end
115
112
 
113
+ # fix case equality for wrapped objects, kind_of?(klass) works, but klass === was failing
114
+ def define_case_equality(klass)
115
+ class << klass
116
+ def ===(other)
117
+ CowProxy::Base === other ? other.kind_of?(self) : super(other) # rubocop:disable Style/CaseEquality,Style/ClassCheck
118
+ end
119
+ end
120
+ end
121
+
122
+ def methods_to_wrap(klass, proxy_superclass)
123
+ methods = klass.instance_methods
124
+ methods -= %i[__copy_on_write__ __wrap__ __wrapped_value__ __wrapped_method__ __getobj__
125
+ __copy_parent__ enum_for send === frozen?]
126
+ methods -= proxy_superclass.wrapped_class.instance_methods if proxy_superclass.wrapped_class
127
+ methods -= [:inspect] if ENV['DEBUG']
128
+ methods
129
+ end
130
+
116
131
  def get_proxy_klass_for(klass)
117
- wrapper = nil
132
+ proxy_klass = nil
118
133
  klass.ancestors.each do |ancestor|
119
- wrapper = @@wrapper_classes[ancestor] and break
134
+ proxy_klass = @wrapper_classes&.dig ancestor
135
+ break if proxy_klass
120
136
  end
121
- wrapper
137
+ proxy_klass || Base
122
138
  end
123
139
  end
124
140
  end
@@ -128,19 +144,16 @@ end
128
144
  CowProxy.register_proxy klass, nil
129
145
  end
130
146
 
131
- if 1.class == Integer
147
+ if 1.instance_of? Integer
132
148
  CowProxy.register_proxy Integer, nil
133
149
  else
134
- if defined? Fixnum
135
- CowProxy.register_proxy Fixnum, nil
136
- end
137
- if defined? Bignum
138
- CowProxy.register_proxy Bignum, nil
139
- end
150
+ CowProxy.register_proxy Fixnum, nil if defined? Fixnum
151
+ CowProxy.register_proxy Bignum, nil if defined? Bignum
140
152
  end
141
153
 
142
154
  require 'cow_proxy/base.rb'
143
155
  require 'cow_proxy/array.rb'
144
156
  require 'cow_proxy/hash.rb'
145
157
  require 'cow_proxy/string.rb'
158
+ require 'cow_proxy/struct.rb'
146
159
  require 'cow_proxy/set.rb'
@@ -17,6 +17,7 @@ module CowProxy
17
17
  end
18
18
 
19
19
  protected
20
+
20
21
  # Return block with proxy implementation.
21
22
  #
22
23
  # Block calls a method in wrapped object
@@ -28,11 +29,12 @@ module CowProxy
28
29
  # @return [Proc] Block with proxy implementation.
29
30
  def wrapping_block(method, cow_enabled)
30
31
  lambda do |*args, &block|
31
- inst_var = "@#{method}" if method.to_s =~ /^\w+$/
32
- return _instance_variable_get(inst_var) if inst_var && _instance_variable_defined?(inst_var)
33
- if method.to_s =~ /^(\w+)=$/ && _instance_variable_defined?("@#{$1}")
34
- CowProxy.debug { "remove #{$1}" }
35
- _remove_instance_variable "@#{$1}"
32
+ if method.to_s =~ /^\w+$/
33
+ inst_var = "@#{method}"
34
+ return _instance_variable_get(inst_var) if _instance_variable_defined?(inst_var)
35
+ elsif method.to_s =~ /^(\w+)=$/ && _instance_variable_defined?("@#{Regexp.last_match(1)}")
36
+ CowProxy.debug { "remove #{Regexp.last_match(1)}" }
37
+ _remove_instance_variable "@#{Regexp.last_match(1)}"
36
38
  end
37
39
  __wrapped_method__(inst_var, cow_enabled, method, *args, &block)
38
40
  end
@@ -53,6 +55,7 @@ module CowProxy
53
55
  end
54
56
 
55
57
  protected
58
+
56
59
  # Replace wrapped object with a copy, so object can
57
60
  # be modified.
58
61
  #
@@ -63,56 +66,63 @@ module CowProxy
63
66
  def __copy_on_write__(parent = true)
64
67
  CowProxy.debug { "copy on write on #{__getobj__.class.name}" }
65
68
  return @delegate_dc_obj if @dc_obj_duplicated
66
- @delegate_dc_obj = @delegate_dc_obj.dup.tap do |new_target|
67
- @dc_obj_duplicated = true
68
- if parent && @parent_proxy
69
- @parent_proxy.send :__copy_on_write__, false
70
- if @parent_var
71
- parent_dc = @parent_proxy._instance_variable_get(:@delegate_dc_obj)
72
- method = @parent_var[1..-1] + '='
73
- parent_dc.send(method, new_target)
74
- end
75
- end
76
- end
69
+ @delegate_dc_obj = @delegate_dc_obj.dup
70
+ @dc_obj_duplicated = true
71
+ __copy_parent__ if parent && @parent_proxy
72
+ @delegate_dc_obj
77
73
  end
78
74
 
79
75
  private
76
+
80
77
  def __getobj__
81
78
  @delegate_dc_obj
82
79
  end
83
80
 
81
+ def __copy_parent__
82
+ @parent_proxy.send :__copy_on_write__, false
83
+ return unless @parent_var
84
+ parent_dc = @parent_proxy._instance_variable_get(:@delegate_dc_obj)
85
+ method = @parent_var[1..-1] + '='
86
+ parent_dc.send(method, @delegate_dc_obj)
87
+ end
88
+
84
89
  def __wrap__(value, inst_var = nil)
85
- if value.frozen?
86
- CowProxy.debug { "wrap #{value.class.name} with parent #{__getobj__.class.name}" }
87
- wrap_klass = CowProxy.wrapper_class(value)
88
- wrap_value = wrap_klass.new(value, self, inst_var) if wrap_klass
89
- _instance_variable_set(inst_var, wrap_value) if inst_var && wrap_value
90
- wrap_value
91
- end
90
+ return unless value.frozen?
91
+ CowProxy.debug { "wrap #{value.class.name} with parent #{__getobj__.class.name}" }
92
+ wrap_klass = CowProxy.wrapper_class(value)
93
+ wrap_value = wrap_klass&.new(value, self, inst_var)
94
+ _instance_variable_set(inst_var, wrap_value) if inst_var && wrap_value
95
+ wrap_value
92
96
  end
93
97
 
94
98
  def __wrapped_value__(inst_var, method, *args, &block)
95
- CowProxy.debug { "run on #{__getobj__.class.name} (#{__getobj__.object_id}) #{method} #{args.inspect unless args.empty?}" }
99
+ CowProxy.debug do
100
+ "run on #{__getobj__.class.name} (#{__getobj__.object_id}) "\
101
+ "#{method} #{args.inspect unless args.empty?}"
102
+ end
96
103
  value = __getobj__.__send__(method, *args, &block)
97
- wrap_value = __wrap__(value, inst_var) if inst_var && args.empty? && block.nil?
104
+ wrap_value = __wrap__(value, inst_var) if inst_var && args.empty? && !block
98
105
  wrap_value || value
99
106
  end
100
107
 
101
108
  def __wrapped_method__(inst_var, cow, method, *args, &block)
102
109
  __wrapped_value__(inst_var, method, *args, &block)
103
- rescue => e
104
- CowProxy.debug { "error #{e.message} on #{__getobj__.class.name} (#{__getobj__.object_id}) #{method} #{args.inspect unless args.empty?} with#{'out' unless cow} cow" }
110
+ rescue StandardError => e
111
+ CowProxy.debug do
112
+ "error #{e.message} on #{__getobj__.class.name} (#{__getobj__.object_id}) #{method} "\
113
+ "#{args.inspect unless args.empty?} with#{'out' unless cow} cow"
114
+ end
105
115
  raise unless cow && e.message =~ /^can't modify frozen/
106
- CowProxy.debug { "copy on write to run #{method} #{args.inspect unless args.empty?} (#{e.message})" }
116
+ CowProxy.debug { "copy on write to run #{method}" }
107
117
  __copy_on_write__
108
118
  CowProxy.debug { "new target #{__getobj__.class.name} (#{__getobj__.object_id})" }
109
119
  __wrapped_value__(inst_var, method, *args, &block)
110
120
  end
111
121
 
112
- alias :_instance_variable_get :instance_variable_get
113
- alias :_instance_variable_set :instance_variable_set
114
- alias :_remove_instance_variable :remove_instance_variable
115
- alias :_instance_variable_defined? :instance_variable_defined?
116
- alias :_instance_variables? :instance_variable_defined?
122
+ alias _instance_variable_get instance_variable_get
123
+ alias _instance_variable_set instance_variable_set
124
+ alias _remove_instance_variable remove_instance_variable
125
+ alias _instance_variable_defined? instance_variable_defined?
126
+ alias _instance_variables instance_variables
117
127
  end
118
- end
128
+ end
@@ -51,6 +51,7 @@ module CowProxy
51
51
  end
52
52
 
53
53
  private
54
+
54
55
  def mutable_selector(method, &block)
55
56
  return send(method) unless block
56
57
  @delegate_dc_obj = send(method, &block).tap do
@@ -60,6 +60,13 @@ module CowProxy
60
60
  map(&:last)
61
61
  end
62
62
 
63
+ # Returns true if the given key is present in hash.
64
+ #
65
+ # @return [Array] Wrapped values from hash
66
+ def include?(key)
67
+ key?(key)
68
+ end
69
+
63
70
  # Used for merging into another Hash
64
71
  # needs to return unwrapped Hash
65
72
  #
@@ -9,7 +9,7 @@ module CowProxy
9
9
  #
10
10
  # @return CowProxy wrapped value from wrapped object
11
11
  def [](index)
12
- return @hash[index] if @hash && @hash.has_key?(index)
12
+ return @hash[index] if @hash&.has_key?(index)
13
13
 
14
14
  begin
15
15
  value = __getobj__[index]
@@ -20,6 +20,15 @@ module CowProxy
20
20
  end
21
21
  end
22
22
 
23
+ # Extracts the nested value specified by the sequence of idx objects by calling dig
24
+ # at each step, returning nil if any intermediate step is nil.
25
+ #
26
+ # @return CowProxy wrapped value from wrapped object
27
+ def dig(key, *args)
28
+ value = self[key]
29
+ args.empty? ? value : value&.dig(*args)
30
+ end
31
+
23
32
  # Extends {CowProxy::Base#initialize}
24
33
  def initialize(*)
25
34
  super
@@ -27,6 +36,7 @@ module CowProxy
27
36
  end
28
37
 
29
38
  protected
39
+
30
40
  # Copy wrapped values to duplicated wrapped object
31
41
  # @see CowProxy::Base#__copy_on_write__
32
42
  # @return duplicated wrapped object
data/lib/cow_proxy/set.rb CHANGED
@@ -4,5 +4,4 @@ module CowProxy
4
4
  # Wrapper class for Set
5
5
  class Set < WrapClass(::Set)
6
6
  end
7
-
8
7
  end
@@ -0,0 +1,13 @@
1
+ module CowProxy
2
+ # Wrapper class for Struct
3
+ class Struct < WrapClass(::Struct)
4
+ # Extracts the nested value specified by the sequence of idx objects by
5
+ # calling dig at each step, returning nil if any intermediate step is nil.
6
+ #
7
+ # @return CowProxy wrapped value from wrapped object
8
+ def dig(key, *args)
9
+ value = send(key)
10
+ args.empty? ? value : value&.dig(*args)
11
+ end
12
+ end
13
+ end
@@ -1,8 +1,8 @@
1
1
  module CowProxy
2
2
  module Version
3
3
  MAJOR = 0
4
- MINOR = 2
5
- PATCH = 2
4
+ MINOR = 3
5
+ PATCH = 1
6
6
 
7
7
  STRING = [MAJOR, MINOR, PATCH].compact.join('.')
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cow_proxy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergio Cambra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-25 00:00:00.000000000 Z
11
+ date: 2019-03-29 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Make a COW proxy for a frozen object (or deep frozen), it will delegate
14
14
  every read method to proxied object, wrap value in COW proxy if frozen. Trying to
@@ -29,6 +29,7 @@ files:
29
29
  - lib/cow_proxy/indexable.rb
30
30
  - lib/cow_proxy/set.rb
31
31
  - lib/cow_proxy/string.rb
32
+ - lib/cow_proxy/struct.rb
32
33
  - lib/cow_proxy/version.rb
33
34
  homepage: http://github.com/Programatica/cow_proxy
34
35
  licenses:
@@ -42,7 +43,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
42
43
  requirements:
43
44
  - - ">="
44
45
  - !ruby/object:Gem::Version
45
- version: 1.9.3
46
+ version: 2.3.0
46
47
  required_rubygems_version: !ruby/object:Gem::Requirement
47
48
  requirements:
48
49
  - - ">="