polyfill 0.7.0 → 0.8.0

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
  SHA1:
3
- metadata.gz: f89fe1c04a0c5226662965adf194eca6e5efb720
4
- data.tar.gz: 714c012252e26b39e93e3967af657a1ba1a6b7f2
3
+ metadata.gz: ce45400dde62a6c4481017080a3770fe3557619e
4
+ data.tar.gz: 29d853335b679ed69d51df345786021e8919b121
5
5
  SHA512:
6
- metadata.gz: 518365852878a1fd4146557895021ecaeff61ae04804e5d386bf43623a8f5114e3cfc7fc8801c978654f83403cff56326fa63e55795cbc3b89fc3d4653a02ddb
7
- data.tar.gz: 27082bfce68e460d5dc49697f6020194293631b5b1170653a1edf04bc8145e2d002e3027d110094ee238adf58d76aed0c7b4d29bfe7bc521c12a499453918945
6
+ metadata.gz: 2f9a80ce2cc37783a1060d403317eb5e142c6c1a97a81057fc4123c9c7d8e7fcf6fcbec1f40d130d43ef8ad737df0db2f55c09b62f2a10de674d815d88b01f2d
7
+ data.tar.gz: e465ec4b46c0ba099cfd5121402e6366eeca20991f6c00d03656127b0cfb6fe36159f5fd87d955655282e0bc076025d69a1622e10357f81996c7e3bd2ec6525b
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ # [0.8.0][] (2017-04-27)
2
+
3
+ ## Changed
4
+
5
+ - `Polyfill()` is no longer used with `include` or `extend`. Instead `Polyfill.get` should
6
+ be used.
7
+
8
+ ## Added
9
+
10
+ - Polyfill.get for getting modules to include or extend
11
+ - v2.2 Enumerable#slice_when
12
+
13
+ ## Fixed
14
+
15
+ - v2.3 Enumerable#chunk_while and v2.2 Enumerable#slice_after should not require `count`
16
+ - v2.4 Array#sum should not use `each` (the Enumerable version does)
17
+ - load modules before classes so they don't override the local method
18
+
1
19
  # [0.7.0][] (2017-04-22)
2
20
 
3
21
  ## Changed
@@ -123,6 +141,7 @@
123
141
  - v2.4 String#concat?
124
142
  - v2.4 String#prepend?
125
143
 
144
+ [0.8.0]: https://github.com/AaronLasseigne/polyfill/compare/v0.7.0...v0.8.0
126
145
  [0.7.0]: https://github.com/AaronLasseigne/polyfill/compare/v0.6.0...v0.7.0
127
146
  [0.6.0]: https://github.com/AaronLasseigne/polyfill/compare/v0.5.0...v0.6.0
128
147
  [0.5.0]: https://github.com/AaronLasseigne/polyfill/compare/v0.4.0...v0.5.0
