naturally 1.4.0 → 2.2.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
- SHA1:
3
- metadata.gz: a5b55ef725bab4abf9e87b176f86ab6c94d1ea4d
4
- data.tar.gz: 2ca443c6027ed127aa570dceb2bdf63aeb060457
2
+ SHA256:
3
+ metadata.gz: 71953ca154c0b7db095ccc5f8f877afed7142eb0bcf59a5118779ba2c8f79aa1
4
+ data.tar.gz: 302ae86df325c4e526e1b8158dc669c497b27ae9b5ffe60d32bdcb206285eb46
5
5
  SHA512:
6
- metadata.gz: 85d8b14a18c0415b1360ce03a0eabad8e0cdfa07656648d71d251ac9556194aa705ff547b7cf5c6143762e876f425c4c05369047bef7368bdadaff24ce6d3705
7
- data.tar.gz: df7f7705855241de8b0b1f4b26a4996398b90bfc5631088e5bcc9a141d2f98bc149a6192e9a19360b825839106ec06b3c12f586c327736b378eccb3f5c96694a
6
+ metadata.gz: c7f607e695090c2d12a67b1410960312fbc4d9b7d046f1dea91513653fd00da13e01ebf92c1405efdb498a32029baae4733ddd022cee78becf67db7b4988bedd
7
+ data.tar.gz: 3110d73168c9797628ad7490e1713785921abbce1651d54e9d8e1e2c6e2c65d9e5cbf77ad1c8b693ac49d2d4a7741ffb1198480f17a1f4b1fa9af2ca264d4c5f
@@ -1,4 +1,4 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.1
4
- - 2.2
3
+ - 2.4
4
+ - 2.5
data/Gemfile CHANGED
@@ -2,7 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  group :test do
4
4
  gem 'rspec', '~> 3'
5
- gem 'rake', '~> 10'
5
+ gem 'rake', '>= 12.3.3'
6
6
  end
7
7
 
8
8
  # Specify your gem's dependencies in naturally.gemspec
