Skip to content

Commit

Permalink
Setup smarter type registration (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
hopsoft authored Nov 14, 2023
1 parent 47eee7e commit c155d47
Show file tree
Hide file tree
Showing 34 changed files with 112 additions and 78 deletions.
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ This is just a fraction of what's possible with Universal ID. It's an invaluable
- [Supported Data Types](#supported-data-types)
- [Scalars](#scalars)
- [Composites](#composites)
- [Contributed Types](#contributed-types)
- [ActiveRecord](#activerecord)
- [Why Universal ID with ActiveRecord?](#why-universal-id-with-activerecord)
- [Custom Datatypes](#custom-datatypes)
Expand Down Expand Up @@ -216,6 +217,37 @@ Composite support is where things start to get interesting. All of the composite
```
</details>

### Contributed Types

Universal ID is designed to be highly extensible, allowing for third-party contributions to enhance its capabilities.
These contributions can introduce support for additional data types, further broadening the scope of Universal ID’s utility.
The following are some notable contrib extensions:

- **ActiveRecord::Base**: Integrates Universal ID with ActiveRecord base models, enabling intelligent serialization of database records
- **ActiveRecord::Relation**: Supports the serialization of ActiveRecord relations, making it possible to encode complex query structures
- **ActiveSupport::TimeWithZone**: Adds the ability to serialize ActiveSupport's TimeWithZone objects
- **GlobalID**: Extends support to include GlobalIDs
- **SignedGlobalID**: Extends support to include SignedGlobalIDs

#### Requiring Contributed Types

To utilize the contributed types, you must explicitly require them in your application.
This ensures the extensions are loaded and available for use.
Here is an example illustrating how to include contributed types:

```ruby
# load contrib types
require "universal_id/contrib/active_record"
require "universal_id/contrib/active_support"
require "universal_id/contrib/global_id"
require "universal_id/contrib/signed_global_id"

# or simply
require "universal_id/contrib/rails"
```

> :bulb: **Implicit Contribs**: Whenever the `Rails` constant is defined, the related contribs are auto-loaded.
### ActiveRecord

> :information_source: **Broad Compatibility**: Universal ID has built-in support for ActiveRecord, yet it maintains independence from Rails-specific dependencies. This versatile design enables integration into **any Ruby project**.
Expand Down
5 changes: 4 additions & 1 deletion bin/bench
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ require "benchmark"
require "awesome_print"

require_relative "../test/rails_kit/setup"
require_relative "../lib/universalid"
require_relative "../lib/universal_id"

# load contribs
require_relative "../lib/universal_id/contrib/rails"

@count = 5_000
@pad = 98
Expand Down
6 changes: 6 additions & 0 deletions bin/console
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,10 @@ require "pry-doc"
require_relative "../test/rails_kit/setup"
require "universal_id"

# load contribs
require "universal_id/contrib/active_record"
require "universal_id/contrib/active_support"
require "universal_id/contrib/global_id"
require "universal_id/contrib/signed_global_id"

Pry.start
2 changes: 1 addition & 1 deletion bin/loc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/bin/bash

cloc contrib lib
cloc lib
8 changes: 0 additions & 8 deletions contrib/active_record.rb

This file was deleted.

7 changes: 0 additions & 7 deletions contrib/active_support.rb

This file was deleted.

7 changes: 0 additions & 7 deletions contrib/global_id.rb

This file was deleted.

7 changes: 0 additions & 7 deletions contrib/signed_global_id.rb

This file was deleted.

2 changes: 2 additions & 0 deletions lib/universal_id.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
UniversalID::Settings.instance # initialize settings

module UniversalID
module Contrib; end

class << self
attr_writer :logger

Expand Down
14 changes: 0 additions & 14 deletions lib/universal_id/contrib.rb

This file was deleted.

4 changes: 4 additions & 0 deletions lib/universal_id/contrib/active_record.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# frozen_string_literal: true

require_relative "active_record/base_message_pack_type"
require_relative "active_record/relation_message_pack_type"
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions lib/universal_id/contrib/active_support.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# frozen_string_literal: true

require_relative "active_support/time_with_zone_message_pack_type"
3 changes: 3 additions & 0 deletions lib/universal_id/contrib/global_id.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# frozen_string_literal: true

require_relative "global_id/message_pack_type"
File renamed without changes.
File renamed without changes.
6 changes: 6 additions & 0 deletions lib/universal_id/contrib/rails.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true

require_relative "active_record" if defined? ActiveRecord
require_relative "active_support" if defined? ActiveSupport
require_relative "global_id" if defined? GlobalID
require_relative "signed_global_id" if defined? SignedGlobalID
3 changes: 3 additions & 0 deletions lib/universal_id/contrib/signed_global_id.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# frozen_string_literal: true

require_relative "signed_global_id/message_pack_type"
26 changes: 20 additions & 6 deletions lib/universal_id/message_pack_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,30 @@ def create_msgpack_pool
@msgpack_pool = UniversalID::MessagePackFactory.pool([Etc.nprocessors.to_i, 1].max)
end

def register(type:, type_id: nil, recreate_pool: true, **options)
id = type_id || next_type_id
def register_scalar(type:, recreate_pool: true, **options)
register id: next_type_id(order: :asc), type: type, **options
end

def register(type:, id: nil, recreate_pool: true, **options)
options[:recursive] = true unless options.key?(:recursive)
register_type(id, type, options)
register_type(id || next_type_id(order: :desc), type, options)
create_msgpack_pool if recreate_pool
end

def next_type_id
max_type_id = registered_types.map { |type| type[:type].to_i }.max
max_type_id.nil? ? 0 : max_type_id + 1
def next_type_id(order:)
range = 0..127

case order
when :asc
id = range.first
id += 1 while type_registered?(id)
when :desc
id = range.last
id -= 1 while type_registered?(id)
end

id = nil unless range.cover?(id)
id
end
end
end
Expand Down
36 changes: 17 additions & 19 deletions lib/universal_id/message_pack_types.rb
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
# frozen_string_literal: true

# A list of all MessagePack types in the preferred registration order
#
# IMPORTANT: More specific types should be registered before more general types
# because MessagePack will use the first registered type that matches
# MessagePack scans registered type in linear order and first match wins
# NOTE: MessagePack scans registered type in linear order and first match wins

require_relative "contrib"
# scalars
require_relative "message_pack_types/ruby/scalars/complex"
require_relative "message_pack_types/ruby/scalars/rational"
require_relative "message_pack_types/ruby/scalars/date_time"
require_relative "message_pack_types/ruby/scalars/date"
require_relative "message_pack_types/ruby/scalars/range"
require_relative "message_pack_types/ruby/scalars/regexp"

paths = %w[
message_pack_types/uri/uid/type.rb
message_pack_types/ruby/composites/set.rb
message_pack_types/ruby/composites/open_struct.rb
message_pack_types/ruby/composites/struct.rb
message_pack_types/ruby/scalars/complex.rb
message_pack_types/ruby/scalars/rational.rb
message_pack_types/ruby/scalars/date_time.rb
message_pack_types/ruby/scalars/date.rb
message_pack_types/ruby/scalars/range.rb
message_pack_types/ruby/scalars/regexp.rb
]
# composites
require_relative "message_pack_types/ruby/composites/open_struct"
require_relative "message_pack_types/ruby/composites/struct"
require_relative "message_pack_types/ruby/composites/set"

paths.each { |path| require_relative path }
# uid
require_relative "message_pack_types/uri/uid/type"

# contribs
require_relative "contrib/rails" if defined? Rails

UniversalID::MessagePackFactory.create_msgpack_pool
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

UniversalID::MessagePackFactory.register(
UniversalID::MessagePackFactory.register_scalar(
type: Complex,
recreate_pool: false,
packer: ->(obj, packer) { packer.write obj.to_s },
Expand Down
2 changes: 1 addition & 1 deletion lib/universal_id/message_pack_types/ruby/scalars/date.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

UniversalID::MessagePackFactory.register(
UniversalID::MessagePackFactory.register_scalar(
type: Date,
recreate_pool: false,
packer: ->(obj, packer) { packer.write obj.iso8601 },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

UniversalID::MessagePackFactory.register(
UniversalID::MessagePackFactory.register_scalar(
type: DateTime,
recreate_pool: false,
packer: ->(obj, packer) { packer.write obj.iso8601(9) },
Expand Down
2 changes: 1 addition & 1 deletion lib/universal_id/message_pack_types/ruby/scalars/range.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

UniversalID::MessagePackFactory.register(
UniversalID::MessagePackFactory.register_scalar(
type: Range,
recreate_pool: false,
packer: ->(obj, packer) do
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

UniversalID::MessagePackFactory.register(
UniversalID::MessagePackFactory.register_scalar(
type: Rational,
recreate_pool: false,
packer: ->(obj, packer) { packer.write obj.to_s },
Expand Down
2 changes: 1 addition & 1 deletion lib/universal_id/message_pack_types/ruby/scalars/regexp.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

UniversalID::MessagePackFactory.register(
UniversalID::MessagePackFactory.register_scalar(
type: Regexp,
recreate_pool: false,
packer: ->(obj, packer) do
Expand Down
5 changes: 4 additions & 1 deletion test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,7 @@
end

# Load UniversalID
require_relative "../lib/universal_id"
require "universal_id"

# Load contribs
require "universal_id/contrib/rails"
2 changes: 1 addition & 1 deletion universalid.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Gem::Specification.new do |s|
s.metadata["changelog_uri"] = s.homepage + "/blob/main/CHANGELOG.md"

s.files = Dir.chdir(File.expand_path(__dir__)) do
Dir["{config,contrib,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]
Dir["{config,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]
end

s.require_paths = ["lib"]
Expand Down

0 comments on commit c155d47

Please sign in to comment.