sort_authority 0.0.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.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem*
2
+ ext/**/*.bundle
3
+ ext/**/*.o
4
+ Gemfile.lock
5
+ pkg
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT LICENSE
2
+
3
+ Copyright (c) 2013 Wegowise Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # sort_authority
2
+
3
+ Natural order string comparison using
4
+ [natsort](http://sourcefrog.net/projects/natsort/)
5
+ by [Martin Pool](http://sourcefrog.net/).
6
+
7
+ ### Getting Started
8
+
9
+ If you're using Bundler, you can add `sort_authority` to your Gemfile:
10
+
11
+ ```ruby
12
+ gem 'sort_authority'
13
+ ```
14
+
15
+ Or manually install the gem using:
16
+
17
+ ```shell
18
+ gem install sort_authority
19
+ ```
20
+
21
+ ### Usage
22
+
23
+ ```ruby
24
+ # bad
25
+ ['9 Main Street', '10 Main Street'].sort
26
+ # => ['10 Main Street', '9 Main Street']
27
+
28
+ # good!
29
+ require 'sort_authority/ext/enumerable'
30
+ ['9 Main Street', '10 Main Street'].natural_sort
31
+ # => ['9 Main Street', '10 Main Street']
32
+ ```
33
+
34
+ ### Benchmarks
35
+
36
+ Comparison of sorting an array `['x1'] * 100_000` with [naturalsort](https://rubygems.org/gems/naturalsort),
37
+ [naturally](https://rubygems.org/gems/naturally), [natcmp](https://rubygems.org/gems/natcmp),
38
+ and [sensible_sort](http://www.davekoelle.com/alphanum.html).
39
+
40
+ ```
41
+ user system total real
42
+ sort 0.000000 0.000000 0.000000 ( 0.001758)
43
+ strnatcmp.c 0.030000 0.000000 0.030000 ( 0.029083)
44
+ naturalsort gem 0.060000 0.000000 0.060000 ( 0.061879)
45
+ naturally gem 0.990000 0.020000 1.010000 ( 1.009634)
46
+ natcmp gem 1.140000 0.010000 1.150000 ( 1.141950)
47
+ sensible_sort 2.280000 0.000000 2.280000 ( 2.286274)
48
+ ```
49
+
50
+
51
+ ### Contributing
52
+
53
+ Fork, branch, and pull-request.
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ Bundler::GemHelper.install_tasks
8
+
9
+ require 'rake/testtask'
10
+ Rake::TestTask.new do |t|
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ end
data/ext/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'ffi-compiler/compile_task'
2
+
3
+ FFI::Compiler::CompileTask.new('strnatcmp') do |c|
4
+ c.have_header?('ctype.h', '/usr/include')
5
+ c.have_header?('string.h', '/usr/include')
6
+ c.have_header?('assert.h', '/usr/include')
7
+ c.have_header?('stdio.h', '/usr/include')
8
+ end
data/ext/strnatcmp.c ADDED
@@ -0,0 +1,178 @@
1
+ /* -*- mode: c; c-file-style: "k&r" -*-
2
+
3
+ strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
4
+ Copyright (C) 2000, 2004 by Martin Pool <mbp sourcefrog net>
5
+
6
+ This software is provided 'as-is', without any express or implied
7
+ warranty. In no event will the authors be held liable for any damages
8
+ arising from the use of this software.
9
+
10
+ Permission is granted to anyone to use this software for any purpose,
11
+ including commercial applications, and to alter it and redistribute it
12
+ freely, subject to the following restrictions:
13
+
14
+ 1. The origin of this software must not be misrepresented; you must not
15
+ claim that you wrote the original software. If you use this software
16
+ in a product, an acknowledgment in the product documentation would be
17
+ appreciated but is not required.
18
+ 2. Altered source versions must be plainly marked as such, and must not be
19
+ misrepresented as being the original software.
20
+ 3. This notice may not be removed or altered from any source distribution.
21
+ */
22
+
23
+
24
+ /* partial change history:
25
+ *
26
+ * 2004-10-10 mbp: Lift out character type dependencies into macros.
27
+ *
28
+ * Eric Sosman pointed out that ctype functions take a parameter whose
29
+ * value must be that of an unsigned int, even on platforms that have
30
+ * negative chars in their default char type.
31
+ */
32
+
33
+ #include <ctype.h>
34
+ #include <string.h>
35
+ #include <assert.h>
36
+ #include <stdio.h>
37
+
38
+ #include "strnatcmp.h"
39
+
40
+
41
+ /* These are defined as macros to make it easier to adapt this code to
42
+ * different characters types or comparison functions. */
43
+ static inline int
44
+ nat_isdigit(nat_char a)
45
+ {
46
+ return isdigit((unsigned char) a);
47
+ }
48
+
49
+
50
+ static inline int
51
+ nat_isspace(nat_char a)
52
+ {
53
+ return isspace((unsigned char) a);
54
+ }
55
+
56
+
57
+ static inline nat_char
58
+ nat_toupper(nat_char a)
59
+ {
60
+ return toupper((unsigned char) a);
61
+ }
62
+
63
+
64
+
65
+ static int
66
+ compare_right(nat_char const *a, nat_char const *b)
67
+ {
68
+ int bias = 0;
69
+
70
+ /* The longest run of digits wins. That aside, the greatest
71
+ value wins, but we can't know that it will until we've scanned
72
+ both numbers to know that they have the same magnitude, so we
73
+ remember it in BIAS. */
74
+ for (;; a++, b++) {
75
+ if (!nat_isdigit(*a) && !nat_isdigit(*b))
76
+ return bias;
77
+ else if (!nat_isdigit(*a))
78
+ return -1;
79
+ else if (!nat_isdigit(*b))
80
+ return +1;
81
+ else if (*a < *b) {
82
+ if (!bias)
83
+ bias = -1;
84
+ } else if (*a > *b) {
85
+ if (!bias)
86
+ bias = +1;
87
+ } else if (!*a && !*b)
88
+ return bias;
89
+ }
90
+
91
+ return 0;
92
+ }
93
+
94
+
95
+ static int
96
+ compare_left(nat_char const *a, nat_char const *b)
97
+ {
98
+ /* Compare two left-aligned numbers: the first to have a
99
+ different value wins. */
100
+ for (;; a++, b++) {
101
+ if (!nat_isdigit(*a) && !nat_isdigit(*b))
102
+ return 0;
103
+ else if (!nat_isdigit(*a))
104
+ return -1;
105
+ else if (!nat_isdigit(*b))
106
+ return +1;
107
+ else if (*a < *b)
108
+ return -1;
109
+ else if (*a > *b)
110
+ return +1;
111
+ }
112
+
113
+ return 0;
114
+ }
115
+
116
+
117
+ static int strnatcmp0(nat_char const *a, nat_char const *b, int fold_case)
118
+ {
119
+ int ai, bi;
120
+ nat_char ca, cb;
121
+ int fractional, result;
122
+
123
+ assert(a && b);
124
+ ai = bi = 0;
125
+ while (1) {
126
+ ca = a[ai]; cb = b[bi];
127
+
128
+ /* skip over leading spaces or zeros */
129
+ while (nat_isspace(ca))
130
+ ca = a[++ai];
131
+
132
+ while (nat_isspace(cb))
133
+ cb = b[++bi];
134
+
135
+ /* process run of digits */
136
+ if (nat_isdigit(ca) && nat_isdigit(cb)) {
137
+ fractional = (ca == '0' || cb == '0');
138
+
139
+ if (fractional) {
140
+ if ((result = compare_left(a+ai, b+bi)) != 0)
141
+ return result;
142
+ } else {
143
+ if ((result = compare_right(a+ai, b+bi)) != 0)
144
+ return result;
145
+ }
146
+ }
147
+
148
+ if (!ca && !cb) {
149
+ /* The strings compare the same. Perhaps the caller
150
+ will want to call strcmp to break the tie. */
151
+ return 0;
152
+ }
153
+
154
+ if (fold_case) {
155
+ ca = nat_toupper(ca);
156
+ cb = nat_toupper(cb);
157
+ }
158
+
159
+ if (ca < cb)
160
+ return -1;
161
+ else if (ca > cb)
162
+ return +1;
163
+
164
+ ++ai; ++bi;
165
+ }
166
+ }
167
+
168
+
169
+
170
+ int strnatcmp(nat_char const *a, nat_char const *b) {
171
+ return strnatcmp0(a, b, 0);
172
+ }
173
+
174
+
175
+ /* Compare, recognizing numeric string and ignoring case. */
176
+ int strnatcasecmp(nat_char const *a, nat_char const *b) {
177
+ return strnatcmp0(a, b, 1);
178
+ }
data/ext/strnatcmp.h ADDED
@@ -0,0 +1,31 @@
1
+ /* -*- mode: c; c-file-style: "k&r" -*-
2
+
3
+ strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
4
+ Copyright (C) 2000, 2004 by Martin Pool <mbp sourcefrog net>
5
+
6
+ This software is provided 'as-is', without any express or implied
7
+ warranty. In no event will the authors be held liable for any damages
8
+ arising from the use of this software.
9
+
10
+ Permission is granted to anyone to use this software for any purpose,
11
+ including commercial applications, and to alter it and redistribute it
12
+ freely, subject to the following restrictions:
13
+
14
+ 1. The origin of this software must not be misrepresented; you must not
15
+ claim that you wrote the original software. If you use this software
16
+ in a product, an acknowledgment in the product documentation would be
17
+ appreciated but is not required.
18
+ 2. Altered source versions must be plainly marked as such, and must not be
19
+ misrepresented as being the original software.
20
+ 3. This notice may not be removed or altered from any source distribution.
21
+ */
22
+
23
+
24
+ /* CUSTOMIZATION SECTION
25
+ *
26
+ * You can change this typedef, but must then also change the inline
27
+ * functions in strnatcmp.c */
28
+ typedef char nat_char;
29
+
30
+ int strnatcmp(nat_char const *a, nat_char const *b);
31
+ int strnatcasecmp(nat_char const *a, nat_char const *b);
@@ -0,0 +1,17 @@
1
+ module Enumerable
2
+ def natural_sort
3
+ sort {|a, b| SortAuthority.strnatcmp(a, b) }
4
+ end
5
+
6
+ def natural_sort!
7
+ sort! {|a, b| SortAuthority.strnatcmp(a, b) }
8
+ end
9
+
10
+ def natural_sort_by(&block)
11
+ sort {|a, b| SortAuthority.strnatcmp(block.call(a), block.call(b)) }
12
+ end
13
+
14
+ def natural_sort_by!(&block)
15
+ sort! {|a, b| SortAuthority.strnatcmp(block.call(a), block.call(b)) }
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module SortAuthority
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,8 @@
1
+ require 'ffi'
2
+ require 'ffi-compiler/loader'
3
+
4
+ module SortAuthority
5
+ extend FFI::Library
6
+ ffi_lib FFI::Compiler::Loader.find('strnatcmp')
7
+ attach_function :strnatcmp, [:pointer, :pointer], :int
8
+ end
@@ -0,0 +1,20 @@
1
+ require File.expand_path('../lib/sort_authority/version', __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'sort_authority'
5
+ s.version = SortAuthority::VERSION
6
+ s.authors = ['Nathan Fixler']
7
+ s.email = 'nathan@fixler.org'
8
+ s.license = 'MIT'
9
+ s.summary = 'Natural order string comparison'
10
+ s.description = 'String ordering for humans.'
11
+ s.homepage = 'https://rubygems.org/gems/sort_authority'
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.extensions << 'ext/Rakefile'
15
+
16
+ s.add_dependency 'rake'
17
+ s.add_dependency 'ffi-compiler', '~> 0.1.3'
18
+
19
+ s.add_development_dependency 'minitest', '~> 5.0.0'
20
+ end
@@ -0,0 +1,8 @@
1
+ require 'test_helper'
2
+
3
+ describe 'SortAuthority Benchmark' do
4
+ bench_performance_linear '#natural_sort' do |n|
5
+ ary = ['x1'] * n
6
+ ary.natural_sort
7
+ end
8
+ end
@@ -0,0 +1,33 @@
1
+ require 'test_helper'
2
+
3
+ describe Enumerable do
4
+ it 'natural sorts' do
5
+ assert_equal ['9m', '10m'], ['10m', '9m'].natural_sort
6
+ end
7
+
8
+ it 'natural sorts in place' do
9
+ actual = ['10m', '9m']
10
+ actual.natural_sort!
11
+ assert_equal ['9m', '10m'], actual
12
+ end
13
+
14
+ it 'natural sorts with a block' do
15
+ actual = []
16
+ actual << OpenStruct.new(name: 'hello world')
17
+ actual << OpenStruct.new(name: 'hello world 2')
18
+ actual << OpenStruct.new(name: 'hello 2 world')
19
+ assert_equal ['hello 2 world', 'hello world', 'hello world 2'],
20
+ actual.natural_sort_by(&:name).map(&:name)
21
+ end
22
+
23
+ it 'natural sorts with a block in place' do
24
+ actual = []
25
+ actual << OpenStruct.new(name: 'hello world')
26
+ actual << OpenStruct.new(name: 'hello world 2')
27
+ actual << OpenStruct.new(name: 'hello 2 world')
28
+ actual.natural_sort_by!(&:name)
29
+ assert_equal ['hello 2 world', 'hello world', 'hello world 2'],
30
+ actual.map(&:name)
31
+ end
32
+
33
+ end
@@ -0,0 +1,53 @@
1
+ require 'test_helper'
2
+
3
+ describe 'strnatcmp' do
4
+ include SortAuthority
5
+
6
+ it 'sorts two empty strings' do
7
+ assert_equal 0, strnatcmp('', '')
8
+ end
9
+
10
+ it 'sorts two identical strings with letters' do
11
+ assert_equal 0, strnatcmp('x', 'x')
12
+ end
13
+
14
+ it 'sorts two identical strings with letters and numbers' do
15
+ assert_equal 0, strnatcmp('x1', 'x1')
16
+ end
17
+
18
+ it 'sorts letters (x < y)' do
19
+ assert_equal -1, strnatcmp('x', 'y')
20
+ end
21
+
22
+ it 'sorts letters (y > x)' do
23
+ assert_equal 1, strnatcmp('y', 'x')
24
+ end
25
+
26
+ it 'sorts letters and numbers (x1 < y1)' do
27
+ assert_equal -1, strnatcmp('x1', 'y1')
28
+ end
29
+
30
+ it 'sorts letters and numbers (y1 > x1)' do
31
+ assert_equal 1, strnatcmp('y1', 'x1')
32
+ end
33
+
34
+ it 'sorts letters and numbers (x1 < x2)' do
35
+ assert_equal -1, strnatcmp('x1', 'x2')
36
+ end
37
+
38
+ it 'sorts letters and numbers (x2 > x1)' do
39
+ assert_equal 1, strnatcmp('x2', 'x1')
40
+ end
41
+
42
+ it 'sorts numbers numerically' do
43
+ assert_equal -1, strnatcmp('9', '10')
44
+ end
45
+
46
+ it 'sorts multi-word strings that are equal' do
47
+ assert_equal 0, strnatcmp('x 1', 'x 1')
48
+ end
49
+
50
+ it 'sorts multi-word strings that are not equal' do
51
+ assert_equal -1, strnatcmp('x 1', 'x 2')
52
+ end
53
+ end
@@ -0,0 +1,9 @@
1
+ require_relative '../lib/sort_authority'
2
+ require_relative '../lib/sort_authority/ext/enumerable'
3
+
4
+ gem 'minitest'
5
+
6
+ require 'minitest/autorun'
7
+ require 'minitest/spec'
8
+ require 'minitest/benchmark'
9
+ require 'ostruct'
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sort_authority
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Nathan Fixler
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-08-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: ffi-compiler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.1.3
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.1.3
46
+ - !ruby/object:Gem::Dependency
47
+ name: minitest
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 5.0.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 5.0.0
62
+ description: String ordering for humans.
63
+ email: nathan@fixler.org
64
+ executables: []
65
+ extensions:
66
+ - ext/Rakefile
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - Gemfile
71
+ - LICENSE
72
+ - README.md
73
+ - Rakefile
74
+ - ext/Rakefile
75
+ - ext/strnatcmp.c
76
+ - ext/strnatcmp.h
77
+ - lib/sort_authority.rb
78
+ - lib/sort_authority/ext/enumerable.rb
79
+ - lib/sort_authority/version.rb
80
+ - sort_authority.gemspec
81
+ - test/benchmark_test.rb
82
+ - test/ext/enumerable_test.rb
83
+ - test/strnatcmp_test.rb
84
+ - test/test_helper.rb
85
+ homepage: https://rubygems.org/gems/sort_authority
86
+ licenses:
87
+ - MIT
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ segments:
99
+ - 0
100
+ hash: -215718808201645381
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ! '>='
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ segments:
108
+ - 0
109
+ hash: -215718808201645381
110
+ requirements: []
111
+ rubyforge_project:
112
+ rubygems_version: 1.8.23
113
+ signing_key:
114
+ specification_version: 3
115
+ summary: Natural order string comparison
116
+ test_files: []