naturally 1.3.1 → 1.3.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
  SHA1:
3
- metadata.gz: a36e33ade747d0c59b3788ba0bb22c1f6defb184
4
- data.tar.gz: a81707fb161b155dc47bed22462cf8a9ed734fe5
3
+ metadata.gz: 11b862eddf78c3c623300fc93d643a1b0081f815
4
+ data.tar.gz: 576b16fec163a92e756c98c1df4f3b1af8ebab31
5
5
  SHA512:
6
- metadata.gz: 7536fef931a0c593bc9918bff1e6407922a18cc339f8e0d96edf8e97db941232809238749fe6fd65f9fb9264cd8fa89d16d01cef8e64afe30301def32f6726f3
7
- data.tar.gz: 00f5d433456e0516da7229ab2c64b9c5bbb448aa556e58252115a564d431078dfe19ca8fa1d28b5fd6cfaeee89e3e600db59f3c27d67f9f94e58fb5995a45888
6
+ metadata.gz: 08f910f8cd088cb92969c6f1d257cc53a2178e54894639bac1b361651c986f312e90c6cd42eacb202b89fc664dbf1108d76b474578a40e0a35462bb6ab4f23fe
7
+ data.tar.gz: 1f389ebe3746dc8d2f5c80cd35eb0dcdf4e408fd5f9116945e3355bdf2e037ecff46a60ca458bb3e02df5a701b3847460c184d4ba29012248aae2a09b32e26c9
data/.travis.yml CHANGED
@@ -1,6 +1,4 @@
1
1
  language: ruby
2
2
  rvm:
3
- - "1.9.3"
4
- - "2.0.0"
5
- - "2.1.0"
6
- - "2.1.1"
3
+ - 2.1
4
+ - 2.2
data/Gemfile CHANGED
@@ -1,7 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  group :test do
4
- gem 'rspec', '~> 2.0'
4
+ gem 'rspec', '~> 3.0'
5
5
  gem 'rake', '~> 10.0'
6
6
  end
7
7
 
data/README.md CHANGED
@@ -1,29 +1,8 @@
1
1
  # Naturally
2
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)
3
3
 
4
- Natural (version number) sorting with added support for **legal document numbering** 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/).
6
-
7
- The core of the search is [from here](https://github.com/ahoward/version_sorter). It's since been extended to handle the particular types of numbers that come up in statutes, such
8
- as *335.1, 336, 336a*, etc.
9
-
10
- `Naturally` will also sort "numbers" in college course code format such as
11
- *MATH101, MATH102, ...*. See the specs for examples.
12
-
13
-
14
- ## Installation
15
-
16
- Add this line to your application's Gemfile:
17
-
18
- gem 'naturally'
19
-
20
- And then execute:
21
-
22
- $ bundle
23
-
24
- Or install it outside of bundler with:
25
-
26
- $ gem install naturally
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/).
27
6
 
28
7
 
29
8
  ## Usage
