order_already 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 58bbcf6f098dfc3b905837315feb9634415f5ffd97402acb4ff0aaf783d3fb94
4
+ data.tar.gz: 97c0f11482e7c1170b737a9fcb556c7f2a6825b61f562f9bd655c72be39fa3d6
5
+ SHA512:
6
+ metadata.gz: 83034c9a260068af690abff7de0d4a92cd0fae477e10b9428f6a71a296fd2af9b8246c43c85549e9994800e609f23c2d393c6890f4579b561477e30f300d0c5c
7
+ data.tar.gz: b008abc0792f025b468a875d41021797cfa708bd3d28782aa27487e1225736a52fc7e93cbeca85db54b0481c4cb0793d5161509983ea0687013532b6c815f01b
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,19 @@
1
+ # The behavior of RuboCop can be controlled via the .rubocop.yml
2
+ # configuration file. It makes it possible to enable/disable
3
+ # certain cops (checks) and to alter their behavior if they accept
4
+ # any parameters. The file can be placed either in your home
5
+ # directory or in some project directory.
6
+ #
7
+ # RuboCop will start looking for the configuration file in the directory
8
+ # where the inspected file is and continue its way up to the root directory.
9
+ #
10
+ # See https://docs.rubocop.org/rubocop/configuration
11
+
12
+ inherit_gem:
13
+ bixby: bixby_default.yml
14
+
15
+ AllCops:
16
+ NewCops: enable
17
+ SuggestExtensions: false
18
+ TargetRubyVersion: 2.6
19
+ DisplayCopNames: true
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in order_already.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
data/Gemfile.lock ADDED
@@ -0,0 +1,92 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ order_already (0.1.0)
5
+ rails-html-sanitizer (~> 1.4)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activesupport (7.0.4)
11
+ concurrent-ruby (~> 1.0, >= 1.0.2)
12
+ i18n (>= 1.6, < 2)
13
+ minitest (>= 5.1)
14
+ tzinfo (~> 2.0)
15
+ ast (2.4.2)
16
+ bixby (5.0.2)
17
+ rubocop (= 1.28.2)
18
+ rubocop-ast
19
+ rubocop-performance
20
+ rubocop-rails
21
+ rubocop-rspec
22
+ concurrent-ruby (1.1.10)
23
+ crass (1.0.6)
24
+ diff-lcs (1.5.0)
25
+ i18n (1.12.0)
26
+ concurrent-ruby (~> 1.0)
27
+ loofah (2.19.0)
28
+ crass (~> 1.0.2)
29
+ nokogiri (>= 1.5.9)
30
+ minitest (5.16.3)
31
+ nokogiri (1.13.9-arm64-darwin)
32
+ racc (~> 1.4)
33
+ parallel (1.22.1)
34
+ parser (3.1.2.1)
35
+ ast (~> 2.4.1)
36
+ racc (1.6.0)
37
+ rack (3.0.0)
38
+ rails-html-sanitizer (1.4.3)
39
+ loofah (~> 2.3)
40
+ rainbow (3.1.1)
41
+ rake (13.0.6)
42
+ regexp_parser (2.6.1)
43
+ rexml (3.2.5)
44
+ rspec (3.12.0)
45
+ rspec-core (~> 3.12.0)
46
+ rspec-expectations (~> 3.12.0)
47
+ rspec-mocks (~> 3.12.0)
48
+ rspec-core (3.12.0)
49
+ rspec-support (~> 3.12.0)
50
+ rspec-expectations (3.12.0)
51
+ diff-lcs (>= 1.2.0, < 2.0)
52
+ rspec-support (~> 3.12.0)
53
+ rspec-mocks (3.12.0)
54
+ diff-lcs (>= 1.2.0, < 2.0)
55
+ rspec-support (~> 3.12.0)
56
+ rspec-support (3.12.0)
57
+ rubocop (1.28.2)
58
+ parallel (~> 1.10)
59
+ parser (>= 3.1.0.0)
60
+ rainbow (>= 2.2.2, < 4.0)
61
+ regexp_parser (>= 1.8, < 3.0)
62
+ rexml
63
+ rubocop-ast (>= 1.17.0, < 2.0)
64
+ ruby-progressbar (~> 1.7)
65
+ unicode-display_width (>= 1.4.0, < 3.0)
66
+ rubocop-ast (1.23.0)
67
+ parser (>= 3.1.1.0)
68
+ rubocop-performance (1.15.1)
69
+ rubocop (>= 1.7.0, < 2.0)
70
+ rubocop-ast (>= 0.4.0)
71
+ rubocop-rails (2.15.2)
72
+ activesupport (>= 4.2.0)
73
+ rack (>= 1.1)
74
+ rubocop (>= 1.7.0, < 2.0)
75
+ rubocop-rspec (2.11.1)
76
+ rubocop (~> 1.19)
77
+ ruby-progressbar (1.11.0)
78
+ tzinfo (2.0.5)
79
+ concurrent-ruby (~> 1.0)
80
+ unicode-display_width (2.3.0)
81
+
82
+ PLATFORMS
83
+ arm64-darwin-21
84
+
85
+ DEPENDENCIES
86
+ bixby (~> 5.0, >= 5.0.2)
87
+ order_already!
88
+ rake (~> 13.0)
89
+ rspec (~> 3.0)
90
+
91
+ BUNDLED WITH
92
+ 2.3.9
data/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ Copyright 2022 Software Services by Scientist.com
2
+
3
+ Additional copyright may be held by others, as reflected in the commit history.
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License");
6
+ you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # OrderAlready
2
+
3
+ In the spirit of Browse Everything and Questioning Authority, the Order Already module provides a simple interface for ordering properties that have an indeterminate persistence order (looking at you Fedora Commons and RDF N-Triples).
4
+
5
+ To do this, OrderAlready takes liberties with the persistence layer's storage of the ordered values; (e.g. we prepend an index and delimiter to each value then at object reification we sort on that index). You may not feel comfortable with this solution, because Order Already munges your canonical data. And that's understandable.
6
+
7
+ However, the other options for sorting is described in the theoretical in the [Portland Common Data Model's Wiki](https://github.com/duraspace/pcdm/wiki#ordering-extension). This is rather impractical, and non-performant, for attributes of a pcdm::Object, such as their creators, subjects, etc.
8
+
9
+ ## Installation
10
+
11
+ Install the gem and add to the application's Gemfile by executing:
12
+
13
+ $ bundle add order_already
14
+
15
+ If bundler is not being used to manage dependencies, install the gem by executing:
16
+
17
+ $ gem install order_already
18
+
19
+ ## Usage
20
+
21
+ With a plain old Ruby object (PORO):
22
+
23
+ ```ruby
24
+ class MyModel
25
+ attr_accessor :author, :subject
26
+
27
+ include OrderAlready.for(:author, :subject)
28
+ end
29
+ ```
30
+
31
+ With Samvera Portland Common Data Model (PCDM) object:
32
+
33
+ ```ruby
34
+ class GenericWork < ActiveFedora::Base
35
+ include ::Hyrax::WorkBehavior
36
+
37
+ self.indexer = GenericWorkIndexer
38
+ # Change this to restrict which works can be added as a child.
39
+ # self.valid_child_concerns = []
40
+ validates :title, presence: { message: 'Your work must have a title.' }
41
+
42
+ # This must be included at the end, because it finalizes the metadata
43
+ # schema (by adding accepts_nested_attributes)
44
+ include ::Hyrax::BasicMetadata
45
+
46
+ include OrderAlready.for(:creator, :contributor)
47
+ end
48
+ ```
49
+
50
+ ## Development
51
+
52
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
53
+
54
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
55
+
56
+ ## Contributing
57
+
58
+ Bug reports and pull requests are welcome on GitHub at https://github.com/scientist-softserv/order_already.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require 'rubocop/rake_task'
9
+ RuboCop::RakeTask.new(:rubocop)
10
+
11
+ task default: %i[rubocop spec]
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OrderAlready
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "order_already/version"
4
+ # require "set"
5
+ # require "loofah"
6
+ require "rails-html-sanitizer"
7
+ # require "rails/html/scrubbers"
8
+
9
+ # In the spirit of Browse Everything and Questioning Authority, the Order Already module provides a
10
+ # simple interface for ordering properties that have an indeterminate persistence order (looking at
11
+ # you Fedora Commons and RDF N-Triples).
12
+ #
13
+ # To do this, we take liberties with the persistence layer's values (e.g. we prepend an index and
14
+ # delimiter to the given value). You may not feel comfortable with this solution, because you are
15
+ # munging your data's
16
+ module OrderAlready
17
+ class Error < StandardError; end
18
+
19
+ # @api public
20
+ #
21
+ # @param attributes [Array<Symbol>] the name of the attributes/properties you want to order.
22
+ # @return [Module] a module that wraps the attr_accessor methods for the given :attributes.
23
+ #
24
+ # @note In testing, you need to use `prepend` instead of `include`; but that may be a function of
25
+ # my specs.
26
+ #
27
+ # @example
28
+ # class MyRecord
29
+ # attr_reader :creators
30
+ # def creators=(values)
31
+ # # We're going to "persist" these in an "arbitrarily different" way than what the user
32
+ # # provided.
33
+ # @creators = Array(values).reverse
34
+ # end
35
+ # prepend OrderAlready.for(:creators)
36
+ # end
37
+ def self.for(*attributes)
38
+ # By creating a module, we have access to `super`.
39
+ Module.new do
40
+ attributes.each do |attribute|
41
+ define_method(attribute) do
42
+ StringSerializer.deserialize(super())
43
+ end
44
+
45
+ define_method("#{attribute}=") do |values|
46
+ super(StringSerializer.serialize(values))
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ module StringSerializer
53
+ # defaults
54
+ TOKEN_DELIMITER = '~'
55
+
56
+ # Convert a serialized array to a normal array of values.
57
+ # @param arr [Array]
58
+ # @return [Array]
59
+ def self.deserialize(arr)
60
+ return [] if arr&.empty?
61
+
62
+ sort(arr).map do |val|
63
+ get_value(val)
64
+ end
65
+ end
66
+
67
+ # Serialize a normal array of values to an array of ordered values
68
+ #
69
+ # @param arr [Array]
70
+ # @return [Array]
71
+ def self.serialize(arr)
72
+ return [] if arr&.empty?
73
+
74
+ arr = sanitize(arr)
75
+
76
+ res = []
77
+ arr.each_with_index do |val, ix|
78
+ res << encode(ix, val)
79
+ end
80
+
81
+ res
82
+ end
83
+
84
+ def self.sanitize(values)
85
+ full_sanitizer = Rails::Html::FullSanitizer.new
86
+ sanitized_values = Array.new(values.size, '')
87
+ empty = TOKEN_DELIMITER * 3
88
+ values.each_with_index do |v, i|
89
+ sanitized_values[i] = full_sanitizer.sanitize(v) unless v == empty
90
+ end
91
+ end
92
+ private_class_method :sanitize
93
+
94
+ # Sort an array of serialized values using the index token to determine the order
95
+ def self.sort(arr)
96
+ # Hack to force a stable sort; see https://stackoverflow.com/questions/15442298/is-sort-in-ruby-stable
97
+ n = 0
98
+ arr.sort_by { |val| [get_index(val), n += 1] }
99
+ end
100
+ private_class_method :sort
101
+
102
+ #
103
+ # encode an index and a value into a composite field
104
+ #
105
+ def self.encode(index, val)
106
+ "#{index}#{TOKEN_DELIMITER}#{val}"
107
+ end
108
+ private_class_method :encode
109
+
110
+ # extract the index attribute from the serialized value; return index '0' if the
111
+ # field cannot be parsed correctly
112
+ def self.get_index(val)
113
+ tokens = val.split(TOKEN_DELIMITER, 2)
114
+ return tokens[0] if tokens.length == 2
115
+
116
+ '0'
117
+ end
118
+ private_class_method :get_index
119
+
120
+ # extract the value attribute from the serialized value; return the entire value if the
121
+ # field cannot be parsed correctly
122
+ #
123
+ def self.get_value(val)
124
+ tokens = val.split(TOKEN_DELIMITER, 2)
125
+ return tokens[1] if tokens.length == 2
126
+
127
+ val
128
+ end
129
+ private_class_method :get_value
130
+
131
+ # @api private
132
+ #
133
+ # convert an ActiveTriples::Relation to a standard array (for debugging)
134
+ def self.relation_to_array(arr)
135
+ arr.map(&:to_s)
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/order_already/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "order_already"
7
+ spec.version = OrderAlready::VERSION
8
+ spec.authors = ["Jeremy Friesen", "Rob Kaufman"]
9
+ spec.email = ["jeremy.n.friesen@gmail.com", "rob@notch8.com"]
10
+
11
+ spec.summary = "A tiny gem to provide naive sorting for Fedora Commons objects."
12
+ spec.description = "A tiny gem to provide naive sorting for Fedora Commons objects."
13
+ spec.homepage = "https://github.com/scientist-softserv/order_already"
14
+ spec.required_ruby_version = ">= 2.6.0"
15
+
16
+ spec.licenses = ["Apache-2.0"]
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = spec.homepage
19
+ spec.metadata["changelog_uri"] = File.join(spec.homepage, "CHANGELOG.md")
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject do |f|
25
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
26
+ end
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ # Uncomment to register a new dependency of your gem
33
+ # spec.add_dependency "example-gem", "~> 1.0"
34
+
35
+ # For more information and examples about making a new gem, check out our
36
+ # guide at: https://bundler.io/guides/creating_gem.html
37
+
38
+ spec.add_dependency "rails-html-sanitizer", "~> 1.4"
39
+
40
+ spec.add_development_dependency "rspec", "~> 3.0"
41
+ spec.add_development_dependency 'bixby', '~> 5.0', '>= 5.0.2' # bixby 5 briefly dropped Ruby 2.5
42
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: order_already
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jeremy Friesen
8
+ - Rob Kaufman
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2022-11-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails-html-sanitizer
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.4'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.4'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rspec
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '3.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '3.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: bixby
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '5.0'
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 5.0.2
52
+ type: :development
53
+ prerelease: false
54
+ version_requirements: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - "~>"
57
+ - !ruby/object:Gem::Version
58
+ version: '5.0'
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 5.0.2
62
+ description: A tiny gem to provide naive sorting for Fedora Commons objects.
63
+ email:
64
+ - jeremy.n.friesen@gmail.com
65
+ - rob@notch8.com
66
+ executables: []
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - ".rspec"
71
+ - ".rubocop.yml"
72
+ - Gemfile
73
+ - Gemfile.lock
74
+ - LICENSE
75
+ - README.md
76
+ - Rakefile
77
+ - lib/order_already.rb
78
+ - lib/order_already/version.rb
79
+ - order_already.gemspec
80
+ homepage: https://github.com/scientist-softserv/order_already
81
+ licenses:
82
+ - Apache-2.0
83
+ metadata:
84
+ homepage_uri: https://github.com/scientist-softserv/order_already
85
+ source_code_uri: https://github.com/scientist-softserv/order_already
86
+ changelog_uri: https://github.com/scientist-softserv/order_already/CHANGELOG.md
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: 2.6.0
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubygems_version: 3.3.7
103
+ signing_key:
104
+ specification_version: 4
105
+ summary: A tiny gem to provide naive sorting for Fedora Commons objects.
106
+ test_files: []