active_support_cr
Version, currently 0.1.812 versions
- 0.1.10latestOct 22, 2026
- 0.1.9not indexedOct 22, 2026
- 0.1.8not indexedOct 22, 2026
- 0.1.7not indexedOct 22, 2026
- 0.1.6not indexedOct 22, 2026
- 0.1.5not indexedOct 22, 2026
- 0.1.4Oct 17, 2026
- 0.1.3not indexedOct 17, 2026
- 0.1.2not indexedOct 17, 2026
- 0.1.1not indexedOct 17, 2026
- 0.1.0not indexedOct 17, 2026
- master branchOct 12, 2026
github.com/OrelSokolov/active_support.cr
No description declared in shard.yml.
Nothing has been indexed for 0.1.8 yet. The tag is recorded, its shard.yml has not been read, so the manifest and dependency list below are empty because they are unknown rather than because they are absent.
Installation
# Add this to your shard.yml
dependencies:
active_support_cr:
github: OrelSokolov/active_support.cr
version: ~> 0.1.8Then run:
shards installshard.yml
No shard.yml has been indexed for 0.1.8. You can read it on the repository.
Dependencies
Unknown: the shard.yml for this version has not been read yet.
README
This README is the one indexed from the repository at its latest ref, not from the tag for this version.
active_support.cr
A Crystal port of Rails ActiveSupport 8.1.4 — core extensions
(blank?, Duration, date math, Inflector, deep hash ops), Concern,
delegate, Notifications, cache stores, I18n, number helpers, message
verification/encryption and time zones. Namespace ActiveSupport
mirrors the original 1:1; the goal is that Ruby snippets from the Rails
docs work unchanged. Estimated parity ~85%. Core extensions are
implemented by reopening Crystal stdlib types (adding methods is allowed;
overriding existing ones is not) and by macros in place of
define_method.
⚠️ EXPERIMENTAL FRAMEWORK, DO NOT USE IN PRODUCTION YET.
Installation
dependencies:
active_support_cr:
github: OrelSokolov/active_support.cr
shards install
Requires Crystal >= 1.21.0. pg and redis are needed only for the
optional adapters/* cache stores.
Usage
require "active_support_cr"
2.days.ago
%w[a b c].to_sentence # => "a, b, and c"
"post".pluralize # => "posts"
hash.deep_merge(other)
"42".present? # => true
Time.use_zone("Europe/Berlin") { Time.current }
Ruby-compat aliases
Ruby's attr_* macros live in the zero-dependency
attr_reader.cr shard and are
re-exported here (delegating to the idiomatic Crystal
getter/setter/property):
class Entry
attr_reader :body, :content_type # Ruby symbol style
attr_accessor hits : Int32 = 0 # typed declarations work too
end
Feature comparison vs activesupport 8.1.4
Core extensions
| Feature | Original (activesupport) | Port (active_support.cr) | Status |
|---|---|---|---|
Object: blank?, present?, presence, in?, presence_in, deep_dup, to_param | ✅ | Same | ✅ |
NilClass: blank?, present?, try | ✅ | Same | ✅ |
String: squish, truncate, truncate_bytes, at/from/to, first/last, starts_with?/ends_with?, exclude?, remove, indent, strip_heredoc | ✅ | Same | ✅ |
Inflector: pluralize, camelize, underscore, tableize, classify, humanize, titleize, parameterize, demodulize, foreign_key, ordinalize | ✅ | Same | ✅ |
Hash: symbolize_keys, deep_symbolize_keys, stringify_keys, except, slice, reverse_merge, with_defaults, deep_merge, deep_transform_keys/values, compact_blank, assert_valid_keys | ✅ | Same | ✅ |
Array: in_groups_of, in_groups, split, extract!, to_sentence, second..fifth, second_to_last, wrap, excluding, including | ✅ | Same | ✅ |
Enumerable: index_by, index_with, many?, exclude?, pluck, pick, sole, in_order_of, compact_blank | ✅ | Same | ✅ |
Int/Numeric: seconds..years, bytes..exabytes, multiple_of?, in_milliseconds (Duration mechanics) | ✅ | Duration is a fixed Time::Span (see deviations) | 🔶 |
Date/Time math: beginning/end_of_*, all_day/..., ago/since, advance, change, yesterday/tomorrow, next_week/prev_*, months_ago/..., next_occurring, past?/future?/today?, to_fs(:db/:short/:long/:iso8601) | ✅ | Same | ✅ |
constantize / String#constantize | ✅ | Not portable — dependent shards use compile-time registries | ❌ (equivalent: compile-time class registry) |
Time zones
| Feature | Original | Port | Status |
|---|---|---|---|
Time.zone, TimeWithZone, in_time_zone, utc/localtime, formatted_offset | ✅ | TimeZone built on Time::Location; TWZ arithmetic, <=>/== by UTC instant, zoned strftime, to_fs | ✅ |
Per-user timezone: Time.use_zone(zone, &) | ✅ | Fiber-local override registry + ensure-restore | ✅ |
Time.current, TimeZone#now, I18n.l with TWZ | ✅ | Same | ✅ |
| DST-aware calendar Duration arithmetic across TWZ | ✅ | Duration is a fixed span — twz + 1.day always shifts 24 h | ❌ (equivalent: shift via Time#shift calendar math) |
Infrastructure
| Feature | Original | Port | Status |
|---|---|---|---|
ActiveSupport::Concern (included do / class_methods do) | ✅ | Crystal included hook + macros | ✅ |
attr_reader / attr_writer / attr_accessor | ✅ | Re-exported from the attr_reader.cr shard; Ruby symbol args (:foo) and typed declarations accepted | ✅ |
delegate :x, to: :y, prefix:, allow_nil: / delegate_missing_to | ✅ | Macros | ✅ |
mattr_accessor/cattr_accessor, class_attribute, thread_mattr_* | ✅ | Macros over @@/fiber-locals | ✅ |
ActiveSupport::Notifications (instrument/subscribe/publish) | ✅ | Same | ✅ |
ActiveSupport::Cache: MemoryStore, NullStore, FileStore; fetch/read/write/exist?/delete, expires_in:, race_condition_ttl: | ✅ | Same Store API | ✅ |
RedisCacheStore | ✅ | adapters/redis_cache.cr (SET/GET EX, INCRBY counters, namespace); stores raw strings — INCR on a write-stored integer succeeds (Rails wraps values in a coder and returns nil) | 🔶 |
SolidCache (PG table) | separate gem | adapters/pg_cache.cr: solid_cache_entries(key, value, expires_at) + atomic SQL counters + delete_expired | 🔶 |
I18n: t(key, scope:, default:, count:), yml locales, pluralization, l, available_locales, fallbacks | ✅ | Same | ✅ |
NumberHelper family | ✅ | Same (locale data) | ✅ |
MessageVerifier / MessageEncryptor (signed/encrypted cookies) | ✅ | SHA1/SHA256 digests, KeyGenerator-derived keys, legacy {"_rails":...} envelope read/write (wire format of actionpack 8.1 cookies) | ✅ |
SecureRandom.base58/base36, Digest::UUID | ✅ | Same | ✅ |
ActiveSupport::CurrentAttributes | ✅ | class Current < AS::CurrentAttributes, typed attribute user: User? (nilable), class-level accessors, reset/reset_all; reset wired into request dispatch by railties.cr | 🔶 (process-global class vars, not fiber-isolated; no instance/set{}/callbacks API) |
TaggedLogging, Deprecation, BacktraceCleaner | ✅ | Same | ✅ |
HashWithIndifferentAccess | ✅ | Approximation used by params: Symbol|String keys | 🔶 |
Deviations & Crystal-specific notes
- Core extensions reopen stdlib types (
struct Int,class String); overriding existing stdlib methods is forbidden, so those keep Crystal semantics. Durationis a fixedTime::Span: calendarmonths/yearshave variable length, and TWZ arithmetic does not cross DST boundaries the way Rails durations do.- The fiber-local zone registry is not cleaned up if a fiber dies inside
a
use_zoneblock;Time.zonewithout a fiber override is process-global (like Rails'Time.zone=). - FileStore writes timestamps with sub-second precision (expiry < 1 s).
- Cache stores keep raw string values (see RedisCacheStore row).
Testing
crystal spec
Suite: 323/323. Oracle specs (oracle/cases.rb → expected.yml,
including 19 tz_* timezone cases) are recorded with the real
activesupport 8.1.4 and replayed unchanged; cache stores are additionally
verified against a real redis-server (oracle/cache_stores_cases.rb)
and a shared contract spec runs across all four stores.
Documentation
Built from the current release. The first visit to a release nobody has asked for starts its build.
Links
This release
- Version
0.1.8- Tagged
- Oct 22, 2026
- Commit
2eae335b23d0- Indexed
- not yet
Dependents
- action_pack_cr
- turbo_cr
- action_view_cr
- once_campfire_crails
- active_storage_cr
- railties_cr
- active_record_cr
- importmap_cr
- action_cable_cr
- active_job_cr
- rails_settings_cached_cr
- active_model_cr
and 7 more
Repository
github.com/OrelSokolov/active_support.cr
Metadata
- Created
- Oct 13, 2026
- Updated
- Oct 23, 2026
- Synced
- Oct 23, 2026
- Versions
- 12