data/README.md CHANGED
@@ -33,7 +33,7 @@ See the [implementation table](#implementation-table) for specifics about what h
33
33
  Add it to your Gemfile:
34
34
 
35
35
  ```ruby
36
- gem 'polyfill', '0.7.0'
36
+ gem 'polyfill', '0.8.0'
37
37
  ```
38
38
 
39
39
  Or install it manually:
@@ -52,6 +52,8 @@ This project uses [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
52
52
 
53
53
  ## Usage
54
54
 
55
+ ### Polyfill
56
+
55
57
  With the `Polyfill` method, you can polyfill methods for one or more Ruby
56
58
  objects. Each object is passed as a key. The value is an array of strings
57
59
  containing the methods you would like to polyfill. Instance methods need to
@@ -88,15 +90,37 @@ This currently adds support for `respond_to?`, `__send__`, and `send`.
88
90
  using Polyfill(native: true, Numeric: :all)
89
91
  ```
90
92
 
93
+ ### Polyfill.get
94
+
91
95
  Prior to Ruby 2.4, refinements do not work on Modules. When using a polyfill
92
96
  on a module it will instead refine the core classes that use the module. If
93
97
  you're building your own class, it will not receive the polyfill. Instead,
94
- you can add the polyfill by using `include`.
98
+ you can `include` (or `extend`) in a polyfill with `Polyfill.get`.
99
+
100
+ ```ruby
101
+ class Foo
102
+ include Comparable
103
+ include Polyfill.get(:Comparable, :all)
104
+ end
105
+ ```
106
+
107
+ To use specific methods you can pass an array of symbols in place of `:all`.
95
108
 
96
109
  ```ruby
97
110
  class Foo
98
111
  include Comparable
99
- include Polyfill(Comparable: :all)
112
+ include Polyfill.get(:Comparable, %i[clamp])
113
+ end
114
+ ```
115
+
116
+ Like before, the polyfills can be halted at a maximum version with the
117
+ `:version` option. The version must be a string with the major and minor
118
+ version only.
119
+
120
+ ```ruby
121
+ class Foo
122
+ include Comparable
123
+ include Polyfill.get(:Comparable, :all, version: '2.3')
100
124
  end
101
125
  ```
102
126
 
@@ -111,7 +135,7 @@ end
111
135
  | | #min | No | This method already existed but was inherited from `Enumerable`. It was optimized on `Array` so redefining `Enumerable#min` no longer affects this.
112
136
  | | #pack | No |
113
137
  | | #sum | Yes |
114
- | BasicObject | #__send__ | No |
138
+ | BasicObject | #\_\_send\_\_ | No |
115
139
  | Binding | #irb | No |
116
140
  | Comparable | #clamp | Yes |
117
141
  | CSV | #new | No |
@@ -297,7 +321,7 @@ end
297
321
  | | #min | No |
298
322
  | | #min_by | No |
299
323
  | | #slice_after | Yes |
300
- | | #slice_when | No |
324
+ | | #slice_when | Yes |
301
325
  | Etc | .confstr | No |
302
326
  | | .sysconf | No |
303
327
  | | .nprocessors | No |
@@ -306,9 +330,8 @@ end
306
330
  | | #prev_float | No |
307
331
  | File | .birthtime | No |
308
332
  | | #birthtime | No |
309
- | | .find | No |
310
- | | #find | No |
311
333
  | File::Stat | #birthtime | No |
334
+ | Find | .find | No |
312
335
  | GC | .latest_gc_info | No |
313
336
  | | .stat | No |
314
337
  | IO | #each_codepoint | No |
data/Rakefile CHANGED
@@ -3,6 +3,8 @@ require 'rspec/core/rake_task'
3
3
  require 'rubocop/rake_task'
4
4
 
5
5
  RSpec::Core::RakeTask.new(:spec)
6
- RuboCop::RakeTask.new
6
+ RuboCop::RakeTask.new(:rubocop) do |task|
7
+ task.options = ['--display-cop-names']
8
+ end
7
9
 
8
10
  task default: %i[spec rubocop]
data/lib/polyfill.rb CHANGED
@@ -5,6 +5,99 @@ require 'polyfill/utils'
5
5
 
6
6
  module Polyfill
7
7
  module Parcel; end
8
+
9
+ def get(module_name, methods, options = {})
10
+ if Object.const_get(module_name.to_s).is_a?(Class)
11
+ raise ArgumentError, "#{module_name} is a class not a module"
12
+ end
13
+
14
+ #
15
+ # parse options
16
+ #
17
+ versions = {
18
+ '2.2' => Polyfill::V2_2,
19
+ '2.3' => Polyfill::V2_3,
20
+ '2.4' => Polyfill::V2_4
21
+ }
22
+ desired_version = options.delete(:version) || versions.keys.max
23
+ unless versions.keys.include?(desired_version)
24
+ raise ArgumentError, "invalid value for keyword version: #{desired_version}"
25
+ end
26
+ versions.reject! do |version_number, _|
27
+ version_number > desired_version
28
+ end
29
+
30
+ unless options.empty?
31
+ raise ArgumentError, "unknown keyword: #{options.first[0]}"
32
+ end
33
+
34
+ #
35
+ # find all polyfills for the module across all versions
36
+ #
37
+ module_names = module_name.to_s.split('::')
38
+ current_ruby_version = RUBY_VERSION[/\A(\d+\.\d+)/, 1]
39
+
40
+ modules_with_updates = []
41
+ modules = []
42
+ versions.each do |version_number, version_module|
43
+ begin
44
+ final_module = module_names
45
+ .reduce(version_module) do |current_mod, name|
46
+ current_mod.const_get(name, false)
47
+ end
48
+
49
+ modules_with_updates << final_module
50
+
51
+ next if version_number <= current_ruby_version
52
+
53
+ modules << final_module.clone
54
+ rescue NameError
55
+ nil
56
+ end
57
+ end
58
+
59
+ if modules_with_updates.empty?
60
+ raise ArgumentError, %Q("#{module_name}" has no updates)
61
+ end
62
+
63
+ #
64
+ # remove methods that were not requested
65
+ #
66
+ methods_with_updates = modules_with_updates.flat_map(&:instance_methods).uniq
67
+ requested_methods = methods == :all ? methods_with_updates : methods
68
+
69
+ unless (leftovers = (requested_methods - methods_with_updates)).empty?
70
+ raise ArgumentError, %Q("##{leftovers.first}" is not a valid method on #{module_name} or has no updates)
71
+ end
72
+
73
+ modules.each do |instance_module|
74
+ instance_module.instance_methods.each do |name|
75
+ instance_module.send(:remove_method, name) unless requested_methods.include?(name)
76
+ end
77
+ end
78
+
79
+ #
80
+ # build the module to return
81
+ #
82
+ mod = Module.new
83
+
84
+ # make sure the methods get added if this module is included
85
+ mod.singleton_class.send(:define_method, :included) do |base|
86
+ modules.each do |module_to_add|
87
+ base.include module_to_add unless module_to_add.instance_methods.empty?
88
+ end
89
+ end
90
+
91
+ # make sure the methods get added if this module is extended
92
+ mod.singleton_class.send(:define_method, :extended) do |base|
93
+ modules.each do |module_to_add|
94
+ base.extend module_to_add unless module_to_add.instance_methods.empty?
95
+ end
96
+ end
97
+
98
+ Polyfill::Parcel.const_set("O#{mod.object_id}", mod)
99
+ end
100
+ module_function :get
8
101
  end
9
102
 
10
103
  def Polyfill(options = {}) # rubocop:disable Style/MethodName
@@ -14,6 +107,15 @@ def Polyfill(options = {}) # rubocop:disable Style/MethodName
14
107
  # parse options
15
108
  #
16
109
  objects, others = options.partition { |key,| key[/\A[A-Z]/] }
110
+ objects.sort! do |a, b|
111
+ if !a.is_a?(Class) && b.is_a?(Class)
112
+ -1
113
+ elsif a.is_a?(Class) && !b.is_a?(Class)
114
+ 1
115
+ else
116
+ 0
117
+ end
118
+ end
17
119
  others = others.to_h
18
120
 
19
121
  versions = {
@@ -36,10 +138,9 @@ def Polyfill(options = {}) # rubocop:disable Style/MethodName
36
138
  end
37
139
 
38
140
  #
39
- # useful vars
141
+ # useful var
40
142
  #
41
143
  current_ruby_version = RUBY_VERSION[/\A(\d+\.\d+)/, 1]
42
- all_instance_modules = []
43
144
 
44
145
  objects.each do |full_name, methods|
45
146
  #
@@ -63,7 +164,7 @@ def Polyfill(options = {}) # rubocop:disable Style/MethodName
63
164
  .compact
64
165
 
65
166
  if object_modules.empty?
66
- raise ArgumentError, %Q("#{full_name}" is not a valid class or has no updates)
167
+ raise ArgumentError, %Q("#{full_name}" is not a valid object or has no updates)
67
168
  end
68
169
 
69
170
  #
@@ -172,8 +273,6 @@ def Polyfill(options = {}) # rubocop:disable Style/MethodName
172
273
 
173
274
  next if instance_module.instance_methods.empty?
174
275
 
175
- all_instance_modules << instance_module
176
-
177
276
  mod.module_exec(requested_instance_methods) do |methods_added|
178
277
  base_classes.each do |klass|
179
278
  refine Object.const_get(klass) do
@@ -201,15 +300,6 @@ def Polyfill(options = {}) # rubocop:disable Style/MethodName
201
300
  end
202
301
  end
203
302
 
204
- #
205
- # make sure the includes get added if this module is included
206
- #
207
- mod.singleton_class.send(:define_method, :included) do |base|
208
- all_instance_modules.each do |instance_module|
209
- base.include instance_module
210
- end
211
- end
212
-
213
303
  Polyfill::Parcel.const_set("O#{mod.object_id}", mod)
214
304
  end
215
305
 
@@ -6,27 +6,38 @@ module Polyfill
6
6
  raise ArgumentError, 'wrong number of arguments (given 0, expected 1)' if !pattern && !block_given?
7
7
 
8
8
  matcher = pattern || ::Proc.new
9
- enum_count =
10
- begin
11
- size
12
- rescue NameError
13
- count
14
- end
15
9
 
16
10
  ::Enumerator.new do |yielder|
17
11
  output = []
18
- run = 1
19
12
  each do |element, *rest|
20
13
  elements = rest.any? ? [element, *rest] : element
21
14
 
22
15
  output.push(elements)
23
- if matcher === elements || run == enum_count # rubocop:disable Style/CaseEquality
16
+ if matcher === output.last # rubocop:disable Style/CaseEquality
24
17
  yielder << output
25
18
  output = []
26
19
  end
20
+ end
21
+ yielder << output unless output.empty?
22
+ end
23
+ end
24
+
25
+ def slice_when
26
+ block = ::Proc.new
27
+
28
+ ::Enumerator.new do |yielder|
29
+ output = []
30
+ each do |element, *rest|
31
+ elements = rest.any? ? [element, *rest] : element
27
32
 
28
- run += 1
33
+ if output.empty? || !block.call(output.last, elements)
34
+ output.push(elements)
35
+ else
36
+ yielder << output
37
+ output = [elements]
38
+ end
29
39
  end
40
+ yielder << output unless output.empty?
30
41
  end
31
42
  end
32
43
  end
@@ -4,35 +4,19 @@ module Polyfill
4
4
  def chunk_while
5
5
  block = ::Proc.new
6
6
 
7
- enum_count =
8
- begin
9
- size
10
- rescue NameError
11
- count
12
- end
13
-
14
- return [self] if enum_count == 1
15
-
16
7
  ::Enumerator.new do |yielder|
17
8
  output = []
18
- each_cons(2).with_index(1) do |(a, b), run|
19
- if run == enum_count - 1
20
- if block.call(a, b)
21
- output.push(a, b)
22
- yielder << output
23
- else
24
- output.push(a)
25
- yielder << output
26
- yielder << [b]
27
- end
9
+ each do |element, *rest|
10
+ elements = rest.any? ? [element, *rest] : element
11
+
12
+ if output.empty? || block.call(output.last, elements)
13
+ output.push(elements)
28
14
  else
29
- output.push(a)
30
- unless block.call(a, b)
31
- yielder << output
32
- output = []
33
- end
15
+ yielder << output
16
+ output = [elements]
34
17
  end
35
18
  end
19
+ yielder << output unless output.empty?
36
20
  end
37
21
  end
38
22
 
@@ -1,6 +1,10 @@
1
+ require_relative 'numeric'
2
+
1
3
  module Polyfill
2
4
  module V2_4
3
5
  module Array
6
+ using Polyfill(Numeric: %w[#dup])
7
+
4
8
  def concat(*others)
5
9
  return super if others.length == 1
6
10
 
@@ -13,14 +17,10 @@ module Polyfill
13
17
  end
14
18
 
15
19
  def sum(init = 0)
16
- acc =
17
- begin
18
- init.dup
19
- rescue TypeError
20
- init
21
- end
20
+ acc = init.dup
22
21
 
23
- each do |elem|
22
+ for i in 0..(size - 1) # rubocop:disable Style/For
23
+ elem = self[i]
24
24
  acc += block_given? ? yield(elem) : elem
25
25
  end
26
26
 
@@ -1,6 +1,10 @@
1
+ require_relative 'numeric'
2
+
1
3
  module Polyfill
2
4
  module V2_4
3
5
  module Enumerable
6
+ using Polyfill(Numeric: %w[#dup])
7
+
4
8
  def chunk(*)
5
9
  return enum_for(:chunk) unless block_given?
6
10
 
@@ -8,12 +12,7 @@ module Polyfill
8
12
  end
9
13
 
10
14
  def sum(init = 0)
11
- acc =
12
- begin
13
- init.dup
14
- rescue TypeError
15
- init
16
- end
15
+ acc = init.dup
17
16
 
18
17
  each do |elem|
19
18
  acc += block_given? ? yield(elem) : elem
@@ -1,3 +1,3 @@
1
1
  module Polyfill
2
- VERSION = '0.7.0'.freeze
2
+ VERSION = '0.8.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: polyfill
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Lasseigne
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-04-22 00:00:00.000000000 Z
11
+ date: 2017-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler