RubyRangeable
Reference Ruby implementation of Rangeable<Element> — a generic, integer-coordinate, closed-interval set container with first-insert ordered active queries.
Installation
Add to your Gemfile:
gem 'rangeable', '~> 1.0'
Or install directly:
$ gem install rangeable
Usage
require 'rangeable'
# Any Ruby object with sensible == / hash works as an element.
Strong = Struct.new(:tag)
Italic = Struct.new(:tag)
r = Rangeable.new
r.insert(Strong.new(:bold), start: 2, end: 5)
r.insert(Strong.new(:bold), start: 3, end: 7) # merges with [2, 5] → [2, 7]
r.insert(Strong.new(:bold), start: 9, end: 11) # disjoint
r.insert(Italic.new(:em), start: 3, end: 8)
r.get_range(Strong.new(:bold)) # => [[2, 7], [9, 11]]
r.get_range(Italic.new(:em)) # => [[3, 8]]
r[4].objs # => [Strong<:bold>, Italic<:em>] first-insert order
r[8].objs # => [Italic<:em>]
r[10].objs # => [Strong<:bold>]
Refinement sugar
Avoid polluting the global namespace:
using Rangeable::Refinements
Strong.new(:bold).get_range(from: r) # => [[2, 7], [9, 11]]
Sweep iteration via transitions
r.transitions(over: 0..15).each do |event|
case event.kind
when :open then puts "#{event.coordinate}: open #{event.element}"
when :close then puts "#{event.coordinate}: close #{event.element}"
end
end
API
| Method | Returns | Notes |
|---|---|---|
Rangeable.new |
empty Rangeable |
|
r.insert(element, start:, end:) |
:mutated / :idempotent |
raises Rangeable::InvalidIntervalError if start > end |
r[i] |
Rangeable::Slot |
wrapper exposing .objs |
r[i].objs |
frozen Array<Element> |
first-insert order |
r.get_range(element) |
Array<[lo, hi]> |
merged disjoint ranges |
r.transitions(over: a..b) |
Array<Event> |
each Event has coordinate, kind, element |
r.count |
Integer |
distinct elements |
r.empty? |
Boolean |
|
| `r.each { \ | element, ranges\ | ... }` |
r.copy |
Rangeable |
deep copy |
Semantics
- End is inclusive:
[a, b]coversa..b, both ends. - Same-element merging: equal elements (by
==/hash) merge on overlap or integer adjacency.[2, 4] ∪ [5, 7] = [2, 7]. - Idempotent insert: re-inserting a contained interval costs no version bump.
- Out-of-order rejected:
insert(_, start: 5, end: 2)raises. - Active-set ordering: deterministic across runs — first-insert order of the element, not hash bucket order.
- Element immutability: elements are frozen on insert (
element.dup.freeze); mutating an element after insert is undefined behaviour.
See RangeableRFC § 4 for normative semantics, § 6 for algorithms, § 7 for the Φ-potential amortised-complexity proof, and § 10 for the 23-case normative test contract this gem must pass.
Performance
| Workload | Result |
|---|---|
Brute-force baseline (m=50, L=2000) vs Rangeable |
~5.5× speedup in micro-benchmark |
| Real consumer (ZMediumToMarkdown) | 2.23× end-to-end speedup, 55% render-time reduction; all 306 existing tests byte-identical |
See also
- RangeableRFC — the normative specification this gem implements.
- SwiftRangeable — sibling reference implementation in Swift; produces byte-identical outputs against a shared cross-language fixture.
- ZMediumToMarkdown — real-world consumer that exercises this gem on Medium GraphQL renderers.
Development
$ bundle install
$ bundle exec rake test
The test suite covers the full RFC § 10 contract (23 normative tests), a 1000-iteration property test against a brute-force oracle, and a markdown-shaped micro-benchmark.
License
MIT © ZhgChgLi