naturally 1.4.0 → 2.2.1

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 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: