Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Omakase Ruby styling for Rails
inherit_gem:
rubocop-rails-omakase: rubocop.yml

Style/LeadingCommentSpace:
AllowGemfileRubyComment: true
AllowRBSInlineAnnotation: true
Exclude:
- '**/*.rbs'
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ gem "rake", "~> 13.0"
gem "kramdown", "~> 2.5"
gem "minitest", "~> 5.16"

gem "rbs-inline", require: false
gem "sorbet", require: false
gem "sorbet-runtime"
gem "rubocop-rails-omakase"
6 changes: 6 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ GEM
rb-fsevent (0.11.2)
rb-inotify (0.11.1)
ffi (~> 1.0)
rbs (3.9.5)
logger
rbs-inline (0.12.0)
prism (>= 0.29)
rbs (>= 3.8.0)
rdoc (6.15.1)
erb
psych (>= 4.0.0)
Expand Down Expand Up @@ -157,6 +162,7 @@ DEPENDENCIES
kramdown (~> 2.5)
minitest (~> 5.16)
rake (~> 13.0)
rbs-inline
rubocop-rails-omakase

BUNDLED WITH
Expand Down
36 changes: 36 additions & 0 deletions TYPE_CHECKING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Type Checking with RBS Inline and Sorbet

This project uses a combination of RBS inline annotations and Sorbet for type checking.

## RBS Inline

Type annotations in the `lib/` directory use RBS inline format with `#:` comments.

To generate RBS files from inline annotations:
```bash
bundle exec rbs-inline lib --output sig/generated
```

## Sorbet

The Sorbet gem is available for additional type checking capabilities.

### Setup

Initialize Sorbet (if not already done):
```bash
bundle exec srb init
```

### Type Checking

Run Sorbet type checker:
```bash
bundle exec srb tc
```

## Workflow

1. Write code with RBS inline annotations in `lib/` files
2. Generate RBS files: `bundle exec rbs-inline lib --output sig/generated`
3. Run type checking as needed with Sorbet
3 changes: 3 additions & 0 deletions lib/hotpages.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,17 @@ def teardown
# To add/remove extensions, modify this array before calling Extension.setup
# Extensions order is important, because initialization is performed in the order defined
# and this affects prepended/included modules' order.
#: () -> Array[untyped]
def extensions = @extensions ||= DEFAULT_EXTENSIONS
def extensions=(extensions)
@extensions = extensions
end

#: () -> Hotpages::Config
def config = @config ||= Config.defaults

attr_accessor :site_class
#: () -> TestSite
def site = @site ||= site_class.instance.tap(&:setup)