data/README.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # Naturally
2
- [![Gem Version](https://badge.fury.io/rb/naturally.png)](http://badge.fury.io/rb/naturally) [![Build Status](https://travis-ci.org/dogweather/naturally.png)](https://travis-ci.org/dogweather/naturally) [![Code Climate](https://codeclimate.com/github/dogweather/naturally.png)](https://codeclimate.com/github/dogweather/naturally)
2
+ [![Gem Version](https://badge.fury.io/rb/naturally.png)](http://badge.fury.io/rb/naturally) [![Build Status](https://travis-ci.org/public-law/naturally.png)](https://travis-ci.org/public-law/naturally)
3
+ [![Maintainability](https://api.codeclimate.com/v1/badges/0ebf4ef97723f2622105/maintainability)](https://codeclimate.com/github/dogweather/naturally/maintainability)
3
4
 
4
- Natural (version number) sorting with support for **legal document numbering**, **college course codes**, and **Unicode**.
5
- See Jeff Atwood's [Sorting for Humans: Natural Sort Order](http://www.codinghorror.com/blog/2007/12/sorting-for-humans-natural-sort-order.html) and the WebLaws.org post [Counting to 10 in Californian](http://www.weblaws.org/blog/2012/08/counting-from-1-to-10-in-californian/).
5
+ Natural ("version number") sorting with support for **legal document numbering**, **college course codes**, and **Unicode**.
6
+ See Jeff Atwood's [Sorting for Humans: Natural Sort Order](http://www.codinghorror.com/blog/2007/12/sorting-for-humans-natural-sort-order.html) and the Public.Law post [Counting to 10 in Californian](https://blog.public.law/2012/08/07/counting-from-1-to-10-in-californian/).
6
7
 
7
- ##Installation
8
+ ## Installation
8
9
 
9
10
  ```Shell
10
11
  $ gem install naturally
@@ -15,8 +16,11 @@ $ gem install naturally
15
16
  ```Ruby
16
17
  require 'naturally'
17
18
 
18
- # Sort a simple array of strings
19
+ # Sort a simple array of strings with legal numbering
19
20
  Naturally.sort(["336", "335a", "335", "335.1"]) # => ["335", "335.1", "335a", "336"]
21
+
22
+ # Sort version numbers
23
+ Naturally.sort(["13.10", "13.04", "10.10", "10.04.4"]) # => ["10.04.4", "10.10", "13.04", "13.10"]
20
24
  ```
21
25
 
22
26
  Usually the library is used to sort an array of objects:
@@ -37,7 +41,7 @@ releases = [
37
41
  ]
38
42
 
39
43
  # Sort by version number
40
- sorted = Naturally.sort_by(releases, :version)
44
+ sorted = Naturally.sort releases, by: :version
41
45
 
42
46
  # Check what we have
43
47
  expect(sorted.map(&:name)).to eq [
@@ -3,26 +3,39 @@ require 'naturally/segment'
3
3
  # A module which performs natural sorting on a variety of number
4
4
  # formats. (See the specs for examples.)
5
5
  module Naturally
6
- # Perform a natural sort.
6
+ # Perform a natural sort. Supports two syntaxes:
7
+ #
8
+ # 1. sort(objects) # Simple arrays
9
+ # 2. sort(objects, by: some_attribute) # Complex objects
7
10
  #
8
11
  # @param [Array<String>] an_array the list of numbers to sort.
12
+ # @param [by] (optional) an attribute of the array by which to sort.
9
13
  # @return [Array<String>] the numbers sorted naturally.
10
- def self.sort(an_array)
11
- an_array.sort_by { |x| normalize(x) }
14
+ def self.sort(an_array, by:nil)
15
+ if by.nil?
16
+ an_array.sort_by { |x| normalize(x) }
17
+ else
18
+ self.sort_by(an_array, by)
19
+ end
12
20
  end
13
21
 
14
22
  # Sort an array of objects "naturally" by a given attribute.
23
+ # If block is given, attribute is ignored and each object
24
+ # is yielded to the block to obtain the sort key.
15
25
  #
16
26
  # @param [Array<Object>] an_array the list of objects to sort.
17
27
  # @param [Symbol] an_attribute the attribute by which to sort.
28
+ # @param [Block] &block a block that should evaluate to the
29
+ # sort key for the yielded object
18
30
  # @return [Array<Object>] the objects in natural sort order.
19
- def self.sort_by(an_array, an_attribute)
31
+ def self.sort_by(an_array, an_attribute=nil, &block)
32
+ return sort_by_block(an_array, &block) if block_given?
20
33
  an_array.sort_by { |obj| normalize(obj.send(an_attribute)) }
21
34
  end
22
35
 
23
- # Convert the given number an array of {Segment}s.
36
+ # Convert the given number to an array of {Segment}s.
24
37
  # This enables it to be sorted against other arrays
25
- # by the standard #sort method.
38
+ # by the built-in #sort method.
26
39
  #
27
40
  # For example, '1.2a.3' becomes
28
41
  # [Segment<'1'>, Segment<'2a'>, Segment<'3'>]
@@ -32,7 +45,19 @@ module Naturally
32
45
  # @return [Array<Segment>] an array of Segments which
33
46
  # can be sorted naturally via a standard #sort.
34
47
  def self.normalize(complex_number)
35
- tokens = complex_number.to_s.scan(/\p{Word}+/)
48
+ tokens = complex_number.to_s.gsub(/\_/,'').scan(/\p{Word}+/)
36
49
  tokens.map { |t| Segment.new(t) }
37
50
  end
51
+
52
+ private
53
+ # Sort an array of objects "naturally", yielding each object
54
+ # to the block to obtain the sort key.
55
+ #
56
+ # @param [Array<Object>] an_array the list of objects to sort.
57
+ # @param [Block] &block a block that should evaluate to the
58
+ # sort key for the yielded object
59
+ # @return [Array<Object>] the objects in natural sort order.
60
+ def self.sort_by_block(an_array, &block)
61
+ an_array.sort_by { |obj| normalize(yield(obj)) }
62
+ end
38
63
  end
@@ -1,7 +1,8 @@
1
1
  module Naturally
2
2
  # An entity which can be compared to other like elements for
3
3
  # sorting. It's an object representing
4
- # a value which implements the {Comparable} interface.
4
+ # a value which implements the {Comparable} interface which can
5
+ # convert itself to an array.
5
6
  class Segment
6
7
  include Comparable
7
8
 
@@ -25,7 +26,7 @@ module Naturally
25
26
  # @example a college course code
26
27
  # Segment.new('MATH101').to_array #=> [:str, "MATH", 101]
27
28
  #
28
- # @example Section 633a of the ADEA
29
+ # @example Section 633a of the U.S. Age Discrimination in Employment Act
29
30
  # Segment.new('633a').to_array #=> [:int, 633, "a"]
30
31
  def to_array
31
32
  # TODO: Refactor, probably via polymorphism
@@ -1,4 +1,4 @@
1
1
  module Naturally
2
2
  # Gem version
3
- VERSION = '1.4.0'
3
+ VERSION = '2.2.1'
4
4
  end
@@ -7,11 +7,11 @@ Gem::Specification.new do |gem|
7
7
  gem.version = Naturally::VERSION
8
8
  gem.license = 'MIT'
9
9
  gem.authors = ["Robb Shecter"]
10
- gem.email = ["robb@weblaws.org"]
10
+ gem.email = ["robb@public.law"]
11
11
  gem.summary = %q{Sorts numbers according to the way people are used to seeing them.}
12
12
  gem.description = %q{Natural Sorting with support for legal numbering, course numbers, and other number/letter mixes.}
13
13
  gem.homepage = "http://github.com/dogweather/naturally"
14
- gem.required_ruby_version = '>= 2.1'
14
+ gem.required_ruby_version = '>= 2.0'
15
15
 
16
16
  gem.files = `git ls-files`.split($/)
17
17
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
@@ -1,92 +1,128 @@
1
+ # coding: utf-8
1
2
  require 'naturally'
2
3
 
3
4
  describe Naturally do
4
- def it_sorts(this:, to_this:)
5
+ # Just a helper for these tests
6
+ def it_sorts(opts = {})
7
+ this = opts[:this]
8
+ to_this = opts[:to_this]
5
9
  actual = Naturally.sort(this)
6
10
  expect(actual).to eq(to_this)
7
11
  end
8
12
 
13
+
9
14
  describe '#sort' do
15
+ it 'supports a nicer by: syntax' do
16
+ UbuntuVersion ||= Struct.new(:name, :version)
17
+ releases = [
18
+ UbuntuVersion.new('Saucy Salamander', '13.10'),
19
+ UbuntuVersion.new('Raring Ringtail', '13.04'),
20
+ UbuntuVersion.new('Precise Pangolin', '12.04.4'),
21
+ UbuntuVersion.new('Maverick Meerkat', '10.10'),
22
+ UbuntuVersion.new('Quantal Quetzal', '12.10'),
23
+ UbuntuVersion.new('Lucid Lynx', '10.04.4')
24
+ ]
25
+
26
+ actual = Naturally.sort releases, by: :version
27
+
28
+ expect(actual.map(&:name)).to eq [
29
+ 'Lucid Lynx',
30
+ 'Maverick Meerkat',
31
+ 'Precise Pangolin',
32
+ 'Quantal Quetzal',
33
+ 'Raring Ringtail',
34
+ 'Saucy Salamander'
35
+ ]
36
+ end
37
+
38
+
10
39
  it 'sorts an array of strings nicely as if they were legal numbers' do
11
40
  it_sorts(
12
- this: %w(676 676.1 676.11 676.12 676.2 676.3 676.9 676.10),
41
+ this: %w(676 676.1 676.11 676.12 676.2 676.3 676.9 676.10),
13
42
  to_this: %w(676 676.1 676.2 676.3 676.9 676.10 676.11 676.12)
14
43
  )
15
44
  end
16
45
 
17
46
  it 'sorts a more complex list of strings' do
18
47
  it_sorts(
19
- this: %w(350 351 352 352.1 352.5 353.1 354 354.3 354.4 354.45 354.5),
48
+ this: %w(350 351 352 352.1 352.5 353.1 354 354.3 354.4 354.45 354.5),
20
49
  to_this: %w(350 351 352 352.1 352.5 353.1 354 354.3 354.4 354.5 354.45)
21
50
  )
22
51
  end
23
52
 
24
53
  it 'sorts when numbers have letters in them' do
25
54
  it_sorts(
26
- this: %w(335 335.1 336a 336 337 337a 337.1 337.15 337.2),
55
+ this: %w(335 335.1 336a 336 337 337a 337.1 337.15 337.2),
27
56
  to_this: %w(335 335.1 336 336a 337 337.1 337.2 337.15 337a)
28
57
  )
29
58
  end
30
59
 
31
60
  it 'sorts when numbers have unicode letters in them' do
32
61
  it_sorts(
33
- this: %w(335 335.1 336a 336 337 337я 337.1 337.15 337.2),
62
+ this: %w(335 335.1 336a 336 337 337я 337.1 337.15 337.2),
34
63
  to_this: %w(335 335.1 336 336a 337 337.1 337.2 337.15 337я)
35
64
  )
36
65
  end
37
66
 
38
67
  it 'sorts when letters have numbers in them' do
39
68
  it_sorts(
40
- this: %w(PC1, PC3, PC5, PC7, PC9, PC10, PC11, PC12, PC13, PC14, PROF2, PBLI, SBP1, SBP3),
69
+ this: %w(PC1, PC3, PC5, PC7, PC9, PC10, PC11, PC12, PC13, PC14, PROF2, PBLI, SBP1, SBP3),
41
70
  to_this: %w(PBLI, PC1, PC3, PC5, PC7, PC9, PC10, PC11, PC12, PC13, PC14, PROF2, SBP1, SBP3)
42
71
  )
43
72
  end
44
73
 
45
74
  it 'sorts when letters have numbers and unicode characters in them' do
46
75
  it_sorts(
47
- this: %w(АБ4, АБ2, АБ10, АБ12, АБ1, АБ3, АД8, АД5, АЩФ12, АЩФ8, ЫВА1),
76
+ this: %w(АБ4, АБ2, АБ10, АБ12, АБ1, АБ3, АД8, АД5, АЩФ12, АЩФ8, ЫВА1),
48
77
  to_this: %w(АБ1, АБ2, АБ3, АБ4, АБ10, АБ12, АД5, АД8, АЩФ8, АЩФ12, ЫВА1)
49
78
  )
50
79
  end
51
80
 
52
81
  it 'sorts double digits with letters correctly' do
53
82
  it_sorts(
54
- this: %w(12a 12b 12c 13a 13b 2 3 4 5 10 11 12),
83
+ this: %w(12a 12b 12c 13a 13b 2 3 4 5 10 11 12),
55
84
  to_this: %w(2 3 4 5 10 11 12 12a 12b 12c 13a 13b)
56
85
  )
57
86
  end
58
87
 
59
88
  it 'sorts double digits with unicode letters correctly' do
60
89
  it_sorts(
61
- this: %w(12а 12б 12в 13а 13б 2 3 4 5 10 11 12),
90
+ this: %w(12а 12б 12в 13а 13б 2 3 4 5 10 11 12),
62
91
  to_this: %w(2 3 4 5 10 11 12 12а 12б 12в 13а 13б)
63
92
  )
64
93
  end
65
94
 
95
+ it 'sorts strings suffixed with underscore and numbers correctly' do
96
+ it_sorts(
97
+ this: %w(item_10 item_11 item_1 item_7 item_5 item_3 item_4 item_6 item_2),
98
+ to_this: %w(item_1 item_2 item_3 item_4 item_5 item_6 item_7 item_10 item_11)
99
+ )
100
+ end
101
+
66
102
  it 'sorts letters with digits correctly' do
67
103
  it_sorts(
68
- this: %w(1 a 2 b 3 c),
104
+ this: %w(1 a 2 b 3 c),
69
105
  to_this: %w(1 2 3 a b c)
70
106
  )
71
107
  end
72
108
 
73
109
  it 'sorts complex numbers with digits correctly' do
74
110
  it_sorts(
75
- this: %w(1 a 2 b 3 c 1.1 a.1 1.2 a.2 1.3 a.3 b.1 ),
111
+ this: %w(1 a 2 b 3 c 1.1 a.1 1.2 a.2 1.3 a.3 b.1 ),
76
112
  to_this: %w(1 1.1 1.2 1.3 2 3 a a.1 a.2 a.3 b b.1 c)
77
113
  )
78
114
  end
79
115
 
80
116
  it 'sorts complex mixes of numbers and digits correctly' do
81
117
  it_sorts(
82
- this: %w( 1.a.1 1.1 ),
118
+ this: %w( 1.a.1 1.1 ),
83
119
  to_this: %w( 1.1 1.a.1 )
84
120
  )
85
121
  end
86
122
 
87
123
  it 'sorts complex mixes of numbers and digits correctly' do
88
124
  it_sorts(
89
- this: %w( 1a1 1aa aaa ),
125
+ this: %w( 1a1 1aa aaa ),
90
126
  to_this: %w( 1aa 1a1 aaa )
91
127
  )
92
128
  end
@@ -94,7 +130,7 @@ describe Naturally do
94
130
 
95
131
  describe '#sort_naturally_by' do
96
132
  it 'sorts by an attribute' do
97
- UbuntuVersion = Struct.new(:name, :version)
133
+ UbuntuVersion ||= Struct.new(:name, :version)
98
134
  releases = [
99
135
  UbuntuVersion.new('Saucy Salamander', '13.10'),
100
136
  UbuntuVersion.new('Raring Ringtail', '13.04'),
@@ -150,4 +186,26 @@ describe Naturally do
150
186
  ]
151
187
  end
152
188
  end
189
+
190
+ describe '#sort_naturally_by_block' do
191
+ it 'sorts using a block' do
192
+ releases = [
193
+ {:name => 'Saucy Salamander', :version => '13.10'},
194
+ {:name => 'Raring Ringtail', :version => '13.04'},
195
+ {:name => 'Precise Pangolin', :version => '12.04.4'},
196
+ {:name => 'Maverick Meerkat', :version => '10.10'},
197
+ {:name => 'Quantal Quetzal', :version => '12.10'},
198
+ {:name => 'Lucid Lynx', :version => '10.04.4'}
199
+ ]
200
+ actual = Naturally.sort_by(releases){|r| r[:version]}
201
+ expect(actual.map{|r| r[:name]}).to eq [
202
+ 'Lucid Lynx',
203
+ 'Maverick Meerkat',
204
+ 'Precise Pangolin',
205
+ 'Quantal Quetzal',
206
+ 'Raring Ringtail',
207
+ 'Saucy Salamander'
208
+ ]
209
+ end
210
+ end
153
211
  end
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: naturally
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 2.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robb Shecter
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-04 00:00:00.000000000 Z
11
+ date: 2021-01-18 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Natural Sorting with support for legal numbering, course numbers, and
14
14
  other number/letter mixes.
15
15
  email:
16
- - robb@weblaws.org
16
+ - robb@public.law
17
17
  executables: []
18
18
  extensions: []
19
19
  extra_rdoc_files: []
@@ -33,7 +33,7 @@ homepage: http://github.com/dogweather/naturally
33
33
  licenses:
34
34
  - MIT
35
35
  metadata: {}
36
- post_install_message:
36
+ post_install_message:
37
37
  rdoc_options: []
38
38
  require_paths:
39
39
  - lib
@@ -41,16 +41,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
41
41
  requirements:
42
42
  - - ">="
43
43
  - !ruby/object:Gem::Version
44
- version: '2.1'
44
+ version: '2.0'
45
45
  required_rubygems_version: !ruby/object:Gem::Requirement
46
46
  requirements:
47
47
  - - ">="
48
48
  - !ruby/object:Gem::Version
49
49
  version: '0'
50
50
  requirements: []
51
- rubyforge_project:
52
- rubygems_version: 2.4.8
53
- signing_key:
51
+ rubygems_version: 3.2.5
52
+ signing_key:
54
53
  specification_version: 4
55
54
  summary: Sorts numbers according to the way people are used to seeing them.
56
55
  test_files: