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
5 changes: 5 additions & 0 deletions Gemfile.devtools
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

gem "rake", ">= 12.3.3"

group :sig do
gem "rbs"
gem "rbs-inline"
end

group :test do
gem "simplecov", require: false, platforms: :ruby
gem "simplecov-cobertura", require: false, platforms: :ruby
Expand Down
42 changes: 42 additions & 0 deletions bin/gen-sig
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require "fileutils"
require "pathname"

root_path = Pathname.new(File.expand_path("../", __dir__))

manifest_path = root_path.join("sig/manifest.yaml")
manifest = manifest_path.exist? ? manifest_path.read : "dependencies: []"

FileUtils.rm_rf(root_path.join("sig"))

system "rbs-inline lib --opt-out --output=./sig"

EMPTY_COMMENT_BEFORE_CODE = /
^[ \t]*\#[ \t]*\n
(?=^[ \t]*[^#\s])
/mx
EMPTY_STRING = ""
GENERATED_LINE = /^ *# Generated from .+.rb with RBS::Inline *$/
RBS_COMMENT_BLOCK = /
^[ \t]*\#[ \t]*@rbs.*\n
(?:^[ \t]*\#.*\n)*
/x

root_path.glob("sig/**/*.rbs").each do |file|
contents = file.read

contents.gsub!(GENERATED_LINE, EMPTY_STRING)
&.gsub!(RBS_COMMENT_BLOCK, EMPTY_STRING)
&.gsub!(EMPTY_COMMENT_BEFORE_CODE, EMPTY_STRING)
&.strip

if contents.nil? || contents.strip.empty?
File.delete(file)
else
File.write(file, "#{contents}\n")
end
end

File.write(manifest_path, manifest)
3 changes: 3 additions & 0 deletions lib/dry/monads.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module Dry
# @api public
module Monads
# @api private
# @rbs () -> Zeitwerk::Loader
def self.loader
@loader ||= ::Zeitwerk::Loader.new.tap do |loader|
root = ::File.expand_path("..", __dir__)
Expand All @@ -31,6 +32,7 @@ def self.loader
end

# @private
# @rbs (::Class | ::Module base) -> void
def self.included(base)
if all_loaded?
base.include(*constructors)
Expand Down Expand Up @@ -68,6 +70,7 @@ def self.included(base)
# @param [Array<Symbol>] monads
# @return [Module]
# @api public
# @rbs (*::Symbol monads) -> void
def self.[](*monads)
monads.sort!
@mixins.fetch_or_store(monads.hash) do
Expand Down
9 changes: 8 additions & 1 deletion lib/dry/monads/do/all.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ module Do
module All
# @private
class MethodTracker < ::Module
attr_reader :wrappers
attr_reader :wrappers #: ::Hash[::Class | ::Module, ::Module]

# @rbs (::Hash[::Class | ::Module, ::Module] wrappers) -> void
def initialize(wrappers)
super()

Expand Down Expand Up @@ -86,11 +87,13 @@ def included(base)
end
end

# @rbs (::Class | ::Module target) -> void
def extend_object(target)
super
target.prepend(wrappers[target])
end

# @rbs (::Class | ::Module target, ::Symbol method) -> void
def wrap_method(target, method)
visibility = Do.method_visibility(target, method)
Do.wrap_method(wrappers[target], method, visibility)
Expand All @@ -99,6 +102,7 @@ def wrap_method(target, method)

class << self
# @api private
# @rbs (::Class | ::Module base) -> void
def included(base)
super

Expand All @@ -110,6 +114,7 @@ def included(base)
end

# @api private
# @rbs (::Class | ::Module klass, ::Module target) -> void
def wrap_defined_methods(klass, target)
klass.public_instance_methods(false).each do |m|
Do.wrap_method(target, m, :public)
Expand All @@ -128,6 +133,7 @@ def wrap_defined_methods(klass, target)
# @api private
module InstanceMixin
# @api private
# @rbs (::Class | ::Module object) -> void
def extended(object)
super

Expand Down Expand Up @@ -156,6 +162,7 @@ def extended(object)

if ::Gem::Version.new(::RUBY_VERSION) >= ::Gem::Version.new("3.4.0")
::Warning.singleton_class.prepend(::Module.new {
# @rbs (::String message, ?category: ::Symbol?, **__todo__) -> void
def warn(message, category: nil, **)
if message.include?("lib/dry/monads/do.rb") &&
message.include?("warning: the block passed to")
Expand Down
46 changes: 46 additions & 0 deletions sig/dry/monads.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@


module Dry
# Common, idiomatic monads for Ruby
#
# @api public
module Monads
# @api private
def self.loader: () -> Zeitwerk::Loader

# @private
def self.included: (::Class | ::Module base) -> void

# Build a module with cherry-picked monads.
# It saves a bit of typing when you add multiple
# monads to one class. Not loaded monads get loaded automatically.
#
# @example
# require 'dry/monads'
#
# class CreateUser
# include Dry::Monads[:result, :do]
#
# def initialize(repo, send_email)
# @repo = repo
# @send_email = send_email
# end
#
# def call(name)
# if @repo.user_exist?(name)
# Failure(:user_exists)
# else
# user = yield @repo.add_user(name)
# yield @send_email.(user)
# Success(user)
# end
# end
# end
#
# @param [Array<Symbol>] monads
# @return [Module]
# @api public
def self.[]: (*::Symbol monads) -> void
end
end

7 changes: 7 additions & 0 deletions sig/dry/monads/all.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@


module Dry
module Monads
end
end

8 changes: 8 additions & 0 deletions sig/dry/monads/constants.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@


module Dry
module Monads
include Core::Constants
end
end

18 changes: 18 additions & 0 deletions sig/dry/monads/conversion_stubs.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@


module Dry
module Monads
module ConversionStubs
def self.[]: (*untyped method_names) -> untyped

module Methods
def self?.to_maybe: () -> untyped

def self?.to_result: () -> untyped

def self?.to_validated: () -> untyped
end
end
end
end

12 changes: 12 additions & 0 deletions sig/dry/monads/curry.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@


module Dry
module Monads
# @private
module Curry
# @private
def self.call: (untyped value) -> untyped
end
end
end

102 changes: 102 additions & 0 deletions sig/dry/monads/do.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@


module Dry
module Monads
# An implementation of do-notation.
#
# @see Do.for
module Do
extend Mixin

VISIBILITY_WORD: untyped

# @api private
class Halt < StandardError
# @api private
attr_reader result: untyped

def initialize: (untyped result) -> untyped
end

# @api private
class MethodTracker < ::Module
# @api private
def initialize: (untyped tracked_methods, untyped base, untyped wrapper) -> untyped
end

# Generates a module that passes a block to methods
# that either unwraps a single-valued monadic value or halts
# the execution.
#
# @example A complete example
#
# class CreateUser
# include Dry::Monads::Result::Mixin
# include Dry::Monads::Try::Mixin
# include Dry::Monads::Do.for(:call)
#
# attr_reader :user_repo
#
# def initialize(:user_repo)
# @user_repo = user_repo
# end
#
# def call(params)
# json = yield parse_json(params)
# hash = yield validate(json)
#
# user_repo.transaction do
# user = yield create_user(hash[:user])
# yield create_profile(user, hash[:profile])
# end
#
# Success(user)
# end
#
# private
#
# def parse_json(params)
# Try(JSON::ParserError) {
# JSON.parse(params)
# }.to_result
# end
#
# def validate(json)
# UserSchema.(json).to_monad
# end
#
# def create_user(user_data)
# Try(Sequel::Error) {
# user_repo.create(user_data)
# }.to_result
# end
#
# def create_profile(user, profile_data)
# Try(Sequel::Error) {
# user_repo.create_profile(user, profile_data)
# }.to_result
# end
# end
#
# @param [Array<Symbol>] methods
# @return [Module]
def self.for: (*untyped methods) -> untyped

# @api private
def self.included: (untyped base) -> untyped

# @api private
def self.wrap_method: (untyped target, untyped method, untyped visibility) -> untyped

# @api private
def self.method_visibility: (untyped mod, untyped method) -> untyped

# @api private
def self.coerce_to_monad: (untyped monads) -> untyped

# @api private
def self.halt: (untyped result) -> untyped
end
end
end

Loading