def site_generator = @site_generator ||= SiteGenerator.new(site:)
Expand Down
4 changes: 4 additions & 0 deletions lib/hotpages/config.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
class Hotpages::Config
class << self
#: () -> Hotpages::Config
def defaults
new(
assets: new(
Expand Down Expand Up @@ -30,8 +31,10 @@ def defaults
end
end

#: (?Hash[untyped, untyped]) -> void
def initialize(defaults = {}) = add(**defaults)

#: (**String | (String | Hotpages::Config)? | Integer | String | Hotpages::Config | Hash[untyped, untyped] | Array[untyped] | Array[untyped] | String | Symbol | Hotpages::Config | String | Hash[untyped, untyped] | Hotpages::Config) -> Hotpages::Config
def add(**configs)
configs.each do |key, value|
define_attribute(key, value)
Expand All @@ -58,6 +61,7 @@ def to_h

private

#: (Symbol, (String | Hotpages::Config | Integer | Hash[untyped, untyped] | Array[untyped] | Symbol)?) -> Symbol
def define_attribute(name, value)
# Do not re-define
return if respond_to?(name)
Expand Down
7 changes: 7 additions & 0 deletions lib/hotpages/extension.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
module Hotpages::Extension
class << self
#: (extensions: Array[untyped], config: Hotpages::Config) -> Array[untyped]
def setup(extensions:, config:)
extensions.each { it.setup(config) }
end
end

#: (extensions: Array[untyped], config: Hotpages::Config) -> Array[untyped]
def setup(config, extension = self)
@setup_block.call(Setup.new(config, extension))
end
Expand All @@ -16,27 +18,32 @@ def extension(&setup_block)
end

class Setup
#: (Hotpages::Config, Module) -> void
def initialize(config, extension)
@config = config
@extension = extension
end

#: (?Module, to: Class | Module) -> Class?
def prepend(mod = extension, to:)
to.prepend(mod)
if mod.const_defined?(:ClassMethods)
to.singleton_class.prepend(mod::ClassMethods)
end
end

#: (?Module, to: Class) -> nil
def include(mod = extension, to:)
to.include(mod)
if mod.const_defined?(:ClassMethods)
to.extend(mod::ClassMethods)
end
end

#: (Module) -> nil
def add_helper(helper_mod) = include(helper_mod, to: Hotpages::Page)

#: () -> (Hotpages::Config | Hash[untyped, untyped])
def configure = yield config

private
Expand Down
3 changes: 3 additions & 0 deletions lib/hotpages/extensions/i18n.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ module Site
GETTEXT_DOMAIN = "hotpages_site"
Gettext = FastGettext

#: () -> String
def setup
super
Gettext.add_text_domain(
Expand All @@ -45,6 +46,7 @@ def reload

def locales = i18n_config.locales
def default_locale = i18n_config.default_locale
#: () -> Pathname
def locales_path = root.join(i18n_config.locales_directory)
def default_locale?(locale) = default_locale.to_s == locale.to_s
def locales_without_default = locales.reject { default_locale?(it) }
Expand All @@ -60,6 +62,7 @@ def with_locale(locale, &block)

private

#: () -> Hotpages::Config
def i18n_config = config.site.i18n
end

Expand Down
16 changes: 14 additions & 2 deletions lib/hotpages/page.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@ class Hotpages::Page < Hotpages::PagePathComponent
include Hotpages::Helpers

class << self
#: (Class) -> void
def inherited(subclass)
super
subclass.layout_path = self.layout_path.dup if self.layout_path
end

#: (String) -> String
def layout(layout_path)
@layout_path = layout_path
end
attr_accessor :layout_path
attr_accessor :layout_path #: String

#: () -> void
def include_all_site_helpers
site.helper_constants.each do |helper_module|
next if included_modules.include?(helper_module)
Expand All @@ -29,7 +32,13 @@ def include_all_site_helpers

layout :site # Default layout path, can be overridden by individual pages

attr_reader :base_path, :segments, :name, :site, :config, :template_file_ext, :layout_path
attr_reader :base_path, #: String
:segments, #: Hash[Symbol, String]
:name, #: String?
:site, #: Hotpages::Site
:config, #: Hash[Symbol, untyped]
:template_file_ext, #: String?
:layout_path #: String?

def initialize(base_path:, segments: {}, name: nil, template_file_ext: nil, layout: nil)
@base_path = base_path
Expand All @@ -44,13 +53,16 @@ def initialize(base_path:, segments: {}, name: nil, template_file_ext: nil, layo
self.class.include_all_site_helpers
end

#: (String) -> void
def layout(layout_path)
@layout_path = layout_path
end

#: () -> String
def body
raise "No template file is found at #{self.class.name} at `/#{site.directory.pages}/#{[ base_path, template_file_ext ].compact.join(".")}`, "\
"please provide body method or template file."
end
#: () -> String
def body_type = "html.erb"
end
1 change: 1 addition & 0 deletions lib/hotpages/page/expandable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module Hotpages::Page::Expandable
EXPANDABLE_PATH_COMPONENT_REGEXP = Hotpages::Page::EXPANDABLE_NAME_REGEXP

class << self
#: (Class) -> Class
def included(base)
base.extend(ClassMethods)
end
Expand Down
11 changes: 11 additions & 0 deletions lib/hotpages/site.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class Hotpages::Site
using Hotpages::Support::StringInflections

class << self
#: (Class) -> Pathname
def inherited(subclass)
Hotpages::Extension.setup(
extensions: Hotpages.extensions,
Expand All @@ -23,12 +24,14 @@ def inherited(subclass)
config.site.root = Pathname.new(base_class_location).join("../site")
end

#: () -> Hotpages::Config
def config = @config ||= Hotpages.config
end

attr_reader :config
define_hook :initialize, only: :after

#: () -> void
def initialize
run_hooks :initialize do
@config = self.class.config
Expand All @@ -42,6 +45,7 @@ def initialize
end
end

#: () -> bool
def setup
loader.setup
end
Expand All @@ -65,6 +69,7 @@ def page_base_class(class_name: config.site.page_base_class_name)
class_name.constantize
end

#: () -> Array[untyped]
def assets_paths = @assets_paths ||= [ assets_path ]
def assets(filter_ext = nil)
Enumerator.new do |yielder|
Expand All @@ -89,17 +94,23 @@ module Paths

delegate %i[ root directory ] => :site_config

#: () -> Pathname
def dist_path = root.join(site_config.dist_path)

def pages_path = root.join(directory.pages)
#: () -> Pathname
def models_path = root.join(directory.models)
def layouts_path = root.join(directory.layouts)
#: () -> Pathname
def helpers_path = root.join(directory.helpers)
#: () -> Pathname
def assets_path = root.join(directory.assets)
#: () -> Pathname
def shared_path = root.join(directory.shared)

private

#: () -> Hotpages::Config
def site_config = config.site
end
include Paths
Expand Down
1 change: 1 addition & 0 deletions lib/hotpages/support/cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def content_of(of_version)
end

class Store < Hash
#: (String, version: Integer) -> String?
def fetch(cache_key, version:, &block)
self[cache_key]&.content_of(version) ||
block &&
Expand Down
6 changes: 6 additions & 0 deletions lib/hotpages/support/hooks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ def in?(*types) = types.include?(type)
private_constant :Type

class << self
#: (Class) -> Class
def included(base)
base.extend(ClassMethods)
end
end

module ClassMethods
#: (Class) -> Hash[untyped, untyped]
def inherited(subclass)
super

Expand All @@ -28,9 +30,11 @@ def inherited(subclass)
end
end

#: () -> Hash[untyped, untyped]
def hooks = @hooks ||= {}
attr_writer :hooks

#: (Symbol, ?only: nil) -> Array[untyped]
def define_hook(hook_name, only: nil)
Type.all.each do |type|
registered_name = type.key(hook_name)
Expand All @@ -50,6 +54,7 @@ def define_hooks(*hook_names, only: nil)
end
end

#: (Symbol) -> Zeitwerk::Loader
def run_hooks(hook_name, &block)
unless self.class.hooks[Type.before.key(hook_name)]
raise "Hooks for `#{hook_name}` is not registered."
Expand Down Expand Up @@ -91,6 +96,7 @@ def run_hooks(hook_name, &block)

private

#: (Proc) -> Proc
def callable_hook_content(hook_content)
case hook_content
when Symbol
Expand Down
Loading