lexicoid 0.1.0
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 +7 -0
- data/CHANGELOG.md +13 -0
- data/CONTRIBUTING.md +55 -0
- data/LICENSE.md +21 -0
- data/README.md +92 -0
- data/lexicoid.gemspec +33 -0
- data/lib/core_ext/kernel.rb +33 -0
- data/lib/lexicoid/version.rb +8 -0
- data/lib/lexicoid.rb +192 -0
- metadata +71 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6c5ba2e47db70f981a8b76ba0424726acab9b805b97daad96ae00aeac5201ad4
|
4
|
+
data.tar.gz: 7654c97ba2b93b91b4930768c4834b3e1127ff9ba793475cf3f683023f44da89
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9278e656eb7ae2506868f70d01920a88d17cfbde48218739bf6280b77ee721db1ae55f2f19ca4c08ce5eaac7c38c1e78c7447733c54aeb47a96c8f0556c2c55c
|
7
|
+
data.tar.gz: a49464b8626babea74781bc8cbfd864c565cb2b8a3939d49d86d497d9e06e5adf280de5ea9e0d112cc367ac7435f95651a5d1e654ed6b71c8e5bf2eedcced239
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
|
+
|
7
|
+
## [0.1.0](https://github.com/michaelherold/lexicoid-ruby/tree/0.1.0) - 2023-03-19
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
- Converting timestamps in the form of `Integer`s, `Float`s, `Time`s, `DateTime`s, and anything responding to `#to_date`, including `ActiveSupport::TimeWithZone` via `Lexicoid.from`.
|
12
|
+
- Generating the lexicoid for the current moment with `Lexicoid.now`.
|
13
|
+
- Converting timestamps with the `Kernel#Lexicoid` method, with optional exception squelching via `Lexicoid(Time.now, exception: false)`.
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# Contributing
|
2
|
+
|
3
|
+
In the spirit of [free software](http://www.fsf.org/licensing/essays/free-sw.html), we encourage **everyone** to help improve this project. Here are some ways _you_ can contribute:
|
4
|
+
|
5
|
+
* Use alpha, beta, and pre-release versions.
|
6
|
+
* Report bugs.
|
7
|
+
* Suggest new features.
|
8
|
+
* Write or edit documentation.
|
9
|
+
* Write specifications.
|
10
|
+
* Write code (**no patch is too small**: fix typos, add comments, clean up inconsistent whitespace).
|
11
|
+
* Refactor code.
|
12
|
+
* Fix [issues].
|
13
|
+
* Review patches.
|
14
|
+
|
15
|
+
[issues]: https://github.com/michaelherold/lexicoid-ruby/issues
|
16
|
+
|
17
|
+
## Submitting an Issue
|
18
|
+
|
19
|
+
We use the [GitHub issue tracker][issues] to track bugs and features. Before submitting a bug report or feature request, check to make sure no one has already submitted it.
|
20
|
+
|
21
|
+
When submitting a bug report, please include a `<details>` block that includes a stack trace and any details that may be necessary to reproduce the bug, including your gem version, Ruby version, and operating system. This looks like the following:
|
22
|
+
|
23
|
+
```markdown
|
24
|
+
<details>
|
25
|
+
<summary>A description of the details block</summary>
|
26
|
+
|
27
|
+
All of the content that you want in here, perhaps with code fences. Note that
|
28
|
+
if you only have a code fence in here, you _must_ separate it from the <summary>
|
29
|
+
tag and the closing </details> or it won't render correctly.
|
30
|
+
|
31
|
+
Notice the empty line here ↓
|
32
|
+
|
33
|
+
</details>
|
34
|
+
```
|
35
|
+
|
36
|
+
Ideally, a bug report should include a pull request with failing specs.
|
37
|
+
|
38
|
+
## Submitting a Pull Request
|
39
|
+
|
40
|
+
1. Fork the repository.
|
41
|
+
2. Create a topic branch.
|
42
|
+
3. Add specs for your unimplemented feature or bug fix.
|
43
|
+
4. Run `bundle exec rake test`. If your specs pass, return to step 3.
|
44
|
+
5. Implement your feature or bug fix.
|
45
|
+
6. Run `bundle exec rake`. If your specs or any of the linters fail, return to step 5.
|
46
|
+
7. Open `coverage/index.html`. If your changes are not fully covered by your tests, return to step 3.
|
47
|
+
8. Add documentation for your feature or bug fix.
|
48
|
+
9. Commit and push your changes.
|
49
|
+
10. Submit a pull request.
|
50
|
+
|
51
|
+
## Tools to Help You Succeed
|
52
|
+
|
53
|
+
After checking out the repository, run `bin/setup` to install dependencies. Then, run `bundle exec rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
54
|
+
|
55
|
+
Before committing code, run `bundle exec rake` to check that the code conforms to the style guidelines of the project, that all of the tests are green (if you're writing a feature; if you're only submitting a failing test, then it does not have to pass!), and that you sufficiently documented the changes.
|
data/LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright © 2023 Michael Herold
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# Lexicoid for Ruby
|
2
|
+
|
3
|
+
Have you ever needed to convert a timestamp into something short, memorable, easy to look at, and easy to type? Look no further! You want a "lexicoid."
|
4
|
+
|
5
|
+
Inspired by [Brandur Leach](https://brandur.org/fragments/base32-slugs), Lexicoid for Ruby gives you lexicographically sortable, time-based identifiers that are easy to read and easy to type; perfect for using in URLs and other places that take human input.
|
6
|
+
|
7
|
+
Use lexicoids for anywhere that you have a timestamp that operates on "human scale" … think microblog status identifiers or URL-encoded timestamps. Avoid them for distributed systems; instead, consider something like [KSUID](https://github.com/michaelherold/ksuid-ruby) for that use case.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Install the gem and add it to the application's Gemfile by executing:
|
12
|
+
|
13
|
+
$ bundle add lexicoid
|
14
|
+
|
15
|
+
If you are not using Bundler to manage dependencies, install the gem by executing:
|
16
|
+
|
17
|
+
$ gem install lexicoid
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Generate a lexical identifier for the current moment:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
Lexicoid.now
|
25
|
+
```
|
26
|
+
|
27
|
+
Convert any time-related object to a lexical identifier:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
# Integer
|
31
|
+
Lexicoid.from(1_678_767_177)
|
32
|
+
|
33
|
+
# Float
|
34
|
+
Lexicoid.from(1_678_767_285.293477)
|
35
|
+
|
36
|
+
# Time
|
37
|
+
Lexicoid.from(Time.now)
|
38
|
+
|
39
|
+
# DateTime
|
40
|
+
Lexicoid.from(DateTime.now)
|
41
|
+
|
42
|
+
# ActiveSupport::TimeWithZone
|
43
|
+
Lexicoid.from(2.weeks.ago)
|
44
|
+
```
|
45
|
+
|
46
|
+
There is also a `Kernel` method available for converting objects to lexicoids:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
Lexicoid(Time.now)
|
50
|
+
|
51
|
+
Lexicoid("oops") #=> raises ArgumentError
|
52
|
+
|
53
|
+
Lexicoid("oops", exception: false) #=> ""
|
54
|
+
```
|
55
|
+
|
56
|
+
## Contributing
|
57
|
+
|
58
|
+
So you're interested in contributing to Lexicoid? Check out our [contributing guidelines](CONTRIBUTING.md) for more information on how to do that.
|
59
|
+
|
60
|
+
## Supported Ruby Versions
|
61
|
+
|
62
|
+
This library aims to support and is [tested against](https://github.com/michaelherold/lexicoid-ruby/actions) the following Ruby versions:
|
63
|
+
|
64
|
+
* Ruby 3.0
|
65
|
+
* Ruby 3.1
|
66
|
+
* Ruby 3.2
|
67
|
+
|
68
|
+
If something doesn't work on one of these versions, it's a bug.
|
69
|
+
|
70
|
+
This library may inadvertently work (or seem to work) on other Ruby versions, however we will only provide support for the versions listed above.
|
71
|
+
|
72
|
+
If you would like this library to support another Ruby version or implementation, you may volunteer to be a maintainer. Being a maintainer entails making sure all tests run and pass on that implementation. When something breaks on your implementation, you will be responsible for providing patches in a timely fashion. If critical issues for a particular implementation exist at the time of a major release, we may drop support for that Ruby version.
|
73
|
+
|
74
|
+
## Versioning
|
75
|
+
|
76
|
+
This library aims to adhere to [Semantic Versioning 2.0.0](http://semver.org/spec/v2.0.0.html). Report violations of this scheme should as bugs. Specifically, if a minor or patch version breaks backward compatibility, that version should be immediately yanked and/or a new version should be immediately released that restores compatibility. Only new major versions will introduce breaking changes to the public API. As a result of this policy, you can (and should) specify a dependency on this gem using the [pessimistic version constraint](http://guides.rubygems.org/patterns/#pessimistic-version-constraint) with two digits of precision. For example:
|
77
|
+
|
78
|
+
spec.add_dependency "lexicoid", "~> 0.1"
|
79
|
+
|
80
|
+
## License
|
81
|
+
|
82
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
83
|
+
|
84
|
+
## Code of Conduct
|
85
|
+
|
86
|
+
We expect everyone interacting in the Lexicoid for Ruby project's codebases, issue trackers, chat rooms and mailing lists to follow the [code of conduct](https://github.com/michaelherold/lexicoid/blob/main/CODE_OF_CONDUCT.md).
|
87
|
+
|
88
|
+
## Acknowledgments
|
89
|
+
|
90
|
+
[Brandur Leach](https://brandur.org/fragments/base32-slugs) wrote about the concept behind this gem and inspired its creation.
|
91
|
+
|
92
|
+
[Luciano Mammino](https://loige.co/) took the first step, created a [Rust crate](https://crates.io/crates/lexicoid) and came up with the [excellent name](https://github.com/lmammino/lexicoid) for these identifiers.
|
data/lexicoid.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/lexicoid/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "lexicoid"
|
7
|
+
spec.version = Lexicoid::VERSION
|
8
|
+
spec.authors = ["Michael Herold"]
|
9
|
+
spec.email = ["opensource@michaeljherold.com"]
|
10
|
+
|
11
|
+
spec.summary = "Short and stable IDs from timestamps"
|
12
|
+
spec.description = spec.summary
|
13
|
+
spec.homepage = "https://github.com/michaelherold/lexicoid-ruby"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = %w[CHANGELOG.md CONTRIBUTING.md LICENSE.md README.md]
|
17
|
+
spec.files += %w[lexicoid.gemspec]
|
18
|
+
spec.files += Dir["lib/**/*.rb"]
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.required_ruby_version = ">= 3.0.0"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", ">= 2"
|
24
|
+
|
25
|
+
spec.metadata = {
|
26
|
+
"bug_tracker_uri" => "https://github.com/michaelherold/lexicoid-ruby/issues",
|
27
|
+
"changelog_uri" => "https://github.com/michaelherold/lexicoid-ruby/blob/main/CHANGELOG.md",
|
28
|
+
"documentation_uri" => "https://rubydoc.info/gems/lexicoid-ruby/#{Lexicoid::VERSION}",
|
29
|
+
"homepage_uri" => "https://github.com/michaelherold/lexicoid-ruby",
|
30
|
+
"rubygems_mfa_required" => "true",
|
31
|
+
"source_code_uri" => "https://github.com/michaelherold/lexicoid-ruby"
|
32
|
+
}
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# The `Kernel` module is included by class `Object`, so its methods are available in every Ruby object.
|
4
|
+
#
|
5
|
+
# @see https://ruby-doc.org/current/Kernel.html
|
6
|
+
module Kernel
|
7
|
+
private
|
8
|
+
|
9
|
+
# Returns a lexicographically sortable friendly ID for a timestamp
|
10
|
+
#
|
11
|
+
# @api public
|
12
|
+
# @since 0.1.0
|
13
|
+
# @!visibility public
|
14
|
+
#
|
15
|
+
# @example Converts a post's created-at timestamp to a friendly ID
|
16
|
+
# Post = Struct.new(:created_at, keyword_init: true)
|
17
|
+
# post = Post.new(created_at: Time.parse("2023-03-19 14:33:01.80251 -0500"))
|
18
|
+
#
|
19
|
+
# Lexicoid(post.created_at) #=> "gkfqavc"
|
20
|
+
#
|
21
|
+
# @param object [ActiveSupport::TimeWithZone, DateTime, Float, Integer, Time, #to_time]
|
22
|
+
# the object to convert into a lexicographically sorting friendly ID
|
23
|
+
# @param exception [Boolean] raises an ArgumentError when the argument is
|
24
|
+
# improper and this is true, otherwise an improper argument leads to an empty string
|
25
|
+
# @return [String] the friendly ID
|
26
|
+
# @raise [ArgumentError] when the object is not a timestamp-like one
|
27
|
+
def Lexicoid(object, exception: true)
|
28
|
+
Lexicoid.from(object)
|
29
|
+
rescue ArgumentError
|
30
|
+
raise if exception
|
31
|
+
""
|
32
|
+
end
|
33
|
+
end
|
data/lib/lexicoid.rb
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lexicoid/version"
|
4
|
+
|
5
|
+
# Generates lexicographically sortable, friendly IDs from timestamps
|
6
|
+
#
|
7
|
+
# A "lexicoid" is a short identifier derived from a timestamp for use in single
|
8
|
+
# source streaming systems. They are based on an [approach for giving friendly
|
9
|
+
# URLs to microblog posts, by Brandur Leach][1].
|
10
|
+
#
|
11
|
+
# In order to make them easy to use, lexicoids have the following properties:
|
12
|
+
#
|
13
|
+
# 1. They are lexicographically, i.e. alphabetically, sortable by time to the
|
14
|
+
# second.
|
15
|
+
# 2. They encode to lower case letters for easier typing into a URL.
|
16
|
+
# 3. Their alphabet eliminates easily-confused letter combinations like `1`,
|
17
|
+
# `l`, and `I`, or `0` and `O`.
|
18
|
+
#
|
19
|
+
# At their heart, lexicoids are a special case of [Base 32 encoding][2] for
|
20
|
+
# integers.
|
21
|
+
#
|
22
|
+
# Their properties makes them ideally suitable to human-scale use cases, like
|
23
|
+
# microblog post slugs or URL-encoded timestamps. They are poorly suited for
|
24
|
+
# distributed systems with multiple clocks or systems that generate items at a
|
25
|
+
# rate of more than one per second, regardless of the number of clocks.
|
26
|
+
#
|
27
|
+
# [1]: https://brandur.org/fragments/base32-slugs
|
28
|
+
# [2]: https://datatracker.ietf.org/doc/html/rfc4648
|
29
|
+
module Lexicoid
|
30
|
+
# The Time instance representing the time at the beginning of the Unix epoch
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
# @private
|
34
|
+
EPOCH = Time.at(0).utc.freeze
|
35
|
+
|
36
|
+
# The alphabet for encoding lexicographic base32 IDs
|
37
|
+
#
|
38
|
+
# @api private
|
39
|
+
# @private
|
40
|
+
LEXICOGRAPHIC_BASE32 = "234567abcdefghijklmnopqrstuvwxyz"
|
41
|
+
|
42
|
+
# Converts a timestamp into a lexicographically sortable, friendly ID
|
43
|
+
#
|
44
|
+
# @api public
|
45
|
+
# @since 0.1.0
|
46
|
+
#
|
47
|
+
# @example Generate a lexicoid from a DateTime
|
48
|
+
# Lexicoid.from DateTime.parse("2023-03-19T21:19:50-05:00")
|
49
|
+
#
|
50
|
+
# @example Generate a lexicoid from a Float timestamp
|
51
|
+
# Lexicoid.from 2_147_483_647.0
|
52
|
+
#
|
53
|
+
# @example Generate a lexicoid from an Integer timestamp
|
54
|
+
# Lexicoid.from 946_684_799
|
55
|
+
#
|
56
|
+
# @example Generate a lexicoid from a Time
|
57
|
+
# Lexicoid.from Time.parse("2023-03-19 15:06:14.48414 -0500")
|
58
|
+
#
|
59
|
+
# @example Generate a lexicoid in a Rails application
|
60
|
+
# Lexicoid.from 2.weeks.ago
|
61
|
+
#
|
62
|
+
# @param number_or_time [ActiveSupport::TimeWithZone, DateTime, Float, Integer, Time, #to_time]
|
63
|
+
# the object to convert into a lexicographically sorting friendly ID
|
64
|
+
# @return [String] the friendly ID
|
65
|
+
# @raise [ArgumentError] when the object is unsuitable for conversion
|
66
|
+
def self.from(number_or_time)
|
67
|
+
number = maybe_coerce_time(number_or_time)
|
68
|
+
bytes = bytes_from_number(number)
|
69
|
+
buffer = encode(bytes)
|
70
|
+
|
71
|
+
buffer.join
|
72
|
+
end
|
73
|
+
|
74
|
+
# Generates a lexicographically sortable, friendly ID from the current time
|
75
|
+
#
|
76
|
+
# @api public
|
77
|
+
# @since 0.1.0
|
78
|
+
#
|
79
|
+
# @example Generate a friendly ID for the current moment
|
80
|
+
# Lexicoid.now
|
81
|
+
#
|
82
|
+
# @return [String] the friendly ID
|
83
|
+
def self.now
|
84
|
+
from Time.now
|
85
|
+
end
|
86
|
+
|
87
|
+
# Extracts the bytes from a number in big endian order
|
88
|
+
#
|
89
|
+
# @api private
|
90
|
+
# @private
|
91
|
+
#
|
92
|
+
# @param number [Integer] the number to extract bytes from
|
93
|
+
# @return [Array<Integer>] the bytes from the number as integers in big endian
|
94
|
+
# order
|
95
|
+
private_class_method def self.bytes_from_number(number)
|
96
|
+
[].tap do |bytes|
|
97
|
+
bytes.unshift(0) and next if number.zero?
|
98
|
+
|
99
|
+
while number.positive?
|
100
|
+
bytes.unshift(number & 0xFF)
|
101
|
+
number >>= 8
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Encodes a byte array into a lexicographically sortable array of characters
|
107
|
+
#
|
108
|
+
# @api private
|
109
|
+
# @private
|
110
|
+
#
|
111
|
+
# @param bytes [Array<Integer>] the bytes to encode, in big endian order
|
112
|
+
# @return [Array<String>] the bytes encoded to the lexicographic base32 alphabet
|
113
|
+
private_class_method def self.encode(bytes)
|
114
|
+
result = Array.new((bytes.length * 8 + 4) / 5, 0)
|
115
|
+
offset = 0
|
116
|
+
|
117
|
+
while bytes.length.positive?
|
118
|
+
length = bytes.length
|
119
|
+
buffer =
|
120
|
+
case length
|
121
|
+
when 1, 2 then Array.new(bytes.length * 2, 0)
|
122
|
+
when 3, 4 then Array.new(bytes.length * 2 - 1, 0)
|
123
|
+
else Array.new(8, 0)
|
124
|
+
end
|
125
|
+
|
126
|
+
if length > 4
|
127
|
+
buffer[7] = bytes[4] & 0x1F
|
128
|
+
buffer[6] = bytes[4] >> 5
|
129
|
+
end
|
130
|
+
|
131
|
+
if length >= 4
|
132
|
+
buffer[6] |= (bytes[3] << 3) & 0x1F
|
133
|
+
buffer[5] = (bytes[3] >> 2) & 0x1F
|
134
|
+
buffer[4] = bytes[3] >> 7
|
135
|
+
end
|
136
|
+
|
137
|
+
if length >= 3
|
138
|
+
buffer[4] |= (bytes[2] << 1) & 0x1F
|
139
|
+
buffer[3] = (bytes[2] >> 4) & 0x1F
|
140
|
+
end
|
141
|
+
|
142
|
+
if length >= 2
|
143
|
+
buffer[3] |= (bytes[1] << 4) & 0x1F
|
144
|
+
buffer[2] = (bytes[1] >> 1) & 0x1F
|
145
|
+
buffer[1] = (bytes[1] >> 6) & 0x1F
|
146
|
+
end
|
147
|
+
|
148
|
+
if length >= 1
|
149
|
+
buffer[1] |= (bytes[0] << 2) & 0x1F
|
150
|
+
buffer[0] = bytes[0] >> 3
|
151
|
+
end
|
152
|
+
|
153
|
+
buffer.each_with_index do |byte, i|
|
154
|
+
result[i + offset] = LEXICOGRAPHIC_BASE32[byte & 31]
|
155
|
+
end
|
156
|
+
|
157
|
+
break if bytes.length < 5
|
158
|
+
|
159
|
+
bytes = bytes[5..]
|
160
|
+
offset += 8
|
161
|
+
end
|
162
|
+
|
163
|
+
result
|
164
|
+
end
|
165
|
+
|
166
|
+
# Validates and coerces an argument for suitability for conversion
|
167
|
+
#
|
168
|
+
# @api private
|
169
|
+
# @private
|
170
|
+
#
|
171
|
+
# @param object [Object] the object to validate and coerce
|
172
|
+
# @return [Integer] the object converted into an integer timestamp
|
173
|
+
# @raise [ArgumentError] when the object is unsuitable for conversion
|
174
|
+
private_class_method def self.maybe_coerce_time(object)
|
175
|
+
if object.is_a?(Integer) || object.is_a?(Float)
|
176
|
+
raise ArgumentError, "#{object} must be non-negative" if object.negative?
|
177
|
+
object.to_i
|
178
|
+
elsif object.is_a?(Time)
|
179
|
+
raise ArgumentError, "#{object.iso8601} must be after #{EPOCH.iso8601}" unless EPOCH <= object
|
180
|
+
object.to_i
|
181
|
+
elsif object.respond_to?(:to_time)
|
182
|
+
maybe_coerce_time object.to_time
|
183
|
+
else
|
184
|
+
raise(
|
185
|
+
ArgumentError,
|
186
|
+
"#{object.inspect} must be one of Integer, Float, Time, or respond to `#to_time'"
|
187
|
+
)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
require_relative "core_ext/kernel"
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lexicoid
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Herold
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-03-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2'
|
27
|
+
description: Short and stable IDs from timestamps
|
28
|
+
email:
|
29
|
+
- opensource@michaeljherold.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- CHANGELOG.md
|
35
|
+
- CONTRIBUTING.md
|
36
|
+
- LICENSE.md
|
37
|
+
- README.md
|
38
|
+
- lexicoid.gemspec
|
39
|
+
- lib/core_ext/kernel.rb
|
40
|
+
- lib/lexicoid.rb
|
41
|
+
- lib/lexicoid/version.rb
|
42
|
+
homepage: https://github.com/michaelherold/lexicoid-ruby
|
43
|
+
licenses:
|
44
|
+
- MIT
|
45
|
+
metadata:
|
46
|
+
bug_tracker_uri: https://github.com/michaelherold/lexicoid-ruby/issues
|
47
|
+
changelog_uri: https://github.com/michaelherold/lexicoid-ruby/blob/main/CHANGELOG.md
|
48
|
+
documentation_uri: https://rubydoc.info/gems/lexicoid-ruby/0.1.0
|
49
|
+
homepage_uri: https://github.com/michaelherold/lexicoid-ruby
|
50
|
+
rubygems_mfa_required: 'true'
|
51
|
+
source_code_uri: https://github.com/michaelherold/lexicoid-ruby
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options: []
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 3.0.0
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubygems_version: 3.3.7
|
68
|
+
signing_key:
|
69
|
+
specification_version: 4
|
70
|
+
summary: Short and stable IDs from timestamps
|
71
|
+
test_files: []
|