@@ -68,6 +47,11 @@ expect(sorted.map(&:name)).to eq [
68
47
 
69
48
  See [the spec for more examples](https://github.com/dogweather/naturally/blob/master/spec/naturally_spec.rb) of what Naturally can sort.
70
49
 
50
+ ## Related Work
51
+
52
+ * [ahoward/version_sorter](https://github.com/ahoward/version_sorter), the starting point for the `naturally` gem.
53
+ * [GitHub's Version sorter](https://github.com/github/version_sorter)
54
+
71
55
 
72
56
  ## Contributing
73
57
 
data/lib/naturally.rb CHANGED
@@ -1,98 +1,43 @@
1
- # encoding: utf-8
2
-
3
- require 'naturally/version'
4
-
1
+ require 'naturally/segment'
2
+
3
+ # A module which performs natural sorting on a variety of number
4
+ # formats. (See the specs for examples.)
5
+ #
6
+ # It achieves this by capitalizing on Ruby's behavior when
7
+ # comparing arrays: The module sorts arrays of segmented numbers such as
8
+ # ['1.9', '1.9a', '1.10'] by comparing them in their array forms.
9
+ # I.e., approximately [['1', '9'], ['1, '9a'], ['1', '10']]
5
10
  module Naturally
6
11
  # Perform a natural sort.
7
12
  #
8
13
  # @param [Array<String>] an_array the list of numbers to sort.
9
14
  # @return [Array<String>] the numbers sorted naturally.
10
15
  def self.sort(an_array)
11
- return an_array.sort_by { |x| normalize(x) }
16
+ an_array.sort_by { |x| normalize(x) }
12
17
  end
13
18
 
19
+ # Sort an array of objects "naturally" by a given attribute.
20
+ #
21
+ # @param [Array<Object>] an_array the list of objects to sort.
22
+ # @param [Symbol] an_attribute the attribute by which to sort.
23
+ # @return [Array<Object>] the objects in natural sort order.
14
24
  def self.sort_by(an_array, an_attribute)
15
- an_array.sort_by{|i| Naturally.normalize(i.send(an_attribute))}
25
+ an_array.sort_by { |obj| normalize(obj.send(an_attribute)) }
16
26
  end
17
27
 
18
- # Convert the given number into an object that can be sorted
19
- # naturally. This object is an array of {NumberElement} instances.
28
+ # Convert the given number an array of {Segment}s.
29
+ # This enables it to be sorted against other arrays
30
+ # by the standard #sort method.
20
31
  #
21
- # @param [String] number the number in complex form such as 1.2a.3.
22
- # @return [Array<NumberElement>] an array of NumberElements which is
23
- # able to be sorted naturally via a normal 'sort'.
24
- def self.normalize(number)
25
- number.to_s.scan(%r/\p{Word}+/o).map { |i| NumberElement.new(i) }
26
- end
27
-
28
- private
29
-
30
- # An entity which can be compared to other like elements for
31
- # sorting in an array. It's an object representing
32
- # a value which implements the {Comparable} interface.
33
- class NumberElement
34
- include Comparable
35
- attr_accessor :val
36
-
37
- def initialize(v)
38
- @val = v
39
- end
40
-
41
- def <=>(other)
42
- if both_are_integers_without_letters(other)
43
- return @val.to_i <=> other.val.to_i
44
- end
45
-
46
- if either_is_numbers_followed_by_letters(other)
47
- return simple_normalize(@val) <=> simple_normalize(other.val)
48
- end
49
-
50
- if either_is_letters_followed_by_numbers(other)
51
- return reverse_simple_normalize(@val) <=> reverse_simple_normalize(other.val)
52
- end
53
-
54
- @val <=> other.val
55
- end
56
-
57
- def either_is_letters_followed_by_numbers(other)
58
- letters_with_numbers? || other.letters_with_numbers?
59
- end
60
-
61
- def either_is_numbers_followed_by_letters(other)
62
- numbers_with_letters? || other.numbers_with_letters?
63
- end
64
-
65
- def both_are_integers_without_letters(other)
66
- pure_integer? && other.pure_integer?
67
- end
68
-
69
- def pure_integer?
70
- @val =~ /^\d+$/
71
- end
72
-
73
- def numbers_with_letters?
74
- val =~ /^\d+\p{Alpha}+$/
75
- end
76
-
77
- def letters_with_numbers?
78
- val =~ /^\p{Alpha}+\d+$/
79
- end
80
-
81
- def simple_normalize(n)
82
- if n =~ /^(\d+)(\p{Alpha}+)$/
83
- [$1.to_i, $2]
84
- else
85
- [n.to_i]
86
- end
87
- end
88
-
89
- def reverse_simple_normalize(n)
90
- if n =~ /^(\p{Alpha}+)(\d+)$/
91
- [$1, $2.to_i]
92
- else
93
- [n.to_s]
94
- end
95
- end
96
-
32
+ # For example:
33
+ # '1.2a.3' becomes [Segment<'1'>, Segment<'2a'>, Segment<'3'>]
34
+ #
35
+ # @param [String] complex_number the number in a hierarchical form
36
+ # such as 1.2a.3.
37
+ # @return [Array<Segment>] an array of Segments which
38
+ # can be sorted naturally via a standard #sort.
39
+ def self.normalize(complex_number)
40
+ tokens = complex_number.to_s.scan(/\p{Word}+/)
41
+ tokens.map { |t| Segment.new(t) }
97
42
  end
98
43
  end
@@ -0,0 +1,28 @@
1
+ module Naturally
2
+ # An entity which can be compared to other like elements for
3
+ # sorting. It's an object representing
4
+ # a value which implements the {Comparable} interface.
5
+ class Segment
6
+ include Comparable
7
+
8
+ def initialize(v)
9
+ @val = v
10
+ end
11
+
12
+ def <=>(other)
13
+ to_array <=> other.to_array
14
+ end
15
+
16
+ def to_array
17
+ if @val =~ /^(\p{Digit}+)(\p{Alpha}+)$/
18
+ [$1.to_i, $2]
19
+ elsif @val =~ /^(\p{Alpha}+)(\p{Digit}+)$/
20
+ [$1, $2.to_i]
21
+ elsif @val =~ /^\p{Digit}+$/
22
+ [@val.to_i]
23
+ else
24
+ [@val]
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,3 +1,3 @@
1
1
  module Naturally
2
- VERSION = '1.3.1'
2
+ VERSION = '1.3.2'
3
3
  end
data/naturally.gemspec CHANGED
@@ -6,10 +6,11 @@ require 'naturally/version'
6
6
  Gem::Specification.new do |gem|
7
7
  gem.name = "naturally"
8
8
  gem.version = Naturally::VERSION
9
+ gem.license = 'MIT'
9
10
  gem.authors = ["Robb Shecter"]
10
11
  gem.email = ["robb@weblaws.org"]
11
12
  gem.summary = %q{Sorts numbers according to the way people are used to seeing them.}
12
- gem.description = %q{Natural Sorting with support for legal numbering}
13
+ gem.description = %q{Natural Sorting with support for legal numbering, course numbers, and other number/letter mixes.}
13
14
  gem.homepage = "http://github.com/dogweather/naturally"
14
15
 
15
16
  gem.files = `git ls-files`.split($/)
@@ -1,55 +1,67 @@
1
1
  # encoding: utf-8
2
-
3
2
  require 'naturally'
4
3
 
5
4
  describe Naturally do
5
+ def it_sorts(this:, to_this:)
6
+ actual = Naturally.sort(this)
7
+ expect(actual).to eq(to_this)
8
+ end
9
+
6
10
  describe '#sort' do
7
11
  it 'sorts an array of strings nicely as if they were legal numbers' do
8
- a = %w[676 676.1 676.11 676.12 676.2 676.3 676.9 676.10]
9
- b = %w[676 676.1 676.2 676.3 676.9 676.10 676.11 676.12]
10
- Naturally.sort(a).should == b
12
+ it_sorts(
13
+ this: %w(676 676.1 676.11 676.12 676.2 676.3 676.9 676.10),
14
+ to_this: %w(676 676.1 676.2 676.3 676.9 676.10 676.11 676.12)
15
+ )
11
16
  end
12
17
 
13
18
  it 'sorts a more complex list of strings' do
14
- a = %w[350 351 352 352.1 352.5 353.1 354 354.3 354.4 354.45 354.5]
15
- b = %w[350 351 352 352.1 352.5 353.1 354 354.3 354.4 354.5 354.45]
16
- Naturally.sort(a).should == b
19
+ it_sorts(
20
+ this: %w(350 351 352 352.1 352.5 353.1 354 354.3 354.4 354.45 354.5),
21
+ to_this: %w(350 351 352 352.1 352.5 353.1 354 354.3 354.4 354.5 354.45)
22
+ )
17
23
  end
18
24
 
19
25
  it 'sorts when numbers have letters in them' do
20
- a = %w[335 335.1 336a 336 337 337a 337.1 337.15 337.2]
21
- b = %w[335 335.1 336 336a 337 337.1 337.2 337.15 337a]
22
- Naturally.sort(a).should == b
26
+ it_sorts(
27
+ this: %w(335 335.1 336a 336 337 337a 337.1 337.15 337.2),
28
+ to_this: %w(335 335.1 336 336a 337 337.1 337.2 337.15 337a)
29
+ )
23
30
  end
24
31
 
25
32
  it 'sorts when numbers have unicode letters in them' do
26
- a = %w[335 335.1 336a 336 337 337я 337.1 337.15 337.2]
27
- b = %w[335 335.1 336 336a 337 337.1 337.2 337.15 337я]
28
- Naturally.sort(a).should == b
33
+ it_sorts(
34
+ this: %w(335 335.1 336a 336 337 337я 337.1 337.15 337.2),
35
+ to_this: %w(335 335.1 336 336a 337 337.1 337.2 337.15 337я)
36
+ )
29
37
  end
30
38
 
31
39
  it 'sorts when letters have numbers in them' do
32
- a = %w[PC1, PC3, PC5, PC7, PC9, PC10, PC11, PC12, PC13, PC14, PROF2, PBLI, SBP1, SBP3]
33
- b = %w[PBLI, PC1, PC3, PC5, PC7, PC9, PC10, PC11, PC12, PC13, PC14, PROF2, SBP1, SBP3]
34
- Naturally.sort(a).should == b
40
+ it_sorts(
41
+ this: %w(PC1, PC3, PC5, PC7, PC9, PC10, PC11, PC12, PC13, PC14, PROF2, PBLI, SBP1, SBP3),
42
+ to_this: %w(PBLI, PC1, PC3, PC5, PC7, PC9, PC10, PC11, PC12, PC13, PC14, PROF2, SBP1, SBP3)
43
+ )
35
44
  end
36
45
 
37
46
  it 'sorts when letters have numbers and unicode characters in them' do
38
- a = %w[АБ4, АБ2, АБ10, АБ12, АБ1, АБ3, АД8, АД5, АЩФ12, АЩФ8, ЫВА1]
39
- b = %w[АБ1, АБ2, АБ3, АБ4, АБ10, АБ12, АД5, АД8, АЩФ8, АЩФ12, ЫВА1]
40
- Naturally.sort(a).should == b
47
+ it_sorts(
48
+ this: %w(АБ4, АБ2, АБ10, АБ12, АБ1, АБ3, АД8, АД5, АЩФ12, АЩФ8, ЫВА1),
49
+ to_this: %w(АБ1, АБ2, АБ3, АБ4, АБ10, АБ12, АД5, АД8, АЩФ8, АЩФ12, ЫВА1)
50
+ )
41
51
  end
42
52
 
43
53
  it 'sorts double digits with letters correctly' do
44
- a = %w[12a 12b 12c 13a 13b 2 3 4 5 10 11 12]
45
- b = %w[2 3 4 5 10 11 12 12a 12b 12c 13a 13b]
46
- Naturally.sort(a).should == b
54
+ it_sorts(
55
+ this: %w(12a 12b 12c 13a 13b 2 3 4 5 10 11 12),
56
+ to_this: %w(2 3 4 5 10 11 12 12a 12b 12c 13a 13b)
57
+ )
47
58
  end
48
59
 
49
60
  it 'sorts double digits with unicode letters correctly' do
50
- a = %w[12а 12б 12в 13а 13б 2 3 4 5 10 11 12]
51
- b = %w[2 3 4 5 10 11 12 12а 12б 12в 13а 13б]
52
- Naturally.sort(a).should == b
61
+ it_sorts(
62
+ this: %w(12а 12б 12в 13а 13б 2 3 4 5 10 11 12),
63
+ to_this: %w(2 3 4 5 10 11 12 12а 12б 12в 13а 13б)
64
+ )
53
65
  end
54
66
  end
55
67
 
@@ -64,8 +76,8 @@ describe Naturally do
64
76
  UbuntuVersion.new('Quantal Quetzal', '12.10'),
65
77
  UbuntuVersion.new('Lucid Lynx', '10.04.4')
66
78
  ]
67
- sorted = Naturally.sort_by(releases, :version)
68
- expect(sorted.map(&:name)).to eq [
79
+ actual = Naturally.sort_by(releases, :version)
80
+ expect(actual.map(&:name)).to eq [
69
81
  'Lucid Lynx',
70
82
  'Maverick Meerkat',
71
83
  'Precise Pangolin',
@@ -86,8 +98,8 @@ describe Naturally do
86
98
  Thing.new('2.1', 'Калуга'),
87
99
  Thing.new('1.3', 'Васюки')
88
100
  ]
89
- sorted = objects.sort_by{ |o| Naturally.normalize(o.name) }
90
- sorted.map{|o| o.name}.should == %w[
101
+ actual = objects.sort_by { |o| Naturally.normalize(o.name) }
102
+ expect(actual.map(&:name)).to eq %w(
91
103
  Брест
92
104
  Будапешт
93
105
  Васюки
@@ -95,7 +107,7 @@ describe Naturally do
95
107
  Киев
96
108
  Москва
97
109
  Париж
98
- ]
110
+ )
99
111
  end
100
112
  end
101
113
  end
metadata CHANGED
@@ -1,16 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: naturally
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robb Shecter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-07 00:00:00.000000000 Z
11
+ date: 2015-03-11 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: Natural Sorting with support for legal numbering
13
+ description: Natural Sorting with support for legal numbering, course numbers, and
14
+ other number/letter mixes.
14
15
  email:
15
16
  - robb@weblaws.org
16
17
  executables: []
@@ -24,11 +25,13 @@ files:
24
25
  - README.md
25
26
  - Rakefile
26
27
  - lib/naturally.rb
28
+ - lib/naturally/segment.rb
27
29
  - lib/naturally/version.rb
28
30
  - naturally.gemspec
29
31
  - spec/naturally_spec.rb
30
32
  homepage: http://github.com/dogweather/naturally
31
- licenses: []
33
+ licenses:
34
+ - MIT
32
35
  metadata: {}
33
36
  post_install_message:
34
37
  rdoc_options: []
@@ -46,10 +49,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
46
49
  version: '0'
47
50
  requirements: []
48
51
  rubyforge_project:
49
- rubygems_version: 2.2.2
52
+ rubygems_version: 2.4.6
50
53
  signing_key:
51
54
  specification_version: 4
52
55
  summary: Sorts numbers according to the way people are used to seeing them.
53
56
  test_files:
54
57
  - spec/naturally_spec.rb
55
- has_rdoc: