├── .rspec
├── sorbet
├── config
├── tapioca
│ ├── require.rb
│ └── config.yml
└── rbi
│ ├── known_constants.rbi
│ └── gems
│ ├── pry@0.14.1.rbi
│ ├── coderay@1.1.3.rbi
│ ├── unparser@0.6.5.rbi
│ ├── method_source@1.0.0.rbi
│ ├── rspec@3.11.0.rbi
│ ├── netrc@0.11.0.rbi
│ ├── parallel@1.22.1.rbi
│ ├── yard-sorbet@0.7.0.rbi
│ ├── ast@2.4.2.rbi
│ ├── zeitwerk@2.6.0.rbi
│ └── diff-lcs@1.5.0.rbi
├── lib
├── changeset
│ ├── version.rb
│ ├── persistence_interface.rb
│ ├── errors.rb
│ ├── null_event_catalog.rb
│ ├── event_catalog_interface.rb
│ ├── configuration.rb
│ ├── async_changeset.rb
│ ├── db_operation_collection.rb
│ ├── event.rb
│ └── event_collection.rb
└── changeset.rb
├── .github
└── workflows
│ ├── lint.yml
│ ├── sorbet.yml
│ └── rspec.yml
├── changelog.md
├── changeset.gemspec
├── bin
└── tapioca
├── Gemfile
├── MIT-LICENSE
├── LICENSE
├── .gitignore
├── spec
├── changeset
│ ├── changeset_integration_spec.rb
│ ├── merge_child_spec.rb
│ ├── changeset_spec.rb
│ └── merge_child_async_spec.rb
└── spec_helper.rb
├── doc
└── combo.svg
├── Gemfile.lock
├── rbi
└── changeset.rbi
└── README.md
/.rspec:
--------------------------------------------------------------------------------
1 | --require spec_helper
2 |
--------------------------------------------------------------------------------
/sorbet/config:
--------------------------------------------------------------------------------
1 | --dir
2 | .
3 | --ignore=vendor/
4 |
--------------------------------------------------------------------------------
/lib/changeset/version.rb:
--------------------------------------------------------------------------------
1 | # typed: strict
2 |
3 | class Changeset
4 | VERSION = "0.1.5"
5 | end
6 |
--------------------------------------------------------------------------------
/lib/changeset/persistence_interface.rb:
--------------------------------------------------------------------------------
1 | # typed: strict
2 |
3 | class Changeset
4 | module PersistenceInterface
5 | def call
6 | end
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/sorbet/tapioca/require.rb:
--------------------------------------------------------------------------------
1 | # typed: true
2 | # frozen_string_literal: true
3 |
4 | # Add your extra requires here (`bin/tapioca require` can be used to boostrap this list)
5 |
--------------------------------------------------------------------------------
/sorbet/rbi/known_constants.rbi:
--------------------------------------------------------------------------------
1 | # typed: false
2 | class URI::File; end
3 | class ::Spoom::ExecResult; end
4 | class ::Spoom::Sorbet::Config; end
5 | class ::Spoom::Sorbet::Errors::Error; end
--------------------------------------------------------------------------------
/lib/changeset/errors.rb:
--------------------------------------------------------------------------------
1 | # typed: strict
2 |
3 | class Changeset
4 | module Errors
5 | class BaseError < StandardError; end
6 |
7 | class UnknownEventError < BaseError; end
8 |
9 | class MissingConfigurationError < BaseError; end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/lib/changeset/null_event_catalog.rb:
--------------------------------------------------------------------------------
1 | # typed: strict
2 |
3 | class Changeset
4 | class NullEventCatalog
5 | def dispatch(event)
6 | raise "No events in NullEventCatalog"
7 | end
8 |
9 | def known_event?(event_name)
10 | false
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/sorbet/tapioca/config.yml:
--------------------------------------------------------------------------------
1 | gem:
2 | # Add your `gem` command parameters here:
3 | #
4 | # exclude:
5 | # - gem_name
6 | # doc: true
7 | # workers: 5
8 | dsl:
9 | # Add your `dsl` command parameters here:
10 | #
11 | # exclude:
12 | # - SomeGeneratorName
13 | # workers: 5
14 |
--------------------------------------------------------------------------------
/lib/changeset/event_catalog_interface.rb:
--------------------------------------------------------------------------------
1 | # typed: strict
2 |
3 | class Changeset
4 | module EventCatalogInterface
5 | def dispatch(event)
6 | end
7 |
8 | def known_event?(event_name)
9 | false
10 | end
11 |
12 | def class
13 | super
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/sorbet/rbi/gems/pry@0.14.1.rbi:
--------------------------------------------------------------------------------
1 | # typed: true
2 |
3 | # DO NOT EDIT MANUALLY
4 | # This is an autogenerated file for types exported from the `pry` gem.
5 | # Please instead update this file by running `bin/tapioca gem pry`.
6 |
7 | # THIS IS AN EMPTY RBI FILE.
8 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem
9 |
--------------------------------------------------------------------------------
/sorbet/rbi/gems/coderay@1.1.3.rbi:
--------------------------------------------------------------------------------
1 | # typed: true
2 |
3 | # DO NOT EDIT MANUALLY
4 | # This is an autogenerated file for types exported from the `coderay` gem.
5 | # Please instead update this file by running `bin/tapioca gem coderay`.
6 |
7 | # THIS IS AN EMPTY RBI FILE.
8 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem
9 |
--------------------------------------------------------------------------------
/sorbet/rbi/gems/unparser@0.6.5.rbi:
--------------------------------------------------------------------------------
1 | # typed: true
2 |
3 | # DO NOT EDIT MANUALLY
4 | # This is an autogenerated file for types exported from the `unparser` gem.
5 | # Please instead update this file by running `bin/tapioca gem unparser`.
6 |
7 | # THIS IS AN EMPTY RBI FILE.
8 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem
9 |
--------------------------------------------------------------------------------
/sorbet/rbi/gems/method_source@1.0.0.rbi:
--------------------------------------------------------------------------------
1 | # typed: true
2 |
3 | # DO NOT EDIT MANUALLY
4 | # This is an autogenerated file for types exported from the `method_source` gem.
5 | # Please instead update this file by running `bin/tapioca gem method_source`.
6 |
7 | # THIS IS AN EMPTY RBI FILE.
8 | # see https://github.com/Shopify/tapioca#manually-requiring-parts-of-a-gem
9 |
--------------------------------------------------------------------------------
/lib/changeset/configuration.rb:
--------------------------------------------------------------------------------
1 | # typed: true
2 |
3 | class Changeset
4 | class Configuration
5 | attr_writer :db_transaction_wrapper
6 |
7 | def db_transaction_wrapper
8 | return @db_transaction_wrapper if @db_transaction_wrapper
9 |
10 | raise Changeset::Errors::MissingConfigurationError, "db_transaction_wrapper"
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 |
3 | on:
4 | push:
5 | branches:
6 | - '**'
7 |
8 | jobs:
9 | tests:
10 | name: Sorbet
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v3
14 | - uses: ruby/setup-ruby@v1
15 | with:
16 | ruby-version: 3.1
17 | - run: bundle install --jobs 4 --retry 3
18 | - run: bundle exec standardrb
19 |
--------------------------------------------------------------------------------
/.github/workflows/sorbet.yml:
--------------------------------------------------------------------------------
1 | name: Sorbet
2 |
3 | on:
4 | push:
5 | branches:
6 | - '**'
7 |
8 | jobs:
9 | tests:
10 | name: Sorbet
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v3
14 | - uses: ruby/setup-ruby@v1
15 | with:
16 | ruby-version: 3.1
17 | - run: bundle install --jobs 4 --retry 3
18 | - run: bundle exec srb tc
19 |
--------------------------------------------------------------------------------
/changelog.md:
--------------------------------------------------------------------------------
1 | # 0.1.5
2 | - Make commit_db_operations and dispatch_events public
3 |
4 | # 0.1.4
5 | - Make DbOperationCollection an enumerable
6 |
7 | # 0.1.3
8 | - Add `merge_child_async` for legacy code concerns
9 |
10 | # 0.1.2
11 | - Breaking change: `add_event` signature without keyword args
12 |
13 | # 0.1.1
14 | - Now Db operations have to respond to `call` vs `commit`, opening the doors to simple lambdas.
15 | - Remove constraints on events payload types
16 |
--------------------------------------------------------------------------------
/.github/workflows/rspec.yml:
--------------------------------------------------------------------------------
1 | name: Rspec
2 |
3 | on:
4 | push:
5 | branches:
6 | - '**'
7 |
8 | jobs:
9 | tests:
10 | name: all
11 | runs-on: ubuntu-latest
12 | strategy:
13 | matrix:
14 | ruby-version: ['3.2', '3.1', '3.0']
15 | steps:
16 | - uses: actions/checkout@v3
17 | - uses: ruby/setup-ruby@v1
18 | with:
19 | ruby-version: ${{ matrix.ruby-version }}
20 | - run: bundle install --jobs 4 --retry 3
21 | - run: bundle exec rspec spec
22 |
--------------------------------------------------------------------------------
/changeset.gemspec:
--------------------------------------------------------------------------------
1 | $:.push File.expand_path("lib", __dir__)
2 |
3 | require "changeset/version"
4 |
5 | Gem::Specification.new do |spec|
6 | spec.name = "changeset"
7 | spec.version = Changeset::VERSION
8 | spec.authors = ["Benjamin Roth"]
9 | spec.email = ["benjamin@rubyist.fr"]
10 | spec.summary = "Propagate persistence and events from actions"
11 | spec.description = "Propagate persistence and events from actions"
12 | spec.license = "MIT"
13 |
14 | spec.files = Dir["{lib,rbi}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]
15 |
16 | spec.add_dependency "zeitwerk"
17 | end
18 |
--------------------------------------------------------------------------------
/lib/changeset/async_changeset.rb:
--------------------------------------------------------------------------------
1 | # typed: true
2 |
3 | class Changeset
4 | class AsyncChangeset
5 | class InconsistencyError < StandardError; end
6 |
7 | def initialize(changeset_wrapped_in_proc)
8 | @changeset_wrapped_in_proc = changeset_wrapped_in_proc
9 | @called = false
10 | end
11 |
12 | def db_operations
13 | changeset.send(:db_operations)
14 | end
15 |
16 | def events_collection
17 | changeset.send(:events_collection)
18 | end
19 |
20 | private
21 |
22 | def changeset
23 | @changeset ||= @changeset_wrapped_in_proc.call.tap do |result|
24 | raise InconsistencyError unless result.is_a?(::Changeset)
25 | end
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/bin/tapioca:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'tapioca' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12 |
13 | bundle_binstub = File.expand_path("bundle", __dir__)
14 |
15 | if File.file?(bundle_binstub)
16 | if /This file was generated by Bundler/.match?(File.read(bundle_binstub, 300))
17 | load(bundle_binstub)
18 | else
19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21 | end
22 | end
23 |
24 | require "rubygems"
25 | require "bundler/setup"
26 |
27 | load Gem.bin_path("tapioca", "tapioca")
28 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3 |
4 | # Declare your gem's dependencies in flow.gemspec.
5 | # Bundler will treat runtime dependencies like base dependencies, and
6 | # development dependencies will be added by default to the :development group.
7 | gemspec
8 |
9 | # Declare any dependencies that are still in development here instead of in
10 | # your gemspec. These might include edge Rails or gems from your path or
11 | # Git. Remember to move these dependencies to your gemspec before releasing
12 | # your gem to rubygems.org.
13 |
14 | # To use a debugger
15 | # gem 'byebug', group: [:development, :test]
16 | gem "sorbet-runtime", "0.5.9204"
17 |
18 | group :development, :test do
19 | gem "sorbet", "0.5.9204"
20 | gem "tapioca", require: false
21 | gem "standard"
22 | end
23 |
24 | group :test do
25 | gem "rspec", "3.11.0"
26 | gem "byebug"
27 | end
28 |
--------------------------------------------------------------------------------
/lib/changeset/db_operation_collection.rb:
--------------------------------------------------------------------------------
1 | # typed: true
2 |
3 | class Changeset
4 | class DbOperationCollection
5 | include Enumerable
6 |
7 | def initialize
8 | @collection = []
9 | end
10 |
11 | def add(persistence_handler)
12 | collection.push(persistence_handler)
13 | end
14 |
15 | def merge_child(db_operations)
16 | db_operations.collection.each do |db_operation|
17 | add(db_operation)
18 | end
19 | end
20 |
21 | def merge_child_async(async_change_set)
22 | add(async_change_set)
23 | self
24 | end
25 |
26 | def each(&block)
27 | collection.each do |element|
28 | case element
29 | when Changeset::AsyncChangeset
30 | element.db_operations.each do |operation|
31 | yield(operation)
32 | end
33 | else
34 | yield(element)
35 | end
36 | end
37 | end
38 |
39 | def ==(other)
40 | collection == other.collection
41 | end
42 |
43 | protected
44 |
45 | attr_reader :collection
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/MIT-LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2022 Benjamin Roth
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Benjamin Roth
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/lib/changeset/event.rb:
--------------------------------------------------------------------------------
1 | # typed: true
2 |
3 | class Changeset
4 | class Event
5 | attr_reader :name
6 |
7 | # - if at the time the event is added, we know all params
8 | # raw_payload would be a Hash
9 | # - else, it means we need to wait for db operations to be committed
10 | # in this case, raw_payload would be a Proc
11 | # example: we need db id of some model which is not created yet
12 | def initialize(name:, raw_payload:, events_catalog:)
13 | raise Changeset::Errors::UnknownEventError.new("unknown #{name}") unless events_catalog.known_event?(name)
14 |
15 | @name = name
16 | @events_catalog = events_catalog
17 | @raw_payload = raw_payload
18 | end
19 |
20 | def dispatch
21 | events_catalog.dispatch(self)
22 | end
23 |
24 | def unicity_key
25 | [events_catalog.class, name, payload]
26 | end
27 |
28 | def payload
29 | if raw_payload.is_a?(Proc)
30 | raw_payload.call
31 | else
32 | raw_payload
33 | end
34 | end
35 |
36 | def ==(other)
37 | unicity_key == other.unicity_key
38 | end
39 |
40 | private
41 |
42 | attr_reader :events_catalog, :raw_payload
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | /.config
4 | /coverage/
5 | /InstalledFiles
6 | /pkg/
7 | /spec/reports/
8 | /spec/examples.txt
9 | /test/tmp/
10 | /test/version_tmp/
11 | /tmp/
12 |
13 | # Used by dotenv library to load environment variables.
14 | # .env
15 |
16 | # Ignore Byebug command history file.
17 | .byebug_history
18 |
19 | ## Specific to RubyMotion:
20 | .dat*
21 | .repl_history
22 | build/
23 | *.bridgesupport
24 | build-iPhoneOS/
25 | build-iPhoneSimulator/
26 |
27 | ## Specific to RubyMotion (use of CocoaPods):
28 | #
29 | # We recommend against adding the Pods directory to your .gitignore. However
30 | # you should judge for yourself, the pros and cons are mentioned at:
31 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
32 | #
33 | # vendor/Pods/
34 |
35 | ## Documentation cache and generated files:
36 | /.yardoc/
37 | /_yardoc/
38 | /rdoc/
39 |
40 | ## Environment normalization:
41 | /.bundle/
42 | /vendor/bundle
43 | /lib/bundler/man/
44 |
45 | # for a library or gem, you might want to ignore these files since the code is
46 | # intended to run in multiple environments; otherwise, check them in:
47 | # Gemfile.lock
48 | # .ruby-version
49 | # .ruby-gemset
50 |
51 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
52 | .rvmrc
53 |
54 | # Used by RuboCop. Remote config files pulled in from inherit_from directive.
55 | # .rubocop-https?--*
56 |
--------------------------------------------------------------------------------
/spec/changeset/changeset_integration_spec.rb:
--------------------------------------------------------------------------------
1 | # # typed: ignore
2 |
3 | RSpec.describe "Changeset Integration", with_sorbet: false do
4 | let(:mocked_worker) { spy("mocked_worker") }
5 | let(:event_catalog_klass) do
6 | Class.new do
7 | def initialize(mocked_worker)
8 | @mocked_worker = mocked_worker
9 | end
10 |
11 | def dispatch(event)
12 | send(event.name, event.payload)
13 | end
14 |
15 | def known_event?(event_name)
16 | %i[my_event].include?(event_name)
17 | end
18 |
19 | private
20 |
21 | def my_event(payload)
22 | @mocked_worker.call(payload)
23 | end
24 | end
25 | end
26 | let(:db_operation1) { spy("db_operation1") }
27 | let(:db_operation2) { spy("db_operation2") }
28 | let(:changeset) { ::Changeset.new(event_catalog_klass.new(mocked_worker)) }
29 |
30 | before do
31 | Changeset.configure do |config|
32 | config.db_transaction_wrapper = ->(&block) { block.call }
33 | end
34 | end
35 |
36 | it "triggers db_operations and unique events" do
37 | changeset.add_db_operations(
38 | db_operation1,
39 | db_operation2
40 | )
41 | changeset.add_event(:my_event, {"foo" => 1})
42 | changeset.add_event(:my_event, -> { {"foo" => 1} })
43 |
44 | changeset.push!
45 |
46 | expect(db_operation1).to have_received(:call).ordered
47 | expect(db_operation2).to have_received(:call).ordered
48 | expect(mocked_worker).to have_received(:call).with({"foo" => 1}).once.ordered
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/spec/changeset/merge_child_spec.rb:
--------------------------------------------------------------------------------
1 | # typed: ignore
2 |
3 | RSpec.describe "Changeset Merge Child", with_sorbet: false do
4 | let(:mocked_worker) { spy("mocked_worker") }
5 | let(:mocked_worker2) { spy("mocked_worker2") }
6 | let(:event_catalog_klass) do
7 | Class.new do
8 | def initialize(mocked_worker)
9 | @mocked_worker = mocked_worker
10 | end
11 |
12 | def dispatch(event)
13 | send(event.name, event.payload)
14 | end
15 |
16 | def known_event?(event_name)
17 | %i[my_event].include?(event_name)
18 | end
19 |
20 | private
21 |
22 | def my_event(payload)
23 | @mocked_worker.call(payload)
24 | end
25 | end
26 | end
27 |
28 | let(:db_operation1) { spy("db_operation1") }
29 | let(:db_operation2) { spy("db_operation2") }
30 | let(:db_operation3) { spy("db_operation3") }
31 | let(:changeset) { ::Changeset.new(event_catalog_klass.new(mocked_worker)) }
32 | let(:child_changeset) { ::Changeset.new(event_catalog_klass.new(mocked_worker2)) }
33 |
34 | before do
35 | Changeset.configure do |config|
36 | config.db_transaction_wrapper = ->(&block) { block.call }
37 | end
38 | end
39 |
40 | it "triggers db_operations and unique events" do
41 | changeset.add_db_operations(
42 | db_operation1,
43 | db_operation2
44 | )
45 | changeset.add_event(:my_event, {"foo" => 1})
46 |
47 | child_changeset.add_db_operation(db_operation3)
48 | child_changeset.add_event(:my_event, {"foo" => 2})
49 |
50 | changeset.merge_child(child_changeset)
51 |
52 | changeset.push!
53 |
54 | expect(db_operation1).to have_received(:call).ordered
55 | expect(db_operation2).to have_received(:call).ordered
56 | expect(db_operation3).to have_received(:call).ordered
57 | expect(mocked_worker).to have_received(:call).with({"foo" => 1}).once.ordered
58 | expect(mocked_worker2).to have_received(:call).with({"foo" => 2}).once.ordered
59 | end
60 | end
61 |
--------------------------------------------------------------------------------
/lib/changeset/event_collection.rb:
--------------------------------------------------------------------------------
1 | # typed: true
2 |
3 | class Changeset
4 | class EventCollection
5 | def initialize
6 | @grouped_events = {}
7 | @async_change_sets = []
8 | end
9 |
10 | def add(name:, raw_payload:, events_catalog:)
11 | new_event = Event.new(
12 | name: name,
13 | raw_payload: raw_payload,
14 | events_catalog: events_catalog
15 | )
16 | add_event(new_event)
17 | end
18 |
19 | def merge_child(event_collection)
20 | event_collection.all_events.each do |event|
21 | add_event(event)
22 | end
23 | event_collection.async_change_sets.each do |async_change_set|
24 | async_change_sets.push(async_change_set)
25 | end
26 | end
27 |
28 | def merge_child_async(async_change_set)
29 | async_change_sets.push(async_change_set)
30 | self
31 | end
32 |
33 | # standard:disable Style/ArgumentsForwarding
34 | def each(&block)
35 | uniq_events.each(&block)
36 | end
37 | # standard:enable Style/ArgumentsForwarding
38 |
39 | def ==(other)
40 | uniq_events == other.uniq_events
41 | end
42 |
43 | protected
44 |
45 | attr_reader :grouped_events, :async_change_sets
46 |
47 | # only used for merge
48 | def all_events
49 | [].tap do |collection|
50 | grouped_events.each_value do |events|
51 | collection.concat(events)
52 | end
53 | end
54 | end
55 |
56 | # called after push through #each
57 | def uniq_events
58 | async_change_sets.each do |async_change_set|
59 | async_change_set.events_collection.each do |event|
60 | add_event(event)
61 | end
62 | end
63 |
64 | [].tap do |collection|
65 | grouped_events.each_value do |events|
66 | collection.concat(events.uniq { |event| event.unicity_key })
67 | end
68 | end
69 | end
70 |
71 | private
72 |
73 | def add_event(event)
74 | grouped_events[event.name] ||= []
75 | grouped_events.fetch(event.name).push(event)
76 | end
77 | end
78 | end
79 |
--------------------------------------------------------------------------------
/lib/changeset.rb:
--------------------------------------------------------------------------------
1 | # typed: true
2 |
3 | require "zeitwerk"
4 | loader = Zeitwerk::Loader.for_gem
5 | loader.setup
6 |
7 | class Changeset
8 | def initialize(events_catalog = Changeset::NullEventCatalog.new)
9 | @events_collection = Changeset::EventCollection.new
10 | @db_operations = ::Changeset::DbOperationCollection.new
11 | @events_catalog = events_catalog
12 | end
13 |
14 | def merge_child(change_set)
15 | events_collection.merge_child(change_set.events_collection)
16 | db_operations.merge_child(change_set.db_operations)
17 | self
18 | end
19 |
20 | def merge_child_async(&changeset_wrapped_in_proc)
21 | async_change_set = ::Changeset::AsyncChangeset.new(changeset_wrapped_in_proc)
22 | events_collection.merge_child_async(async_change_set)
23 | db_operations.merge_child_async(async_change_set)
24 | self
25 | end
26 |
27 | def add_event(name, raw_payload)
28 | events_collection.add(name: name, raw_payload: raw_payload, events_catalog: events_catalog)
29 | self
30 | end
31 |
32 | def add_db_operations(*persistence_handlers)
33 | persistence_handlers.each do |persistence_handler|
34 | add_db_operation(persistence_handler)
35 | end
36 | self
37 | end
38 |
39 | def add_db_operation(persistence_handler)
40 | db_operations.add(persistence_handler)
41 | self
42 | end
43 |
44 | def push!
45 | commit_db_operations
46 | dispatch_events
47 | self
48 | end
49 |
50 | def commit_db_operations
51 | Changeset.configuration.db_transaction_wrapper.call do
52 | db_operations.each(&:call)
53 | end
54 | end
55 |
56 | def dispatch_events
57 | events_collection.each do |event|
58 | event.dispatch
59 | end
60 | end
61 |
62 | def ==(other)
63 | db_operations == other.db_operations &&
64 | events_collection == other.events_collection
65 | end
66 |
67 | def self.configuration
68 | @configuration ||= Changeset::Configuration.new
69 | end
70 |
71 | def self.configure(&block)
72 | block.call(configuration)
73 | end
74 |
75 | protected
76 |
77 | attr_reader :events_collection, :db_operations, :events_catalog
78 | end
79 |
--------------------------------------------------------------------------------
/sorbet/rbi/gems/rspec@3.11.0.rbi:
--------------------------------------------------------------------------------
1 | # typed: true
2 |
3 | # DO NOT EDIT MANUALLY
4 | # This is an autogenerated file for types exported from the `rspec` gem.
5 | # Please instead update this file by running `bin/tapioca gem rspec`.
6 |
7 | # source://rspec//lib/rspec/version.rb#1
8 | module RSpec
9 | class << self
10 | # source://rspec-core/3.11.0/lib/rspec/core.rb#70
11 | def clear_examples; end
12 |
13 | # source://rspec-core/3.11.0/lib/rspec/core.rb#85
14 | def configuration; end
15 |
16 | # source://rspec-core/3.11.0/lib/rspec/core.rb#49
17 | def configuration=(_arg0); end
18 |
19 | # source://rspec-core/3.11.0/lib/rspec/core.rb#97
20 | def configure; end
21 |
22 | # source://rspec-core/3.11.0/lib/rspec/core.rb#194
23 | def const_missing(name); end
24 |
25 | # source://rspec-core/3.11.0/lib/rspec/core/dsl.rb#42
26 | def context(*args, &example_group_block); end
27 |
28 | # source://rspec-core/3.11.0/lib/rspec/core.rb#122
29 | def current_example; end
30 |
31 | # source://rspec-core/3.11.0/lib/rspec/core.rb#128
32 | def current_example=(example); end
33 |
34 | # source://rspec-core/3.11.0/lib/rspec/core.rb#154
35 | def current_scope; end
36 |
37 | # source://rspec-core/3.11.0/lib/rspec/core.rb#134
38 | def current_scope=(scope); end
39 |
40 | # source://rspec-core/3.11.0/lib/rspec/core/dsl.rb#42
41 | def describe(*args, &example_group_block); end
42 |
43 | # source://rspec-core/3.11.0/lib/rspec/core/dsl.rb#42
44 | def example_group(*args, &example_group_block); end
45 |
46 | # source://rspec-core/3.11.0/lib/rspec/core/dsl.rb#42
47 | def fcontext(*args, &example_group_block); end
48 |
49 | # source://rspec-core/3.11.0/lib/rspec/core/dsl.rb#42
50 | def fdescribe(*args, &example_group_block); end
51 |
52 | # source://rspec-core/3.11.0/lib/rspec/core.rb#58
53 | def reset; end
54 |
55 | # source://rspec-core/3.11.0/lib/rspec/core/shared_example_group.rb#110
56 | def shared_context(name, *args, &block); end
57 |
58 | # source://rspec-core/3.11.0/lib/rspec/core/shared_example_group.rb#110
59 | def shared_examples(name, *args, &block); end
60 |
61 | # source://rspec-core/3.11.0/lib/rspec/core/shared_example_group.rb#110
62 | def shared_examples_for(name, *args, &block); end
63 |
64 | # source://rspec-core/3.11.0/lib/rspec/core.rb#160
65 | def world; end
66 |
67 | # source://rspec-core/3.11.0/lib/rspec/core.rb#49
68 | def world=(_arg0); end
69 |
70 | # source://rspec-core/3.11.0/lib/rspec/core/dsl.rb#42
71 | def xcontext(*args, &example_group_block); end
72 |
73 | # source://rspec-core/3.11.0/lib/rspec/core/dsl.rb#42
74 | def xdescribe(*args, &example_group_block); end
75 | end
76 | end
77 |
78 | # source://rspec-core/3.11.0/lib/rspec/core.rb#187
79 | RSpec::MODULES_TO_AUTOLOAD = T.let(T.unsafe(nil), Hash)
80 |
81 | # source://rspec-core/3.11.0/lib/rspec/core/shared_context.rb#54
82 | RSpec::SharedContext = RSpec::Core::SharedContext
83 |
84 | # source://rspec//lib/rspec/version.rb#2
85 | module RSpec::Version; end
86 |
87 | # source://rspec//lib/rspec/version.rb#3
88 | RSpec::Version::STRING = T.let(T.unsafe(nil), String)
89 |
--------------------------------------------------------------------------------
/doc/combo.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | changeset (0.1.4)
5 | zeitwerk
6 |
7 | GEM
8 | remote: https://rubygems.org/
9 | specs:
10 | ast (2.4.2)
11 | byebug (11.1.3)
12 | coderay (1.1.3)
13 | diff-lcs (1.5.0)
14 | json (2.7.2)
15 | language_server-protocol (3.17.0.3)
16 | lint_roller (1.1.0)
17 | method_source (1.0.0)
18 | parallel (1.25.1)
19 | parser (3.3.4.0)
20 | ast (~> 2.4.1)
21 | racc
22 | pry (0.14.1)
23 | coderay (~> 1.1)
24 | method_source (~> 1.0)
25 | racc (1.8.1)
26 | rainbow (3.1.1)
27 | rbi (0.0.17)
28 | ast
29 | parser (>= 3.0.0)
30 | sorbet-runtime (>= 0.5.9204)
31 | unparser (>= 0.5.6)
32 | regexp_parser (2.9.2)
33 | rexml (3.3.4)
34 | strscan
35 | rspec (3.11.0)
36 | rspec-core (~> 3.11.0)
37 | rspec-expectations (~> 3.11.0)
38 | rspec-mocks (~> 3.11.0)
39 | rspec-core (3.11.0)
40 | rspec-support (~> 3.11.0)
41 | rspec-expectations (3.11.1)
42 | diff-lcs (>= 1.2.0, < 2.0)
43 | rspec-support (~> 3.11.0)
44 | rspec-mocks (3.11.1)
45 | diff-lcs (>= 1.2.0, < 2.0)
46 | rspec-support (~> 3.11.0)
47 | rspec-support (3.11.1)
48 | rubocop (1.64.1)
49 | json (~> 2.3)
50 | language_server-protocol (>= 3.17.0)
51 | parallel (~> 1.10)
52 | parser (>= 3.3.0.2)
53 | rainbow (>= 2.2.2, < 4.0)
54 | regexp_parser (>= 1.8, < 3.0)
55 | rexml (>= 3.2.5, < 4.0)
56 | rubocop-ast (>= 1.31.1, < 2.0)
57 | ruby-progressbar (~> 1.7)
58 | unicode-display_width (>= 2.4.0, < 3.0)
59 | rubocop-ast (1.31.3)
60 | parser (>= 3.3.1.0)
61 | rubocop-performance (1.21.1)
62 | rubocop (>= 1.48.1, < 2.0)
63 | rubocop-ast (>= 1.31.1, < 2.0)
64 | ruby-progressbar (1.13.0)
65 | sorbet (0.5.9204)
66 | sorbet-static (= 0.5.9204)
67 | sorbet-runtime (0.5.9204)
68 | sorbet-static (0.5.9204-universal-darwin-19)
69 | sorbet-static (0.5.9204-universal-darwin-20)
70 | sorbet-static (0.5.9204-universal-darwin-21)
71 | sorbet-static (0.5.9204-x86_64-linux)
72 | spoom (1.1.13)
73 | sorbet (>= 0.5.9204)
74 | sorbet-runtime (>= 0.5.9204)
75 | thor (>= 0.19.2)
76 | standard (1.39.2)
77 | language_server-protocol (~> 3.17.0.2)
78 | lint_roller (~> 1.0)
79 | rubocop (~> 1.64.0)
80 | standard-custom (~> 1.0.0)
81 | standard-performance (~> 1.4)
82 | standard-custom (1.0.2)
83 | lint_roller (~> 1.0)
84 | rubocop (~> 1.50)
85 | standard-performance (1.4.0)
86 | lint_roller (~> 1.1)
87 | rubocop-performance (~> 1.21.0)
88 | strscan (3.1.0)
89 | tapioca (0.7.3)
90 | bundler (>= 1.17.3)
91 | pry (>= 0.12.2)
92 | rbi (~> 0.0.0, >= 0.0.14)
93 | sorbet-runtime (>= 0.5.9204)
94 | sorbet-static (>= 0.5.9204)
95 | spoom (~> 1.1.0, >= 1.1.11)
96 | thor (>= 1.2.0)
97 | yard-sorbet
98 | thor (1.2.1)
99 | unicode-display_width (2.5.0)
100 | unparser (0.6.5)
101 | diff-lcs (~> 1.3)
102 | parser (>= 3.1.0)
103 | webrick (1.7.0)
104 | yard (0.9.28)
105 | webrick (~> 1.7.0)
106 | yard-sorbet (0.7.0)
107 | sorbet-runtime (>= 0.5)
108 | yard (>= 0.9)
109 | zeitwerk (2.6.6)
110 |
111 | PLATFORMS
112 | arm64-darwin-21
113 | x86_64-darwin-19
114 | x86_64-darwin-20
115 | x86_64-linux
116 |
117 | DEPENDENCIES
118 | byebug
119 | changeset!
120 | rspec (= 3.11.0)
121 | sorbet (= 0.5.9204)
122 | sorbet-runtime (= 0.5.9204)
123 | standard
124 | tapioca
125 |
126 | BUNDLED WITH
127 | 2.5.9
128 |
--------------------------------------------------------------------------------
/spec/changeset/changeset_spec.rb:
--------------------------------------------------------------------------------
1 | # typed: ignore
2 |
3 | RSpec.describe Changeset, with_sorbet: false do
4 | let(:event_catalog_klass) do
5 | Class.new do
6 | def dispatch(event)
7 | send(event.name, event)
8 | end
9 |
10 | def known_event?(event_name)
11 | %i[my_event].include?(event_name)
12 | end
13 |
14 | private
15 |
16 | def my_event(event)
17 | end
18 | end
19 | end
20 |
21 | let(:other_event_catalog_klass) do
22 | Class.new do
23 | def dispatch(event)
24 | send(event.name, event)
25 | end
26 |
27 | def known_event?(event_name)
28 | %i[my_event].include?(event_name)
29 | end
30 |
31 | private
32 |
33 | def my_event(event)
34 | end
35 | end
36 | end
37 |
38 | let(:db_operation_klass) do
39 | Class.new do
40 | def initialize(some_param)
41 | @some_param = some_param
42 | end
43 |
44 | def ==(other)
45 | some_param == other.some_param
46 | end
47 |
48 | protected
49 |
50 | attr_reader :some_param
51 | end
52 | end
53 |
54 | before do
55 | Changeset.configure do |config|
56 | config.db_transaction_wrapper = ->(&block) { block.call }
57 | end
58 | end
59 |
60 | context "comparing changesets" do
61 | let(:changeset1) { Changeset.new(event_catalog_klass.new) }
62 | let(:changeset2) { Changeset.new(event_catalog_klass.new) }
63 |
64 | context "when empty" do
65 | it "is equal" do
66 | expect(changeset1).to eq(changeset2)
67 | end
68 | end
69 |
70 | context "only db operations" do
71 | it "returns true when same collections" do
72 | changeset1.add_db_operations(
73 | db_operation_klass.new("db_operation1"),
74 | db_operation_klass.new("db_operation2")
75 | )
76 |
77 | changeset2.add_db_operations(
78 | db_operation_klass.new("db_operation1"),
79 | db_operation_klass.new("db_operation2")
80 | )
81 |
82 | expect(changeset1).to eq(changeset2)
83 | end
84 |
85 | it "returns false when different collections" do
86 | changeset1.add_db_operations(
87 | db_operation_klass.new("db_operation1"),
88 | db_operation_klass.new("db_operation2")
89 | )
90 |
91 | changeset2.add_db_operations(
92 | db_operation_klass.new("db_operation2"),
93 | db_operation_klass.new("db_operation1")
94 | )
95 |
96 | expect(changeset1).to_not eql(changeset2)
97 | end
98 | end
99 |
100 | context "only events" do
101 | it "returns true when uniq events with same payload" do
102 | changeset1.add_event(:my_event, {"foo" => 1})
103 | changeset1.add_event(:my_event, {"foo" => 1})
104 |
105 | changeset2.add_event(:my_event, {"foo" => 1})
106 |
107 | expect(changeset1).to eq(changeset2)
108 | end
109 |
110 | # not sure we should do this
111 | it "returns true when uniq events with same payload (evaluates blocks)" do
112 | changeset1.add_event(:my_event, {"foo" => 1})
113 |
114 | changeset2.add_event(:my_event, -> { {"foo" => 1} })
115 |
116 | expect(changeset1).to eq(changeset2)
117 | end
118 |
119 | it "returns false when events with different payloads" do
120 | changeset1.add_event(:my_event, {"foo" => 1})
121 |
122 | changeset2.add_event(:my_event, {"foo" => 2})
123 |
124 | expect(changeset1).to_not eq(changeset2)
125 | end
126 |
127 | it "returns false when events with same payload (evaluates blocks)" do
128 | changeset1.add_event(:my_event, -> { {"foo" => 1} })
129 |
130 | changeset2.add_event(:my_event, -> { {"foo" => 2} })
131 |
132 | expect(changeset1).to_not eq(changeset2)
133 | end
134 |
135 | context "different events catalogs" do
136 | let(:changeset2) { Changeset.new(other_event_catalog_klass.new) }
137 |
138 | it "returns false event same payload and name" do
139 | changeset1.add_event(:my_event, {"foo" => 1})
140 |
141 | changeset2.add_event(:my_event, {"foo" => 1})
142 |
143 | expect(changeset1).to_not eq(changeset2)
144 | end
145 | end
146 | end
147 | end
148 | end
149 |
--------------------------------------------------------------------------------
/sorbet/rbi/gems/netrc@0.11.0.rbi:
--------------------------------------------------------------------------------
1 | # typed: true
2 |
3 | # DO NOT EDIT MANUALLY
4 | # This is an autogenerated file for types exported from the `netrc` gem.
5 | # Please instead update this file by running `bin/tapioca gem netrc`.
6 |
7 | # source://netrc//lib/netrc.rb#3
8 | class Netrc
9 | # @return [Netrc] a new instance of Netrc
10 | #
11 | # source://netrc//lib/netrc.rb#166
12 | def initialize(path, data); end
13 |
14 | # source://netrc//lib/netrc.rb#180
15 | def [](k); end
16 |
17 | # source://netrc//lib/netrc.rb#188
18 | def []=(k, info); end
19 |
20 | # source://netrc//lib/netrc.rb#200
21 | def delete(key); end
22 |
23 | # source://netrc//lib/netrc.rb#211
24 | def each(&block); end
25 |
26 | # source://netrc//lib/netrc.rb#196
27 | def length; end
28 |
29 | # source://netrc//lib/netrc.rb#215
30 | def new_item(m, l, p); end
31 |
32 | # Returns the value of attribute new_item_prefix.
33 | #
34 | # source://netrc//lib/netrc.rb#178
35 | def new_item_prefix; end
36 |
37 | # Sets the attribute new_item_prefix
38 | #
39 | # @param value the value to set the attribute new_item_prefix to.
40 | #
41 | # source://netrc//lib/netrc.rb#178
42 | def new_item_prefix=(_arg0); end
43 |
44 | # source://netrc//lib/netrc.rb#219
45 | def save; end
46 |
47 | # source://netrc//lib/netrc.rb#233
48 | def unparse; end
49 |
50 | class << self
51 | # source://netrc//lib/netrc.rb#42
52 | def check_permissions(path); end
53 |
54 | # source://netrc//lib/netrc.rb#33
55 | def config; end
56 |
57 | # @yield [self.config]
58 | #
59 | # source://netrc//lib/netrc.rb#37
60 | def configure; end
61 |
62 | # source://netrc//lib/netrc.rb#10
63 | def default_path; end
64 |
65 | # source://netrc//lib/netrc.rb#14
66 | def home_path; end
67 |
68 | # source://netrc//lib/netrc.rb#85
69 | def lex(lines); end
70 |
71 | # source://netrc//lib/netrc.rb#29
72 | def netrc_filename; end
73 |
74 | # Returns two values, a header and a list of items.
75 | # Each item is a tuple, containing some or all of:
76 | # - machine keyword (including trailing whitespace+comments)
77 | # - machine name
78 | # - login keyword (including surrounding whitespace+comments)
79 | # - login
80 | # - password keyword (including surrounding whitespace+comments)
81 | # - password
82 | # - trailing chars
83 | # This lets us change individual fields, then write out the file
84 | # with all its original formatting.
85 | #
86 | # source://netrc//lib/netrc.rb#129
87 | def parse(ts); end
88 |
89 | # Reads path and parses it as a .netrc file. If path doesn't
90 | # exist, returns an empty object. Decrypt paths ending in .gpg.
91 | #
92 | # source://netrc//lib/netrc.rb#51
93 | def read(path = T.unsafe(nil)); end
94 |
95 | # @return [Boolean]
96 | #
97 | # source://netrc//lib/netrc.rb#112
98 | def skip?(s); end
99 | end
100 | end
101 |
102 | # source://netrc//lib/netrc.rb#244
103 | class Netrc::Entry < ::Struct
104 | # Returns the value of attribute login
105 | #
106 | # @return [Object] the current value of login
107 | def login; end
108 |
109 | # Sets the attribute login
110 | #
111 | # @param value [Object] the value to set the attribute login to.
112 | # @return [Object] the newly set value
113 | def login=(_); end
114 |
115 | # Returns the value of attribute password
116 | #
117 | # @return [Object] the current value of password
118 | def password; end
119 |
120 | # Sets the attribute password
121 | #
122 | # @param value [Object] the value to set the attribute password to.
123 | # @return [Object] the newly set value
124 | def password=(_); end
125 |
126 | def to_ary; end
127 |
128 | class << self
129 | def [](*_arg0); end
130 | def inspect; end
131 | def keyword_init?; end
132 | def members; end
133 | def new(*_arg0); end
134 | end
135 | end
136 |
137 | # source://netrc//lib/netrc.rb#250
138 | class Netrc::Error < ::StandardError; end
139 |
140 | # source://netrc//lib/netrc.rb#68
141 | class Netrc::TokenArray < ::Array
142 | # source://netrc//lib/netrc.rb#76
143 | def readto; end
144 |
145 | # source://netrc//lib/netrc.rb#69
146 | def take; end
147 | end
148 |
149 | # source://netrc//lib/netrc.rb#4
150 | Netrc::VERSION = T.let(T.unsafe(nil), String)
151 |
--------------------------------------------------------------------------------
/spec/changeset/merge_child_async_spec.rb:
--------------------------------------------------------------------------------
1 | # typed: ignore
2 |
3 | RSpec.describe "Changeset Merge Child Async", with_sorbet: false do
4 | context "check calls" do
5 | let(:mocked_worker) { spy("mocked_worker") }
6 | let(:mocked_worker2) { spy("mocked_worker2") }
7 | let(:mocked_worker3) { spy("mocked_worker3") }
8 | let(:event_catalog_klass) do
9 | Class.new do
10 | def initialize(mocked_worker)
11 | @mocked_worker = mocked_worker
12 | end
13 |
14 | def dispatch(event)
15 | send(event.name, event.payload)
16 | end
17 |
18 | def known_event?(event_name)
19 | %i[my_event].include?(event_name)
20 | end
21 |
22 | private
23 |
24 | def my_event(payload)
25 | @mocked_worker.call(payload)
26 | end
27 | end
28 | end
29 |
30 | let(:db_operation1) { spy("db_operation1") }
31 | let(:db_operation2) { spy("db_operation2") }
32 | let(:db_operation3) { spy("db_operation3") }
33 | let(:db_operation4) { spy("db_operation4") }
34 | let(:changeset) { ::Changeset.new(event_catalog_klass.new(mocked_worker)) }
35 | let(:child_changeset) { ::Changeset.new(event_catalog_klass.new(mocked_worker2)) }
36 | let(:grand_child_changeset) { ::Changeset.new(event_catalog_klass.new(mocked_worker3)) }
37 |
38 | before do
39 | Changeset.configure do |config|
40 | config.db_transaction_wrapper = ->(&block) { block.call }
41 | end
42 | end
43 |
44 | it "triggers db_operations and events" do
45 | changeset.add_db_operations(
46 | db_operation1,
47 | db_operation2
48 | )
49 | changeset.add_event(:my_event, {"foo" => 1})
50 |
51 | changeset.merge_child_async do
52 | child_changeset
53 | .add_db_operation(db_operation3)
54 | .add_event(:my_event, {"foo" => 2})
55 | .merge_child_async do
56 | grand_child_changeset
57 | .add_db_operations(db_operation4)
58 | .add_event(:my_event, {"foo" => 3})
59 | end
60 | end
61 |
62 | changeset.push!
63 |
64 | expect(db_operation1).to have_received(:call).ordered
65 | expect(db_operation2).to have_received(:call).ordered
66 | expect(db_operation3).to have_received(:call).ordered
67 | expect(db_operation4).to have_received(:call).ordered
68 | expect(mocked_worker).to have_received(:call).with({"foo" => 1}).once.ordered
69 | expect(mocked_worker2).to have_received(:call).with({"foo" => 2}).once.ordered
70 | expect(mocked_worker3).to have_received(:call).with({"foo" => 3}).once.ordered
71 | end
72 | end
73 |
74 | context "check instanciation order" do
75 | it "triggers db_operations" do
76 | child_changeset_instantiated = false
77 | grand_child_changeset_instantiated = false
78 |
79 | db_operation1_called = false
80 | db_operation2_called = false
81 | db_operation3_called = false
82 | db_operation4_called = false
83 | db_operation5_called = false
84 |
85 | db_operation1 = -> {
86 | db_operation1_called = true
87 | expect(child_changeset_instantiated).to be false
88 | }
89 |
90 | db_operation2 = -> {
91 | db_operation2_called = true
92 | expect(grand_child_changeset_instantiated).to be false
93 | }
94 |
95 | db_operation3 = -> {
96 | db_operation3_called = true
97 | }
98 |
99 | db_operation4 = -> {
100 | db_operation4_called = true
101 | expect(grand_child_changeset_instantiated).to be true
102 | }
103 |
104 | db_operation5 = -> {
105 | db_operation5_called = true
106 | expect(child_changeset_instantiated).to be true
107 | expect(grand_child_changeset_instantiated).to be true
108 | }
109 |
110 | Changeset.new.add_db_operations(
111 | db_operation1
112 | ).merge_child_async do
113 | child_changeset_instantiated = true
114 | Changeset.new
115 | .add_db_operation(db_operation2)
116 | .merge_child_async do
117 | grand_child_changeset_instantiated = true
118 | Changeset.new
119 | .add_db_operations(db_operation3)
120 | end
121 | .add_db_operation(db_operation4)
122 | end
123 | .add_db_operation(db_operation5)
124 | .push!
125 |
126 | expect(db_operation1_called).to be true
127 | expect(db_operation2_called).to be true
128 | expect(db_operation3_called).to be true
129 | expect(db_operation4_called).to be true
130 | expect(db_operation5_called).to be true
131 | end
132 | end
133 | end
134 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | # typed: false
2 |
3 | require "rubygems"
4 | require "sorbet-runtime"
5 | require "byebug"
6 | require_relative "../lib/changeset"
7 |
8 | # This file was generated by the `rspec --init` command. Conventionally, all
9 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
10 | # The generated `.rspec` file contains `--require spec_helper` which will cause
11 | # this file to always be loaded, without a need to explicitly require it in any
12 | # files.
13 | #
14 | # Given that it is always loaded, you are encouraged to keep this file as
15 | # light-weight as possible. Requiring heavyweight dependencies from this file
16 | # will add to the boot time of your test suite on EVERY test run, even for an
17 | # individual file that may not need all of that loaded. Instead, consider making
18 | # a separate helper file that requires the additional dependencies and performs
19 | # the additional setup, and require it from the spec files that actually need
20 | # it.
21 | #
22 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
23 | RSpec.configure do |config|
24 | # rspec-expectations config goes here. You can use an alternate
25 | # assertion/expectation library such as wrong or the stdlib/minitest
26 | # assertions if you prefer.
27 | config.expect_with :rspec do |expectations|
28 | # This option will default to `true` in RSpec 4. It makes the `description`
29 | # and `failure_message` of custom matchers include text for helper methods
30 | # defined using `chain`, e.g.:
31 | # be_bigger_than(2).and_smaller_than(4).description
32 | # # => "be bigger than 2 and smaller than 4"
33 | # ...rather than:
34 | # # => "be bigger than 2"
35 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true
36 | end
37 |
38 | # rspec-mocks config goes here. You can use an alternate test double
39 | # library (such as bogus or mocha) by changing the `mock_with` option here.
40 | config.mock_with :rspec do |mocks|
41 | # Prevents you from mocking or stubbing a method that does not exist on
42 | # a real object. This is generally recommended, and will default to
43 | # `true` in RSpec 4.
44 | mocks.verify_partial_doubles = true
45 | end
46 |
47 | # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
48 | # have no way to turn it off -- the option exists only for backwards
49 | # compatibility in RSpec 3). It causes shared context metadata to be
50 | # inherited by the metadata hash of host groups and examples, rather than
51 | # triggering implicit auto-inclusion in groups with matching metadata.
52 | config.shared_context_metadata_behavior = :apply_to_host_groups
53 |
54 | # The settings below are suggested to provide a good initial experience
55 | # with RSpec, but feel free to customize to your heart's content.
56 | # # This allows you to limit a spec run to individual examples or groups
57 | # # you care about by tagging them with `:focus` metadata. When nothing
58 | # # is tagged with `:focus`, all examples get run. RSpec also provides
59 | # # aliases for `it`, `describe`, and `context` that include `:focus`
60 | # # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
61 | # config.filter_run_when_matching :focus
62 | #
63 | # # Allows RSpec to persist some state between runs in order to support
64 | # # the `--only-failures` and `--next-failure` CLI options. We recommend
65 | # # you configure your source control system to ignore this file.
66 | # config.example_status_persistence_file_path = "spec/examples.txt"
67 | #
68 | # # Limits the available syntax to the non-monkey patched syntax that is
69 | # # recommended. For more details, see:
70 | # # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
71 | # # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
72 | # # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
73 | # config.disable_monkey_patching!
74 | #
75 | # # This setting enables warnings. It's recommended, but in some cases may
76 | # # be too noisy due to issues in dependencies.
77 | # config.warnings = true
78 | #
79 | # # Many RSpec users commonly either run the entire suite or an individual
80 | # # file, and it's useful to allow more verbose output when running an
81 | # # individual spec file.
82 | # if config.files_to_run.one?
83 | # # Use the documentation formatter for detailed output,
84 | # # unless a formatter has already been configured
85 | # # (e.g. via a command-line flag).
86 | # config.default_formatter = "doc"
87 | # end
88 | #
89 | # # Print the 10 slowest examples and example groups at the
90 | # # end of the spec run, to help surface which specs are running
91 | # # particularly slow.
92 | # config.profile_examples = 10
93 | #
94 | # # Run specs in random order to surface order dependencies. If you find an
95 | # # order dependency and want to debug it, you can fix the order by providing
96 | # # the seed, which is printed after each run.
97 | # # --seed 1234
98 | # config.order = :random
99 | #
100 | # # Seed global randomization in this process using the `--seed` CLI option.
101 | # # Setting this allows you to use `--seed` to deterministically reproduce
102 | # # test failures related to randomization by passing the same `--seed` value
103 | # # as the one that triggered the failure.
104 | # Kernel.srand config.seed
105 |
106 | config.around(:each) do |example|
107 | if example.metadata[:with_sorbet]
108 | example.run
109 | else
110 | T::Configuration.call_validation_error_handler = lambda do |signature, opts|
111 | puts opts[:pretty_message]
112 | end
113 |
114 | T::Configuration.inline_type_error_handler = lambda do |error, opts|
115 | puts error.message
116 | end
117 |
118 | example.run
119 |
120 | # if custom value is nil, it goes to the default sorbet behaviour
121 | T::Configuration.inline_type_error_handler = nil
122 | T::Configuration.call_validation_error_handler = nil
123 | end
124 | end
125 | end
126 |
--------------------------------------------------------------------------------
/rbi/changeset.rbi:
--------------------------------------------------------------------------------
1 | # typed: true
2 |
3 | class Changeset
4 | EventPayload = T.type_alias { T.untyped }
5 | Callable = T.type_alias { T.any(Changeset::PersistenceInterface, T.proc.void) }
6 | RawEventPayload = T.type_alias { T.any(EventPayload, T.proc.returns(EventPayload)) }
7 |
8 | sig { params(events_catalog: ::Changeset::EventCatalogInterface).void }
9 | def initialize(events_catalog = Changeset::NullEventCatalog.new)
10 | end
11 |
12 | sig { params(change_set: Changeset).returns(T.self_type) }
13 | def merge_child(change_set)
14 | end
15 |
16 | sig { params(changeset_wrapped_in_proc: T.proc.returns(::Changeset)).returns(T.self_type) }
17 | def merge_child_async(&changeset_wrapped_in_proc)
18 | end
19 |
20 | sig { params(name: Symbol, raw_payload: Changeset::RawEventPayload).returns(T.self_type) }
21 | def add_event(name, raw_payload)
22 | end
23 |
24 | sig { params(persistence_handlers: Changeset::Callable).returns(T.self_type) }
25 | def add_db_operations(*persistence_handlers)
26 | end
27 |
28 | sig { params(persistence_handler: Changeset::Callable).returns(T.self_type) }
29 | def add_db_operation(persistence_handler)
30 | end
31 |
32 | sig { returns(T.self_type) }
33 | def push!
34 | end
35 |
36 | sig { void }
37 | def commit_db_operations
38 | end
39 |
40 | sig { void }
41 | def dispatch_events
42 | end
43 |
44 | sig { params(other: Changeset).returns(T::Boolean) }
45 | def ==(other)
46 | end
47 |
48 | sig { returns(Changeset::Configuration) }
49 | def self.configuration
50 | end
51 |
52 | sig { params(block: T.proc.params(block: Changeset::Configuration).void).void }
53 | def self.configure(&block)
54 | end
55 |
56 | class Configuration
57 | DbTransactionWrapper = T.type_alias { T.proc.params(block: T.proc.void).void }
58 |
59 | sig { params(db_transaction_wrapper: DbTransactionWrapper).returns(DbTransactionWrapper) }
60 | attr_writer :db_transaction_wrapper
61 |
62 | sig { returns(T.proc.void) }
63 | def db_transaction_wrapper
64 | end
65 | end
66 |
67 | module EventCatalogInterface
68 | extend T::Helpers
69 | abstract!
70 |
71 | sig { abstract.params(event: Changeset::Event).void }
72 | def dispatch(event)
73 | end
74 |
75 | sig { abstract.params(event_name: Symbol).returns(T::Boolean) }
76 | def known_event?(event_name)
77 | end
78 |
79 | sig { returns(Class) }
80 | def class
81 | end
82 | end
83 |
84 | class AsyncChangeset
85 | sig { params(changeset_wrapped_in_proc: T.proc.returns(::Changeset)).void }
86 | def initialize(changeset_wrapped_in_proc)
87 | end
88 |
89 | sig { returns(DbOperationCollection) }
90 | def db_operations
91 | end
92 |
93 | sig { returns(EventCollection) }
94 | def events_collection
95 | end
96 | end
97 |
98 | class DbOperationCollection
99 | CollectionElement = T.type_alias { T.any(Changeset::PersistenceInterface, T.proc.void, Changeset::AsyncChangeset) }
100 |
101 | sig { void }
102 | def initialize
103 | end
104 |
105 | sig { params(persistence_handler: CollectionElement).void }
106 | def add(persistence_handler)
107 | end
108 |
109 | sig { params(db_operations: Changeset::DbOperationCollection).void }
110 | def merge_child(db_operations)
111 | end
112 |
113 | sig { params(async_change_set: Changeset::AsyncChangeset).void }
114 | def merge_child_async(async_change_set)
115 | end
116 |
117 | sig { params(block: T.proc.params(arg0: Changeset::Callable).returns(BasicObject)).void }
118 | def each(&block)
119 | end
120 |
121 | sig { params(other: Changeset::DbOperationCollection).returns(T::Boolean) }
122 | def ==(other)
123 | end
124 |
125 | protected
126 |
127 | sig { returns(T::Array[CollectionElement]) }
128 | attr_reader :collection
129 | end
130 |
131 | class EventCollection
132 | GroupedEvent = T.type_alias { T::Hash[Symbol, T::Array[Changeset::Event]] }
133 |
134 | sig { void }
135 | def initialize
136 | end
137 |
138 | sig { params(name: Symbol, raw_payload: Changeset::RawEventPayload, events_catalog: ::Changeset::EventCatalogInterface).void }
139 | def add(name:, raw_payload:, events_catalog:)
140 | end
141 |
142 | sig { params(event_collection: Changeset::EventCollection).void }
143 | def merge_child(event_collection)
144 | end
145 |
146 | sig { params(async_change_set: Changeset::AsyncChangeset).void }
147 | def merge_child_async(async_change_set)
148 | end
149 |
150 | sig { params(block: T.proc.params(arg0: Changeset::Event).returns(BasicObject)).void }
151 | def each(&block)
152 | end
153 |
154 | sig { params(other: Changeset::EventCollection).returns(T::Boolean) }
155 | def ==(other)
156 | end
157 |
158 | protected
159 |
160 | sig { returns(GroupedEvent) }
161 | attr_reader :grouped_events
162 | sig { returns(T::Array[::Changeset::AsyncChangeset]) }
163 | attr_reader :async_change_sets
164 |
165 | # only used for merge
166 | sig { returns(T::Array[Changeset::Event]) }
167 | def all_events
168 | end
169 |
170 | sig { returns(T::Array[Changeset::Event]) }
171 | def uniq_events
172 | end
173 |
174 | private
175 |
176 | sig { params(event: Changeset::Event).void }
177 | def add_event(event)
178 | end
179 | end
180 |
181 | class Event
182 | sig { returns(Symbol) }
183 | attr_reader :name
184 |
185 | sig { params(name: Symbol, raw_payload: Changeset::RawEventPayload, events_catalog: ::Changeset::EventCatalogInterface).void }
186 | def initialize(name:, raw_payload:, events_catalog:)
187 | end
188 |
189 | sig { void }
190 | def dispatch
191 | end
192 |
193 | sig { returns(T::Array[T.untyped]) }
194 | def unicity_key
195 | end
196 |
197 | sig { returns(Changeset::EventPayload) }
198 | def payload
199 | end
200 |
201 | sig { params(other: Changeset::Event).returns(T::Boolean) }
202 | def ==(other)
203 | end
204 |
205 | private
206 |
207 | sig { returns(Changeset::EventCatalogInterface) }
208 | attr_reader :events_catalog
209 |
210 | sig { returns(Changeset::RawEventPayload) }
211 | attr_reader :raw_payload
212 | end
213 |
214 | class NullEventCatalog
215 | include Changeset::EventCatalogInterface
216 |
217 | sig { override.params(event: Changeset::Event).void }
218 | def dispatch(event)
219 | end
220 |
221 | sig { override.params(event_name: Symbol).returns(T::Boolean) }
222 | def known_event?(event_name)
223 | end
224 | end
225 |
226 | module PersistenceInterface
227 | extend T::Helpers
228 | abstract!
229 |
230 | sig { abstract.void }
231 | def call
232 | end
233 | end
234 |
235 | protected
236 | sig { returns(Changeset::DbOperationCollection) }
237 | attr_reader :db_operations
238 | sig { returns(Changeset::EventCollection) }
239 | attr_reader :events_collection
240 | sig { returns(::Changeset::EventCatalogInterface) }
241 | attr_reader :events_catalog
242 | end
--------------------------------------------------------------------------------
/sorbet/rbi/gems/parallel@1.22.1.rbi:
--------------------------------------------------------------------------------
1 | # typed: true
2 |
3 | # DO NOT EDIT MANUALLY
4 | # This is an autogenerated file for types exported from the `parallel` gem.
5 | # Please instead update this file by running `bin/tapioca gem parallel`.
6 |
7 | # source://parallel//lib/parallel/version.rb#2
8 | module Parallel
9 | extend ::Parallel::ProcessorCount
10 |
11 | class << self
12 | # @return [Boolean]
13 | #
14 | # source://parallel//lib/parallel.rb#246
15 | def all?(*args, &block); end
16 |
17 | # @return [Boolean]
18 | #
19 | # source://parallel//lib/parallel.rb#241
20 | def any?(*args, &block); end
21 |
22 | # source://parallel//lib/parallel.rb#237
23 | def each(array, options = T.unsafe(nil), &block); end
24 |
25 | # source://parallel//lib/parallel.rb#251
26 | def each_with_index(array, options = T.unsafe(nil), &block); end
27 |
28 | # source://parallel//lib/parallel.rb#306
29 | def flat_map(*args, &block); end
30 |
31 | # source://parallel//lib/parallel.rb#231
32 | def in_processes(options = T.unsafe(nil), &block); end
33 |
34 | # source://parallel//lib/parallel.rb#215
35 | def in_threads(options = T.unsafe(nil)); end
36 |
37 | # source://parallel//lib/parallel.rb#255
38 | def map(source, options = T.unsafe(nil), &block); end
39 |
40 | # source://parallel//lib/parallel.rb#302
41 | def map_with_index(array, options = T.unsafe(nil), &block); end
42 |
43 | # source://parallel//lib/parallel.rb#310
44 | def worker_number; end
45 |
46 | # TODO: this does not work when doing threads in forks, so should remove and yield the number instead if needed
47 | #
48 | # source://parallel//lib/parallel.rb#315
49 | def worker_number=(worker_num); end
50 |
51 | private
52 |
53 | # source://parallel//lib/parallel.rb#321
54 | def add_progress_bar!(job_factory, options); end
55 |
56 | # source://parallel//lib/parallel.rb#584
57 | def call_with_index(item, index, options, &block); end
58 |
59 | # source://parallel//lib/parallel.rb#516
60 | def create_workers(job_factory, options, &block); end
61 |
62 | # options is either a Integer or a Hash with :count
63 | #
64 | # source://parallel//lib/parallel.rb#574
65 | def extract_count_from_options(options); end
66 |
67 | # source://parallel//lib/parallel.rb#602
68 | def instrument_finish(item, index, result, options); end
69 |
70 | # source://parallel//lib/parallel.rb#607
71 | def instrument_start(item, index, options); end
72 |
73 | # source://parallel//lib/parallel.rb#550
74 | def process_incoming_jobs(read, write, job_factory, options, &block); end
75 |
76 | # source://parallel//lib/parallel.rb#504
77 | def replace_worker(job_factory, workers, index, options, blk); end
78 |
79 | # source://parallel//lib/parallel.rb#595
80 | def with_instrumentation(item, index, options); end
81 |
82 | # source://parallel//lib/parallel.rb#346
83 | def work_direct(job_factory, options, &block); end
84 |
85 | # source://parallel//lib/parallel.rb#456
86 | def work_in_processes(job_factory, options, &blk); end
87 |
88 | # source://parallel//lib/parallel.rb#390
89 | def work_in_ractors(job_factory, options); end
90 |
91 | # source://parallel//lib/parallel.rb#365
92 | def work_in_threads(job_factory, options, &block); end
93 |
94 | # source://parallel//lib/parallel.rb#524
95 | def worker(job_factory, options, &block); end
96 | end
97 | end
98 |
99 | # source://parallel//lib/parallel.rb#14
100 | class Parallel::Break < ::StandardError
101 | # @return [Break] a new instance of Break
102 | #
103 | # source://parallel//lib/parallel.rb#17
104 | def initialize(value = T.unsafe(nil)); end
105 |
106 | # Returns the value of attribute value.
107 | #
108 | # source://parallel//lib/parallel.rb#15
109 | def value; end
110 | end
111 |
112 | # source://parallel//lib/parallel.rb#11
113 | class Parallel::DeadWorker < ::StandardError; end
114 |
115 | # source://parallel//lib/parallel.rb#35
116 | class Parallel::ExceptionWrapper
117 | # @return [ExceptionWrapper] a new instance of ExceptionWrapper
118 | #
119 | # source://parallel//lib/parallel.rb#38
120 | def initialize(exception); end
121 |
122 | # Returns the value of attribute exception.
123 | #
124 | # source://parallel//lib/parallel.rb#36
125 | def exception; end
126 | end
127 |
128 | # source://parallel//lib/parallel.rb#101
129 | class Parallel::JobFactory
130 | # @return [JobFactory] a new instance of JobFactory
131 | #
132 | # source://parallel//lib/parallel.rb#102
133 | def initialize(source, mutex); end
134 |
135 | # source://parallel//lib/parallel.rb#110
136 | def next; end
137 |
138 | # generate item that is sent to workers
139 | # just index is faster + less likely to blow up with unserializable errors
140 | #
141 | # source://parallel//lib/parallel.rb#139
142 | def pack(item, index); end
143 |
144 | # source://parallel//lib/parallel.rb#129
145 | def size; end
146 |
147 | # unpack item that is sent to workers
148 | #
149 | # source://parallel//lib/parallel.rb#144
150 | def unpack(data); end
151 |
152 | private
153 |
154 | # @return [Boolean]
155 | #
156 | # source://parallel//lib/parallel.rb#150
157 | def producer?; end
158 |
159 | # source://parallel//lib/parallel.rb#154
160 | def queue_wrapper(array); end
161 | end
162 |
163 | # source://parallel//lib/parallel.rb#23
164 | class Parallel::Kill < ::Parallel::Break; end
165 |
166 | # TODO: inline this method into parallel.rb and kill physical_processor_count in next major release
167 | #
168 | # source://parallel//lib/parallel/processor_count.rb#4
169 | module Parallel::ProcessorCount
170 | # Number of physical processor cores on the current system.
171 | #
172 | # source://parallel//lib/parallel/processor_count.rb#12
173 | def physical_processor_count; end
174 |
175 | # Number of processors seen by the OS, used for process scheduling
176 | #
177 | # source://parallel//lib/parallel/processor_count.rb#6
178 | def processor_count; end
179 | end
180 |
181 | # source://parallel//lib/parallel.rb#9
182 | Parallel::Stop = T.let(T.unsafe(nil), Object)
183 |
184 | # source://parallel//lib/parallel.rb#26
185 | class Parallel::UndumpableException < ::StandardError
186 | # @return [UndumpableException] a new instance of UndumpableException
187 | #
188 | # source://parallel//lib/parallel.rb#29
189 | def initialize(original); end
190 |
191 | # Returns the value of attribute backtrace.
192 | #
193 | # source://parallel//lib/parallel.rb#27
194 | def backtrace; end
195 | end
196 |
197 | # source://parallel//lib/parallel.rb#159
198 | class Parallel::UserInterruptHandler
199 | class << self
200 | # source://parallel//lib/parallel.rb#184
201 | def kill(thing); end
202 |
203 | # kill all these pids or threads if user presses Ctrl+c
204 | #
205 | # source://parallel//lib/parallel.rb#164
206 | def kill_on_ctrl_c(pids, options); end
207 |
208 | private
209 |
210 | # source://parallel//lib/parallel.rb#208
211 | def restore_interrupt(old, signal); end
212 |
213 | # source://parallel//lib/parallel.rb#193
214 | def trap_interrupt(signal); end
215 | end
216 | end
217 |
218 | # source://parallel//lib/parallel.rb#160
219 | Parallel::UserInterruptHandler::INTERRUPT_SIGNAL = T.let(T.unsafe(nil), Symbol)
220 |
221 | # source://parallel//lib/parallel/version.rb#3
222 | Parallel::VERSION = T.let(T.unsafe(nil), String)
223 |
224 | # source://parallel//lib/parallel/version.rb#3
225 | Parallel::Version = T.let(T.unsafe(nil), String)
226 |
227 | # source://parallel//lib/parallel.rb#54
228 | class Parallel::Worker
229 | # @return [Worker] a new instance of Worker
230 | #
231 | # source://parallel//lib/parallel.rb#58
232 | def initialize(read, write, pid); end
233 |
234 | # might be passed to started_processes and simultaneously closed by another thread
235 | # when running in isolation mode, so we have to check if it is closed before closing
236 | #
237 | # source://parallel//lib/parallel.rb#71
238 | def close_pipes; end
239 |
240 | # Returns the value of attribute pid.
241 | #
242 | # source://parallel//lib/parallel.rb#55
243 | def pid; end
244 |
245 | # Returns the value of attribute read.
246 | #
247 | # source://parallel//lib/parallel.rb#55
248 | def read; end
249 |
250 | # source://parallel//lib/parallel.rb#64
251 | def stop; end
252 |
253 | # Returns the value of attribute thread.
254 | #
255 | # source://parallel//lib/parallel.rb#56
256 | def thread; end
257 |
258 | # Sets the attribute thread
259 | #
260 | # @param value the value to set the attribute thread to.
261 | #
262 | # source://parallel//lib/parallel.rb#56
263 | def thread=(_arg0); end
264 |
265 | # source://parallel//lib/parallel.rb#76
266 | def work(data); end
267 |
268 | # Returns the value of attribute write.
269 | #
270 | # source://parallel//lib/parallel.rb#55
271 | def write; end
272 |
273 | private
274 |
275 | # source://parallel//lib/parallel.rb#94
276 | def wait; end
277 | end
278 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://combohr.com)
2 |
3 |
4 | Table of Contents
5 |
6 |
7 | 1. [Installation](#installation)
8 | 1. [Configuration](#configuration)
9 | 1. [Events](#events)
10 | 1. [Database Operations](#database-operations)
11 | 1. [Merging Changesets](#merging-changesets)
12 | 1. [Push!](#push)
13 | 1. [Testing](#testing-)
14 | 1. [Sorbet](#sorbet)
15 | 1. [Example](#example)
16 | 1. [But why all these classes?](#but-why-all-these-classes)
17 |
18 |
19 |
20 |
21 | # Changeset
22 |
23 | The changeset contains all database operations and events of a command.
24 |
25 | The point of the Changeset is to delay the moment you persist until the end of a chain of method calls.
26 |
27 | The main reasons are:
28 | - use the shortest database transactions possible (holding transactions leads to many errors, nested transactions as well)
29 | - trigger necessary events once all data is persisted (jobs fail if started before transaction ends)
30 |
31 | Whatever the way you organize your code (plain methods, service objects...), you can leverage the changesets.
32 |
33 | ---
34 |
35 | It helped us solve complex use cases at [Combo](https://combohr.com) where some workflows overlapped.
36 |
37 | We had *long running transactions*, *duplicated workers* and needed a **simple**, **testable** yet **robust** way to write our persistence layer code.
38 |
39 | ## Installation
40 |
41 | ```ruby
42 | git_source(:github) { |project| File.join("https://github.com", "#{project}.git") }
43 | gem "changeset", github: "apneadiving/changeset"
44 | ```
45 |
46 | ## Configuration
47 | One configuration is needed to use the gem: tell it how to use database transactions:
48 |
49 | ```ruby
50 | Changeset.configure do |config|
51 | config.db_transaction_wrapper = ->(&block) {
52 | ApplicationRecord.transaction do
53 | block.call
54 | end
55 | }
56 | end
57 | ```
58 |
59 | ## Events
60 |
61 | They are meant to trigger only async processes:
62 | - background jobs
63 | - AMQP
64 | - KAFKA
65 | - ...
66 |
67 | Events have to be registered in a class to be used later:
68 |
69 | ```ruby
70 | class EventsCatalog
71 | KNOWN_EVENTS = [:planning_updated]
72 | def dispatch(event)
73 | send(event.name, event)
74 | end
75 |
76 | def known_event?(event_name)
77 | KNOWN_EVENTS.include?(event_name)
78 | end
79 |
80 | private
81 |
82 | def planning_updated(event)
83 | # Trigger workers or any async processes.
84 | # One event can mean many workers etc, your call.
85 | # From here you can use event.payload
86 | end
87 | end
88 | ```
89 |
90 | There are two ways to add events to a changeset:
91 | ```ruby
92 | changeset = Changeset.new(EventsCatalog)
93 | # if you know all params at the time you add the event:
94 | changeset.add_event(
95 | :planning_udpated,
96 | { week: "2022W47" }
97 | )
98 | # if you do not know all params at the time you add the event,
99 | # but know it will be populated once database operations are committed
100 | changeset.add_event(
101 | :planning_udpated,
102 | -> { { week: some_object.week_identifier } }
103 | )
104 | ```
105 |
106 | For now there is a dedup mechanism to avoid same events to be dispatched several times. Indeed some actions may add same events in their own context and afeter traversing them all we know it is not necessary.
107 |
108 | The unicity is based on:
109 | - the event catalog class name
110 | - the name of the event
111 | - the payload of the event
112 |
113 | ## Database Operations
114 |
115 | They are meant to be objects containing the relevant logic to call the database and commit persistence operations.
116 | These classes must match the PersistenceInterface: respond to `call`.
117 |
118 | You can create any depending on your needs: create, update, delete, bulk upsert...
119 |
120 | A very basic example is:
121 | ```ruby
122 | class BasicPersistenceHandler
123 | def initialize(active_record_object)
124 | @active_record_object = active_record_object
125 | end
126 |
127 | def call
128 | @active_record_object.save!
129 | end
130 | end
131 | ```
132 |
133 | You can then add database operations to the changeset.
134 | ```ruby
135 | changeset = Changeset.new # notice we didnt pass an event catalog because we wont use events
136 |
137 | user = User.new(params)
138 |
139 | changeset.add_db_operation(
140 | BasicPersistenceHandler.new(user)
141 | )
142 | ```
143 |
144 | If you do not need them to be reused, just use a lambda:
145 | ```
146 | user = User.new(params)
147 |
148 | changeset.add_db_operation(
149 | -> { user.save! }
150 | )
151 | ```
152 |
153 | Database operations will then be commited in the order they were added to the changeset.
154 |
155 | ## Merging changesets
156 |
157 | The very point of changesets is they can be merged.
158 |
159 | On merge:
160 | - parent changeset concatenates all db operations of its child
161 | - parent changeset merges all events from its child
162 |
163 | ```ruby
164 | parent_changeset = Changeset.new(EventsCatalog)
165 | parent_changeset
166 | .add_db_operations(
167 | db_operation1,
168 | db_operation2
169 | )
170 | .add_event(:planning_updated, { week: "2022W47" })
171 |
172 | child_changeset = Changeset.new(EventsCatalog)
173 | .add_db_operations(
174 | db_operation3,
175 | db_operation4
176 | )
177 | .add_event(:planning_updated, { week: "2022W47" })
178 | .add_event(:planning_updated, { week: "2022W48" })
179 |
180 | parent_changeset.merge_child(child_changeset)
181 |
182 | parent_changeset
183 | .add_db_operation(
184 | db_operation5
185 | )
186 |
187 | # - db operations will be in order 1, 2, 3, 4, 5
188 | # - only one planning_updated event will be dispatched with param {week: "2022W47"}
189 | # - only one planning_updated event will be dispatched with param {week: "2022W48"}
190 | ```
191 |
192 | ## Push!
193 |
194 | At the end of the calls chain, it is the appropriate time to persist data and trigger events:
195 |
196 | ```ruby
197 | changeset.push!
198 | ```
199 |
200 | This will:
201 | - persist all database operations in a single transaction
202 | - then trigger all events (outside the transaction)
203 |
204 | ## Testing ⚡
205 |
206 | A very convenient aspect of using changesets in you can run multiple scenarios without touching the database.
207 |
208 | In the end you can compare the actual changeset you get against your expected one.
209 |
210 | This requires to use real classes for persistence and implement `==` in these. You cannot really get procs to compare for equality.
211 |
212 | ## Sorbet
213 |
214 | This gem is typed with Sorbet and contains rbi definitions.
215 |
216 | ## Example
217 |
218 | Completely inspired from a discussion on Twitter you can find here: https://twitter.com/davetron5000/status/1575512016504164352
219 |
220 | We need to be fault tolerant in cases like below:
221 |
222 | ```ruby
223 | def charge(customer, amount_cents)
224 | # These two create! calls must
225 | # either both succeed or both fail
226 | invoice = Invoice.create!(
227 | customer: customer,
228 | amount_cents: amount_cents,
229 | )
230 | charge = Charge.create!(
231 | invoice: invoice,
232 | amount_cents: amount_cents,
233 | )
234 | ChargeJob.perform_async(charge.id)
235 | end
236 | ```
237 |
238 | It generally goes down to adding a transaction:
239 |
240 | ```ruby
241 | def charge(customer, amount_cents)
242 | ActiveRecord::Base.transaction do
243 | invoice = Invoice.create!(
244 | customer: customer,
245 | amount_cents: amount_cents,
246 | )
247 | charge = Charge.create!(
248 | invoice: invoice,
249 | amount_cents: amount_cents,
250 | )
251 | end
252 | # we can argue whether or not this should go inside the transaction...
253 | ChargeJob.perform_async(charge.id)
254 | end
255 | ```
256 |
257 | You soon need to reuse this method in a larger context, and you now need to nest transactions:
258 |
259 | ```ruby
260 | def appointment_attended(appointment)
261 | ActiveRecord::Base.transaction(requires_new: true) do
262 | copay_cents = appointment.service.copay_cents
263 | charge = charge(appointment.customer, copay_cents)
264 |
265 | # create_insurance_claim would create yet another nested transaction
266 | insurance_claim = create_insurance_claim(appointment, copay: charge)
267 | end
268 | # again, triggering the job here is maybe not the best option
269 | SubmitToInsuranceJob.perform_async(insurance_claim.id)
270 | end
271 |
272 | def charge(customer, amount_cents)
273 | ActiveRecord::Base.transaction(requires_new: true) do
274 | invoice = Invoice.create!(
275 | customer: customer,
276 | amount_cents: amount_cents,
277 | )
278 | charge = Charge.create!(
279 | invoice: invoice,
280 | amount_cents: amount_cents,
281 | )
282 | end
283 | # we can argue whether or not this should go inside the transaction...
284 | ChargeJob.perform_async(charge.id)
285 | end
286 | ```
287 |
288 | As you can tell, we are putting more and more weight on the transation.
289 | Holding a transaction takes a huge toll on your database opening the door to multiple weird errors.
290 | The most common ones being:
291 | - timeouts
292 | - locking errors
293 | - background job failing because they are unable to find database records (they can actually be trigerred before the transaction ended)
294 |
295 | ---
296 |
297 | Now with the Changeset:
298 |
299 | ```ruby
300 | # we need a catalog
301 | class EventsCatalog
302 | KNOWN_EVENTS = [:customer_charged, :insurance_claim_created]
303 | def dispatch(event)
304 | send(event.name, event)
305 | end
306 |
307 | def known_event?(event_name)
308 | KNOWN_EVENTS.include?(event_name)
309 | end
310 |
311 | private
312 |
313 | def customer_charged(event)
314 | ChargeJob.perform_async(event.payload[:id])
315 | end
316 |
317 | def insurance_claim_created(event)
318 | SubmitToInsuranceJob.perform_async(event.payload[:id])
319 | end
320 | end
321 |
322 | def appointment_attended(appointment)
323 | Changeset.new(EventsCatalog).yield_self do |changeset|
324 | copay_cents = appointment.service.copay_cents
325 |
326 | new_charge, charge_changeset = charge(appointment.customer, copay_cents)
327 | changeset.merge_child(charge_changeset)
328 |
329 | insurance_claim, insurance_claim_changeset = create_insurance_claim(appointment, copay: new_charge)
330 | changeset.merge_child(insurance_claim_changeset)
331 |
332 | changeset.add_event(
333 | :insurance_claim_created,
334 | -> { { id: insurance_claim.id } }
335 | )
336 | end
337 | end
338 |
339 | def charge(customer, amount_cents)
340 | Changeset.new(EventsCatalog).yield_self do |changeset|
341 | invoice = Invoice.new(
342 | customer: customer,
343 | amount_cents: amount_cents
344 | )
345 | charge = Charge.new(
346 | invoice: invoice,
347 | amount_cents: amount_cents
348 | )
349 |
350 | changeset
351 | .add_db_operations(
352 | -> { invoice.save! },
353 | -> { charge.save! }
354 | )
355 | .add_event(
356 | :customer_charged,
357 | -> { { id: charge.id } }
358 | )
359 |
360 | [charge, changeset]
361 | end
362 | end
363 |
364 | # usage
365 | changeset = appointment_attended(appointment)
366 | changeset.push!
367 | ```
368 |
369 | One database transaction, workers triggered at the appropriate time.
370 |
371 | ## But why all these classes?
372 |
373 | I realized this kind of structure was necessary through my job at combohr.com, where we heavily use Domain Driven Design.
374 |
375 | Because we do not use ActiveRecord within the domain (no objects, no query, no nothing), we need a way to bridge back from our own Ruby object to the persistence layer. This is where Persistence classes came into play.
376 |
377 | Anyway it is a good habit to have a facade to decouple your intent and the actual implementation.
378 |
--------------------------------------------------------------------------------
/sorbet/rbi/gems/yard-sorbet@0.7.0.rbi:
--------------------------------------------------------------------------------
1 | # typed: true
2 |
3 | # DO NOT EDIT MANUALLY
4 | # This is an autogenerated file for types exported from the `yard-sorbet` gem.
5 | # Please instead update this file by running `bin/tapioca gem yard-sorbet`.
6 |
7 | class YARD::Handlers::Ruby::ClassHandler < ::YARD::Handlers::Ruby::Base
8 | include ::YARDSorbet::Handlers::StructClassHandler
9 | end
10 |
11 | # Types are documentation
12 | #
13 | # source://yard-sorbet//lib/yard-sorbet/version.rb#5
14 | module YARDSorbet; end
15 |
16 | # Extract & re-add directives to a docstring
17 | #
18 | # source://yard-sorbet//lib/yard-sorbet/directives.rb#6
19 | module YARDSorbet::Directives
20 | class << self
21 | # source://yard-sorbet//lib/yard-sorbet/directives.rb#21
22 | sig { params(docstring: ::String, directives: T::Array[::String]).void }
23 | def add_directives(docstring, directives); end
24 |
25 | # source://yard-sorbet//lib/yard-sorbet/directives.rb#10
26 | sig { params(docstring: T.nilable(::String)).returns([::YARD::Docstring, T::Array[::String]]) }
27 | def extract_directives(docstring); end
28 | end
29 | end
30 |
31 | # Custom YARD Handlers
32 | #
33 | # @see https://rubydoc.info/gems/yard/YARD/Handlers/Base YARD Base Handler documentation
34 | #
35 | # source://yard-sorbet//lib/yard-sorbet/handlers.rb#7
36 | module YARDSorbet::Handlers; end
37 |
38 | # Apllies an `@abstract` tag to `abstract!`/`interface!` modules (if not alerady present).
39 | #
40 | # source://yard-sorbet//lib/yard-sorbet/handlers/abstract_dsl_handler.rb#7
41 | class YARDSorbet::Handlers::AbstractDSLHandler < ::YARD::Handlers::Ruby::Base
42 | # source://yard-sorbet//lib/yard-sorbet/handlers/abstract_dsl_handler.rb#21
43 | sig { void }
44 | def process; end
45 | end
46 |
47 | # Extra text for class namespaces
48 | #
49 | # source://yard-sorbet//lib/yard-sorbet/handlers/abstract_dsl_handler.rb#18
50 | YARDSorbet::Handlers::AbstractDSLHandler::CLASS_TAG_TEXT = T.let(T.unsafe(nil), String)
51 |
52 | # The text accompanying the `@abstract` tag.
53 | #
54 | # @see https://github.com/lsegal/yard/blob/main/templates/default/docstring/html/abstract.erb The `@abstract` tag template
55 | #
56 | # source://yard-sorbet//lib/yard-sorbet/handlers/abstract_dsl_handler.rb#16
57 | YARDSorbet::Handlers::AbstractDSLHandler::TAG_TEXT = T.let(T.unsafe(nil), String)
58 |
59 | # Handle `enums` calls, registering enum values as constants
60 | #
61 | # source://yard-sorbet//lib/yard-sorbet/handlers/enums_handler.rb#7
62 | class YARDSorbet::Handlers::EnumsHandler < ::YARD::Handlers::Ruby::Base
63 | # source://yard-sorbet//lib/yard-sorbet/handlers/enums_handler.rb#14
64 | sig { void }
65 | def process; end
66 |
67 | private
68 |
69 | # source://yard-sorbet//lib/yard-sorbet/handlers/enums_handler.rb#29
70 | sig { params(node: ::YARD::Parser::Ruby::AstNode).returns(T::Boolean) }
71 | def const_assign_node?(node); end
72 | end
73 |
74 | # Extends any modules included via `mixes_in_class_methods`
75 | #
76 | # @see https://sorbet.org/docs/abstract#interfaces-and-the-included-hook Sorbet `mixes_in_class_methods` documentation
77 | #
78 | # source://yard-sorbet//lib/yard-sorbet/handlers/include_handler.rb#9
79 | class YARDSorbet::Handlers::IncludeHandler < ::YARD::Handlers::Ruby::Base
80 | # source://yard-sorbet//lib/yard-sorbet/handlers/include_handler.rb#16
81 | sig { void }
82 | def process; end
83 |
84 | private
85 |
86 | # source://yard-sorbet//lib/yard-sorbet/handlers/include_handler.rb#30
87 | sig { returns(::YARD::CodeObjects::NamespaceObject) }
88 | def included_in; end
89 | end
90 |
91 | # Tracks modules that invoke `mixes_in_class_methods` for use in {IncludeHandler}
92 | #
93 | # @see https://sorbet.org/docs/abstract#interfaces-and-the-included-hook Sorbet `mixes_in_class_methods` documentation
94 | #
95 | # source://yard-sorbet//lib/yard-sorbet/handlers/mixes_in_class_methods_handler.rb#9
96 | class YARDSorbet::Handlers::MixesInClassMethodsHandler < ::YARD::Handlers::Ruby::Base
97 | # source://yard-sorbet//lib/yard-sorbet/handlers/mixes_in_class_methods_handler.rb#23
98 | sig { void }
99 | def process; end
100 |
101 | class << self
102 | # source://yard-sorbet//lib/yard-sorbet/handlers/mixes_in_class_methods_handler.rb#18
103 | sig { params(code_obj: ::String).returns(T.nilable(::String)) }
104 | def mixed_in_class_methods(code_obj); end
105 | end
106 | end
107 |
108 | # A YARD Handler for Sorbet type declarations
109 | #
110 | # source://yard-sorbet//lib/yard-sorbet/handlers/sig_handler.rb#7
111 | class YARDSorbet::Handlers::SigHandler < ::YARD::Handlers::Ruby::Base
112 | # Swap the method definition docstring and the sig docstring.
113 | # Parse relevant parts of the `sig` and include them as well.
114 | #
115 | # source://yard-sorbet//lib/yard-sorbet/handlers/sig_handler.rb#20
116 | sig { void }
117 | def process; end
118 |
119 | private
120 |
121 | # source://yard-sorbet//lib/yard-sorbet/handlers/sig_handler.rb#52
122 | sig do
123 | params(
124 | method_node: ::YARD::Parser::Ruby::AstNode,
125 | node: ::YARD::Parser::Ruby::AstNode,
126 | docstring: ::YARD::Docstring
127 | ).void
128 | end
129 | def parse_params(method_node, node, docstring); end
130 |
131 | # source://yard-sorbet//lib/yard-sorbet/handlers/sig_handler.rb#64
132 | sig { params(node: ::YARD::Parser::Ruby::AstNode, docstring: ::YARD::Docstring).void }
133 | def parse_return(node, docstring); end
134 |
135 | # source://yard-sorbet//lib/yard-sorbet/handlers/sig_handler.rb#32
136 | sig { params(method_node: ::YARD::Parser::Ruby::AstNode, docstring: ::YARD::Docstring).void }
137 | def parse_sig(method_node, docstring); end
138 | end
139 |
140 | # These node types attached to sigs represent attr_* declarations
141 | #
142 | # source://yard-sorbet//lib/yard-sorbet/handlers/sig_handler.rb#14
143 | YARDSorbet::Handlers::SigHandler::ATTR_NODE_TYPES = T.let(T.unsafe(nil), Array)
144 |
145 | # Class-level handler that folds all `const` and `prop` declarations into the constructor documentation
146 | # this needs to be injected as a module otherwise the default Class handler will overwrite documentation
147 | #
148 | # @note this module is included in `YARD::Handlers::Ruby::ClassHandler`
149 | #
150 | # source://yard-sorbet//lib/yard-sorbet/handlers/struct_class_handler.rb#10
151 | module YARDSorbet::Handlers::StructClassHandler
152 | # source://yard-sorbet//lib/yard-sorbet/handlers/struct_class_handler.rb#14
153 | sig { void }
154 | def process; end
155 |
156 | private
157 |
158 | # source://yard-sorbet//lib/yard-sorbet/handlers/struct_class_handler.rb#50
159 | sig do
160 | params(
161 | object: ::YARD::CodeObjects::MethodObject,
162 | props: T::Array[::YARDSorbet::TStructProp],
163 | docstring: ::YARD::Docstring,
164 | directives: T::Array[::String]
165 | ).void
166 | end
167 | def decorate_t_struct_init(object, props, docstring, directives); end
168 |
169 | # Create a virtual `initialize` method with all the `prop`/`const` arguments
170 | #
171 | # source://yard-sorbet//lib/yard-sorbet/handlers/struct_class_handler.rb#30
172 | sig { params(props: T::Array[::YARDSorbet::TStructProp], class_ns: ::YARD::CodeObjects::ClassObject).void }
173 | def process_t_struct_props(props, class_ns); end
174 |
175 | # source://yard-sorbet//lib/yard-sorbet/handlers/struct_class_handler.rb#60
176 | sig { params(props: T::Array[::YARDSorbet::TStructProp]).returns(T::Array[[::String, T.nilable(::String)]]) }
177 | def to_object_parameters(props); end
178 | end
179 |
180 | # Handles all `const`/`prop` calls, creating accessor methods, and compiles them for later usage at the class level
181 | # in creating a constructor
182 | #
183 | # source://yard-sorbet//lib/yard-sorbet/handlers/struct_prop_handler.rb#8
184 | class YARDSorbet::Handlers::StructPropHandler < ::YARD::Handlers::Ruby::Base
185 | # source://yard-sorbet//lib/yard-sorbet/handlers/struct_prop_handler.rb#15
186 | sig { void }
187 | def process; end
188 |
189 | private
190 |
191 | # Add the source and docstring to the method object
192 | #
193 | # source://yard-sorbet//lib/yard-sorbet/handlers/struct_prop_handler.rb#28
194 | sig { params(object: ::YARD::CodeObjects::MethodObject, prop: ::YARDSorbet::TStructProp).void }
195 | def decorate_object(object, prop); end
196 |
197 | # Get the default prop value
198 | #
199 | # source://yard-sorbet//lib/yard-sorbet/handlers/struct_prop_handler.rb#39
200 | sig { returns(T.nilable(::String)) }
201 | def default_value; end
202 |
203 | # source://yard-sorbet//lib/yard-sorbet/handlers/struct_prop_handler.rb#44
204 | sig { params(name: ::String).returns(::YARDSorbet::TStructProp) }
205 | def make_prop(name); end
206 |
207 | # Register the field explicitly as an attribute.
208 | # While `const` attributes are immutable, `prop` attributes may be reassigned.
209 | #
210 | # source://yard-sorbet//lib/yard-sorbet/handlers/struct_prop_handler.rb#57
211 | sig { params(object: ::YARD::CodeObjects::MethodObject, name: ::String).void }
212 | def register_attrs(object, name); end
213 |
214 | # Store the prop for use in the constructor definition
215 | #
216 | # source://yard-sorbet//lib/yard-sorbet/handlers/struct_prop_handler.rb#65
217 | sig { params(prop: ::YARDSorbet::TStructProp).void }
218 | def update_state(prop); end
219 | end
220 |
221 | # Helper methods for working with `YARD` AST Nodes
222 | #
223 | # source://yard-sorbet//lib/yard-sorbet/node_utils.rb#6
224 | module YARDSorbet::NodeUtils
225 | class << self
226 | # Traverse AST nodes in breadth-first order
227 | #
228 | # @note This will skip over some node types.
229 | # @yield [YARD::Parser::Ruby::AstNode]
230 | #
231 | # source://yard-sorbet//lib/yard-sorbet/node_utils.rb#22
232 | sig do
233 | params(
234 | node: ::YARD::Parser::Ruby::AstNode,
235 | _blk: T.proc.params(n: ::YARD::Parser::Ruby::AstNode).void
236 | ).void
237 | end
238 | def bfs_traverse(node, &_blk); end
239 |
240 | # Gets the node that a sorbet `sig` can be attached do, bypassing visisbility modifiers and the like
241 | #
242 | # source://yard-sorbet//lib/yard-sorbet/node_utils.rb#34
243 | sig do
244 | params(
245 | node: ::YARD::Parser::Ruby::AstNode
246 | ).returns(T.any(::YARD::Parser::Ruby::MethodCallNode, ::YARD::Parser::Ruby::MethodDefinitionNode))
247 | end
248 | def get_method_node(node); end
249 |
250 | # Find and return the adjacent node (ascending)
251 | #
252 | # @raise [IndexError] if the node does not have an adjacent sibling (ascending)
253 | #
254 | # source://yard-sorbet//lib/yard-sorbet/node_utils.rb#48
255 | sig { params(node: ::YARD::Parser::Ruby::AstNode).returns(::YARD::Parser::Ruby::AstNode) }
256 | def sibling_node(node); end
257 | end
258 | end
259 |
260 | # Command node types that can have type signatures
261 | #
262 | # source://yard-sorbet//lib/yard-sorbet/node_utils.rb#10
263 | YARDSorbet::NodeUtils::ATTRIBUTE_METHODS = T.let(T.unsafe(nil), Array)
264 |
265 | # Skip these method contents during BFS node traversal, they can have their own nested types via `T.Proc`
266 | #
267 | # source://yard-sorbet//lib/yard-sorbet/node_utils.rb#12
268 | YARDSorbet::NodeUtils::SKIP_METHOD_CONTENTS = T.let(T.unsafe(nil), Array)
269 |
270 | # Node types that can have type signatures
271 | #
272 | # source://yard-sorbet//lib/yard-sorbet/node_utils.rb#14
273 | YARDSorbet::NodeUtils::SigableNode = T.type_alias { T.any(::YARD::Parser::Ruby::MethodCallNode, ::YARD::Parser::Ruby::MethodDefinitionNode) }
274 |
275 | # Translate `sig` type syntax to `YARD` type syntax.
276 | #
277 | # source://yard-sorbet//lib/yard-sorbet/sig_to_yard.rb#6
278 | module YARDSorbet::SigToYARD
279 | class << self
280 | # @see https://yardoc.org/types.html
281 | #
282 | # source://yard-sorbet//lib/yard-sorbet/sig_to_yard.rb#21
283 | sig { params(node: ::YARD::Parser::Ruby::AstNode).returns(T::Array[::String]) }
284 | def convert(node); end
285 |
286 | private
287 |
288 | # source://yard-sorbet//lib/yard-sorbet/sig_to_yard.rb#54
289 | sig { params(node: ::YARD::Parser::Ruby::AstNode).returns(::String) }
290 | def build_generic_type(node); end
291 |
292 | # source://yard-sorbet//lib/yard-sorbet/sig_to_yard.rb#63
293 | sig { params(node: ::YARD::Parser::Ruby::AstNode).returns(T::Array[::String]) }
294 | def convert_aref(node); end
295 |
296 | # source://yard-sorbet//lib/yard-sorbet/sig_to_yard.rb#75
297 | sig { params(node: ::YARD::Parser::Ruby::AstNode).returns([::String]) }
298 | def convert_array(node); end
299 |
300 | # source://yard-sorbet//lib/yard-sorbet/sig_to_yard.rb#83
301 | sig { params(node: ::YARD::Parser::Ruby::MethodCallNode).returns(T::Array[::String]) }
302 | def convert_call(node); end
303 |
304 | # source://yard-sorbet//lib/yard-sorbet/sig_to_yard.rb#88
305 | sig { params(node: ::YARD::Parser::Ruby::AstNode).returns([::String]) }
306 | def convert_collection(node); end
307 |
308 | # source://yard-sorbet//lib/yard-sorbet/sig_to_yard.rb#95
309 | sig { params(node: ::YARD::Parser::Ruby::AstNode).returns([::String]) }
310 | def convert_hash(node); end
311 |
312 | # source://yard-sorbet//lib/yard-sorbet/sig_to_yard.rb#103
313 | sig { params(node: ::YARD::Parser::Ruby::AstNode).returns(T::Array[::String]) }
314 | def convert_list(node); end
315 |
316 | # source://yard-sorbet//lib/yard-sorbet/sig_to_yard.rb#27
317 | sig { params(node: ::YARD::Parser::Ruby::AstNode).returns(T::Array[::String]) }
318 | def convert_node(node); end
319 |
320 | # source://yard-sorbet//lib/yard-sorbet/sig_to_yard.rb#36
321 | sig { params(node: ::YARD::Parser::Ruby::AstNode).returns(T::Array[::String]) }
322 | def convert_node_type(node); end
323 |
324 | # source://yard-sorbet//lib/yard-sorbet/sig_to_yard.rb#108
325 | sig { params(node_source: ::String).returns([::String]) }
326 | def convert_ref(node_source); end
327 |
328 | # source://yard-sorbet//lib/yard-sorbet/sig_to_yard.rb#113
329 | sig { params(node: ::YARD::Parser::Ruby::MethodCallNode).returns(T::Array[::String]) }
330 | def convert_t_method(node); end
331 |
332 | # source://yard-sorbet//lib/yard-sorbet/sig_to_yard.rb#125
333 | sig { params(node: ::YARD::Parser::Ruby::AstNode).returns([::String]) }
334 | def convert_unknown(node); end
335 | end
336 | end
337 |
338 | # source://yard-sorbet//lib/yard-sorbet/sig_to_yard.rb#9
339 | YARDSorbet::SigToYARD::REF_TYPES = T.let(T.unsafe(nil), Hash)
340 |
341 | # Used to store the details of a `T::Struct` `prop` definition
342 | #
343 | # source://yard-sorbet//lib/yard-sorbet/t_struct_prop.rb#6
344 | class YARDSorbet::TStructProp < ::T::Struct
345 | const :default, T.nilable(::String)
346 | const :doc, ::String
347 | const :prop_name, ::String
348 | const :source, ::String
349 | const :types, T::Array[::String]
350 |
351 | class << self
352 | # source://sorbet-runtime/0.5.10346/lib/types/struct.rb#13
353 | def inherited(s); end
354 | end
355 | end
356 |
357 | # Helper methods for working with `YARD` tags
358 | #
359 | # source://yard-sorbet//lib/yard-sorbet/tag_utils.rb#6
360 | module YARDSorbet::TagUtils
361 | class << self
362 | # source://yard-sorbet//lib/yard-sorbet/tag_utils.rb#13
363 | sig do
364 | params(
365 | docstring: ::YARD::Docstring,
366 | tag_name: ::String,
367 | name: T.nilable(::String)
368 | ).returns(T.nilable(::YARD::Tags::Tag))
369 | end
370 | def find_tag(docstring, tag_name, name); end
371 |
372 | # Create or update a `YARD` tag with type information
373 | #
374 | # source://yard-sorbet//lib/yard-sorbet/tag_utils.rb#27
375 | sig do
376 | params(
377 | docstring: ::YARD::Docstring,
378 | tag_name: ::String,
379 | types: T.nilable(T::Array[::String]),
380 | name: T.nilable(::String),
381 | text: ::String
382 | ).void
383 | end
384 | def upsert_tag(docstring, tag_name, types = T.unsafe(nil), name = T.unsafe(nil), text = T.unsafe(nil)); end
385 | end
386 | end
387 |
388 | # {https://rubygems.org/gems/yard-sorbet Version history}
389 | #
390 | # source://yard-sorbet//lib/yard-sorbet/version.rb#7
391 | YARDSorbet::VERSION = T.let(T.unsafe(nil), String)
392 |
--------------------------------------------------------------------------------
/sorbet/rbi/gems/ast@2.4.2.rbi:
--------------------------------------------------------------------------------
1 | # typed: true
2 |
3 | # DO NOT EDIT MANUALLY
4 | # This is an autogenerated file for types exported from the `ast` gem.
5 | # Please instead update this file by running `bin/tapioca gem ast`.
6 |
7 | # {AST} is a library for manipulating abstract syntax trees.
8 | #
9 | # It embraces immutability; each AST node is inherently frozen at
10 | # creation, and updating a child node requires recreating that node
11 | # and its every parent, recursively.
12 | # This is a design choice. It does create some pressure on
13 | # garbage collector, but completely eliminates all concurrency
14 | # and aliasing problems.
15 | #
16 | # See also {AST::Node}, {AST::Processor::Mixin} and {AST::Sexp} for
17 | # additional recommendations and design patterns.
18 | #
19 | # source://ast//lib/ast.rb#13
20 | module AST; end
21 |
22 | # Node is an immutable class, instances of which represent abstract
23 | # syntax tree nodes. It combines semantic information (i.e. anything
24 | # that affects the algorithmic properties of a program) with
25 | # meta-information (line numbers or compiler intermediates).
26 | #
27 | # Notes on inheritance
28 | # ====================
29 | #
30 | # The distinction between semantics and metadata is important. Complete
31 | # semantic information should be contained within just the {#type} and
32 | # {#children} of a Node instance; in other words, if an AST was to be
33 | # stripped of all meta-information, it should remain a valid AST which
34 | # could be successfully processed to yield a result with the same
35 | # algorithmic properties.
36 | #
37 | # Thus, Node should never be inherited in order to define methods which
38 | # affect or return semantic information, such as getters for `class_name`,
39 | # `superclass` and `body` in the case of a hypothetical `ClassNode`. The
40 | # correct solution is to use a generic Node with a {#type} of `:class`
41 | # and three children. See also {Processor} for tips on working with such
42 | # ASTs.
43 | #
44 | # On the other hand, Node can and should be inherited to define
45 | # application-specific metadata (see also {#initialize}) or customize the
46 | # printing format. It is expected that an application would have one or two
47 | # such classes and use them across the entire codebase.
48 | #
49 | # The rationale for this pattern is extensibility and maintainability.
50 | # Unlike static ones, dynamic languages do not require the presence of a
51 | # predefined, rigid structure, nor does it improve dispatch efficiency,
52 | # and while such a structure can certainly be defined, it does not add
53 | # any value but incurs a maintaining cost.
54 | # For example, extending the AST even with a transformation-local
55 | # temporary node type requires making globally visible changes to
56 | # the codebase.
57 | #
58 | # source://ast//lib/ast/node.rb#40
59 | class AST::Node
60 | # Constructs a new instance of Node.
61 | #
62 | # The arguments `type` and `children` are converted with `to_sym` and
63 | # `to_a` respectively. Additionally, the result of converting `children`
64 | # is frozen. While mutating the arguments is generally considered harmful,
65 | # the most common case is to pass an array literal to the constructor. If
66 | # your code does not expect the argument to be frozen, use `#dup`.
67 | #
68 | # The `properties` hash is passed to {#assign_properties}.
69 | #
70 | # @return [Node] a new instance of Node
71 | #
72 | # source://ast//lib/ast/node.rb#72
73 | def initialize(type, children = T.unsafe(nil), properties = T.unsafe(nil)); end
74 |
75 | # Concatenates `array` with `children` and returns the resulting node.
76 | #
77 | # @return [AST::Node]
78 | #
79 | # source://ast//lib/ast/node.rb#168
80 | def +(array); end
81 |
82 | # Appends `element` to `children` and returns the resulting node.
83 | #
84 | # @return [AST::Node]
85 | #
86 | # source://ast//lib/ast/node.rb#177
87 | def <<(element); end
88 |
89 | # Compares `self` to `other`, possibly converting with `to_ast`. Only
90 | # `type` and `children` are compared; metadata is deliberately ignored.
91 | #
92 | # @return [Boolean]
93 | #
94 | # source://ast//lib/ast/node.rb#153
95 | def ==(other); end
96 |
97 | # Appends `element` to `children` and returns the resulting node.
98 | #
99 | # @return [AST::Node]
100 | #
101 | # source://ast//lib/ast/node.rb#177
102 | def append(element); end
103 |
104 | # Returns the children of this node.
105 | # The returned value is frozen.
106 | # The to_a alias is useful for decomposing nodes concisely.
107 | # For example:
108 | #
109 | # node = s(:gasgn, :$foo, s(:integer, 1))
110 | # var_name, value = *node
111 | # p var_name # => :$foo
112 | # p value # => (integer 1)
113 | #
114 | # @return [Array]
115 | #
116 | # source://ast//lib/ast/node.rb#56
117 | def children; end
118 |
119 | # Nodes are already frozen, so there is no harm in returning the
120 | # current node as opposed to initializing from scratch and freezing
121 | # another one.
122 | #
123 | # @return self
124 | #
125 | # source://ast//lib/ast/node.rb#115
126 | def clone; end
127 |
128 | # Concatenates `array` with `children` and returns the resulting node.
129 | #
130 | # @return [AST::Node]
131 | #
132 | # source://ast//lib/ast/node.rb#168
133 | def concat(array); end
134 |
135 | # Enables matching for Node, where type is the first element
136 | # and the children are remaining items.
137 | #
138 | # @return [Array]
139 | #
140 | # source://ast//lib/ast/node.rb#253
141 | def deconstruct; end
142 |
143 | # Nodes are already frozen, so there is no harm in returning the
144 | # current node as opposed to initializing from scratch and freezing
145 | # another one.
146 | #
147 | # @return self
148 | #
149 | # source://ast//lib/ast/node.rb#115
150 | def dup; end
151 |
152 | # Test if other object is equal to
153 | #
154 | # @param other [Object]
155 | # @return [Boolean]
156 | #
157 | # source://ast//lib/ast/node.rb#85
158 | def eql?(other); end
159 |
160 | # Returns the precomputed hash value for this node
161 | #
162 | # @return [Fixnum]
163 | #
164 | # source://ast//lib/ast/node.rb#61
165 | def hash; end
166 |
167 | # Converts `self` to a s-expression ruby string.
168 | # The code return will recreate the node, using the sexp module s()
169 | #
170 | # @param indent [Integer] Base indentation level.
171 | # @return [String]
172 | #
173 | # source://ast//lib/ast/node.rb#211
174 | def inspect(indent = T.unsafe(nil)); end
175 |
176 | # Returns the children of this node.
177 | # The returned value is frozen.
178 | # The to_a alias is useful for decomposing nodes concisely.
179 | # For example:
180 | #
181 | # node = s(:gasgn, :$foo, s(:integer, 1))
182 | # var_name, value = *node
183 | # p var_name # => :$foo
184 | # p value # => (integer 1)
185 | #
186 | # @return [Array]
187 | #
188 | # source://ast//lib/ast/node.rb#56
189 | def to_a; end
190 |
191 | # @return [AST::Node] self
192 | #
193 | # source://ast//lib/ast/node.rb#229
194 | def to_ast; end
195 |
196 | # Converts `self` to a pretty-printed s-expression.
197 | #
198 | # @param indent [Integer] Base indentation level.
199 | # @return [String]
200 | #
201 | # source://ast//lib/ast/node.rb#187
202 | def to_s(indent = T.unsafe(nil)); end
203 |
204 | # Converts `self` to a pretty-printed s-expression.
205 | #
206 | # @param indent [Integer] Base indentation level.
207 | # @return [String]
208 | #
209 | # source://ast//lib/ast/node.rb#187
210 | def to_sexp(indent = T.unsafe(nil)); end
211 |
212 | # Converts `self` to an Array where the first element is the type as a Symbol,
213 | # and subsequent elements are the same representation of its children.
214 | #
215 | # @return [Array]
216 | #
217 | # source://ast//lib/ast/node.rb#237
218 | def to_sexp_array; end
219 |
220 | # Returns the type of this node.
221 | #
222 | # @return [Symbol]
223 | #
224 | # source://ast//lib/ast/node.rb#43
225 | def type; end
226 |
227 | # Returns a new instance of Node where non-nil arguments replace the
228 | # corresponding fields of `self`.
229 | #
230 | # For example, `Node.new(:foo, [ 1, 2 ]).updated(:bar)` would yield
231 | # `(bar 1 2)`, and `Node.new(:foo, [ 1, 2 ]).updated(nil, [])` would
232 | # yield `(foo)`.
233 | #
234 | # If the resulting node would be identical to `self`, does nothing.
235 | #
236 | # @param type [Symbol, nil]
237 | # @param children [Array, nil]
238 | # @param properties [Hash, nil]
239 | # @return [AST::Node]
240 | #
241 | # source://ast//lib/ast/node.rb#133
242 | def updated(type = T.unsafe(nil), children = T.unsafe(nil), properties = T.unsafe(nil)); end
243 |
244 | protected
245 |
246 | # By default, each entry in the `properties` hash is assigned to
247 | # an instance variable in this instance of Node. A subclass should define
248 | # attribute readers for such variables. The values passed in the hash
249 | # are not frozen or whitelisted; such behavior can also be implemented
250 | # by subclassing Node and overriding this method.
251 | #
252 | # @return [nil]
253 | #
254 | # source://ast//lib/ast/node.rb#98
255 | def assign_properties(properties); end
256 |
257 | # Returns `@type` with all underscores replaced by dashes. This allows
258 | # to write symbol literals without quotes in Ruby sources and yet have
259 | # nicely looking s-expressions.
260 | #
261 | # @return [String]
262 | #
263 | # source://ast//lib/ast/node.rb#264
264 | def fancy_type; end
265 |
266 | private
267 |
268 | def original_dup; end
269 | end
270 |
271 | # This class includes {AST::Processor::Mixin}; however, it is
272 | # deprecated, since the module defines all of the behaviors that
273 | # the processor includes. Any new libraries should use
274 | # {AST::Processor::Mixin} instead of subclassing this.
275 | #
276 | # @deprecated Use {AST::Processor::Mixin} instead.
277 | #
278 | # source://ast//lib/ast/processor.rb#8
279 | class AST::Processor
280 | include ::AST::Processor::Mixin
281 | end
282 |
283 | # The processor module is a module which helps transforming one
284 | # AST into another. In a nutshell, the {#process} method accepts
285 | # a {Node} and dispatches it to a handler corresponding to its
286 | # type, and returns a (possibly) updated variant of the node.
287 | #
288 | # The processor module has a set of associated design patterns.
289 | # They are best explained with a concrete example. Let's define a
290 | # simple arithmetic language and an AST format for it:
291 | #
292 | # Terminals (AST nodes which do not have other AST nodes inside):
293 | #
294 | # * `(integer )`,
295 | #
296 | # Nonterminals (AST nodes with other nodes as children):
297 | #
298 | # * `(add )`,
299 | # * `(multiply )`,
300 | # * `(divide )`,
301 | # * `(negate )`,
302 | # * `(store )`: stores value of ``
303 | # into a variable named ``,
304 | # * `(load )`: loads value of a variable named
305 | # ``,
306 | # * `(each ...)`: computes each of the ``s and
307 | # prints the result.
308 | #
309 | # All AST nodes have the same Ruby class, and therefore they don't
310 | # know how to traverse themselves. (A solution which dynamically
311 | # checks the type of children is possible, but is slow and
312 | # error-prone.) So, a class including the module which knows how
313 | # to traverse the entire tree should be defined. Such classes
314 | # have a handler for each nonterminal node which recursively
315 | # processes children nodes:
316 | #
317 | # require 'ast'
318 | #
319 | # class ArithmeticsProcessor
320 | # include AST::Processor::Mixin
321 | # # This method traverses any binary operators such as (add)
322 | # # or (multiply).
323 | # def process_binary_op(node)
324 | # # Children aren't decomposed automatically; it is
325 | # # suggested to use Ruby multiple assignment expansion,
326 | # # as it is very convenient here.
327 | # left_expr, right_expr = *node
328 | #
329 | # # AST::Node#updated won't change node type if nil is
330 | # # passed as a first argument, which allows to reuse the
331 | # # same handler for multiple node types using `alias'
332 | # # (below).
333 | # node.updated(nil, [
334 | # process(left_expr),
335 | # process(right_expr)
336 | # ])
337 | # end
338 | # alias_method :on_add, :process_binary_op
339 | # alias_method :on_multiply, :process_binary_op
340 | # alias_method :on_divide, :process_binary_op
341 | #
342 | # def on_negate(node)
343 | # # It is also possible to use #process_all for more
344 | # # compact code if every child is a Node.
345 | # node.updated(nil, process_all(node))
346 | # end
347 | #
348 | # def on_store(node)
349 | # expr, variable_name = *node
350 | #
351 | # # Note that variable_name is not a Node and thus isn't
352 | # # passed to #process.
353 | # node.updated(nil, [
354 | # process(expr),
355 | # variable_name
356 | # ])
357 | # end
358 | #
359 | # # (load) is effectively a terminal node, and so it does
360 | # # not need an explicit handler, as the following is the
361 | # # default behavior. Essentially, for any nodes that don't
362 | # # have a defined handler, the node remains unchanged.
363 | # def on_load(node)
364 | # nil
365 | # end
366 | #
367 | # def on_each(node)
368 | # node.updated(nil, process_all(node))
369 | # end
370 | # end
371 | #
372 | # Let's test our ArithmeticsProcessor:
373 | #
374 | # include AST::Sexp
375 | # expr = s(:add, s(:integer, 2), s(:integer, 2))
376 | #
377 | # p ArithmeticsProcessor.new.process(expr) == expr # => true
378 | #
379 | # As expected, it does not change anything at all. This isn't
380 | # actually very useful, so let's now define a Calculator, which
381 | # will compute the expression values:
382 | #
383 | # # This Processor folds nonterminal nodes and returns an
384 | # # (integer) terminal node.
385 | # class ArithmeticsCalculator < ArithmeticsProcessor
386 | # def compute_op(node)
387 | # # First, node children are processed and then unpacked
388 | # # to local variables.
389 | # nodes = process_all(node)
390 | #
391 | # if nodes.all? { |node| node.type == :integer }
392 | # # If each of those nodes represents a literal, we can
393 | # # fold this node!
394 | # values = nodes.map { |node| node.children.first }
395 | # AST::Node.new(:integer, [
396 | # yield(values)
397 | # ])
398 | # else
399 | # # Otherwise, we can just leave the current node in the
400 | # # tree and only update it with processed children
401 | # # nodes, which can be partially folded.
402 | # node.updated(nil, nodes)
403 | # end
404 | # end
405 | #
406 | # def on_add(node)
407 | # compute_op(node) { |left, right| left + right }
408 | # end
409 | #
410 | # def on_multiply(node)
411 | # compute_op(node) { |left, right| left * right }
412 | # end
413 | # end
414 | #
415 | # Let's check:
416 | #
417 | # p ArithmeticsCalculator.new.process(expr) # => (integer 4)
418 | #
419 | # Excellent, the calculator works! Now, a careful reader could
420 | # notice that the ArithmeticsCalculator does not know how to
421 | # divide numbers. What if we pass an expression with division to
422 | # it?
423 | #
424 | # expr_with_division = \
425 | # s(:add,
426 | # s(:integer, 1),
427 | # s(:divide,
428 | # s(:add, s(:integer, 8), s(:integer, 4)),
429 | # s(:integer, 3))) # 1 + (8 + 4) / 3
430 | #
431 | # folded_expr_with_division = ArithmeticsCalculator.new.process(expr_with_division)
432 | # p folded_expr_with_division
433 | # # => (add
434 | # # (integer 1)
435 | # # (divide
436 | # # (integer 12)
437 | # # (integer 3)))
438 | #
439 | # As you can see, the expression was folded _partially_: the inner
440 | # `(add)` node which could be computed was folded to
441 | # `(integer 12)`, the `(divide)` node is left as-is because there
442 | # is no computing handler for it, and the root `(add)` node was
443 | # also left as it is because some of its children were not
444 | # literals.
445 | #
446 | # Note that this partial folding is only possible because the
447 | # _data_ format, i.e. the format in which the computed values of
448 | # the nodes are represented, is the same as the AST itself.
449 | #
450 | # Let's extend our ArithmeticsCalculator class further.
451 | #
452 | # class ArithmeticsCalculator
453 | # def on_divide(node)
454 | # compute_op(node) { |left, right| left / right }
455 | # end
456 | #
457 | # def on_negate(node)
458 | # # Note how #compute_op works regardless of the operator
459 | # # arity.
460 | # compute_op(node) { |value| -value }
461 | # end
462 | # end
463 | #
464 | # Now, let's apply our renewed ArithmeticsCalculator to a partial
465 | # result of previous evaluation:
466 | #
467 | # p ArithmeticsCalculator.new.process(expr_with_division) # => (integer 5)
468 | #
469 | # Five! Excellent. This is also pretty much how CRuby 1.8 executed
470 | # its programs.
471 | #
472 | # Now, let's do some automated bug searching. Division by zero is
473 | # an error, right? So if we could detect that someone has divided
474 | # by zero before the program is even run, that could save some
475 | # debugging time.
476 | #
477 | # class DivisionByZeroVerifier < ArithmeticsProcessor
478 | # class VerificationFailure < Exception; end
479 | #
480 | # def on_divide(node)
481 | # # You need to process the children to handle nested divisions
482 | # # such as:
483 | # # (divide
484 | # # (integer 1)
485 | # # (divide (integer 1) (integer 0))
486 | # left, right = process_all(node)
487 | #
488 | # if right.type == :integer &&
489 | # right.children.first == 0
490 | # raise VerificationFailure, "Ouch! This code divides by zero."
491 | # end
492 | # end
493 | #
494 | # def divides_by_zero?(ast)
495 | # process(ast)
496 | # false
497 | # rescue VerificationFailure
498 | # true
499 | # end
500 | # end
501 | #
502 | # nice_expr = \
503 | # s(:divide,
504 | # s(:add, s(:integer, 10), s(:integer, 2)),
505 | # s(:integer, 4))
506 | #
507 | # p DivisionByZeroVerifier.new.divides_by_zero?(nice_expr)
508 | # # => false. Good.
509 | #
510 | # bad_expr = \
511 | # s(:add, s(:integer, 10),
512 | # s(:divide, s(:integer, 1), s(:integer, 0)))
513 | #
514 | # p DivisionByZeroVerifier.new.divides_by_zero?(bad_expr)
515 | # # => true. WHOOPS. DO NOT RUN THIS.
516 | #
517 | # Of course, this won't detect more complex cases... unless you
518 | # use some partial evaluation before! The possibilites are
519 | # endless. Have fun.
520 | #
521 | # source://ast//lib/ast/processor/mixin.rb#240
522 | module AST::Processor::Mixin
523 | # Default handler. Does nothing.
524 | #
525 | # @param node [AST::Node]
526 | # @return [AST::Node, nil]
527 | #
528 | # source://ast//lib/ast/processor/mixin.rb#284
529 | def handler_missing(node); end
530 |
531 | # Dispatches `node`. If a node has type `:foo`, then a handler
532 | # named `on_foo` is invoked with one argument, the `node`; if
533 | # there isn't such a handler, {#handler_missing} is invoked
534 | # with the same argument.
535 | #
536 | # If the handler returns `nil`, `node` is returned; otherwise,
537 | # the return value of the handler is passed along.
538 | #
539 | # @param node [AST::Node, nil]
540 | # @return [AST::Node, nil]
541 | #
542 | # source://ast//lib/ast/processor/mixin.rb#251
543 | def process(node); end
544 |
545 | # {#process}es each node from `nodes` and returns an array of
546 | # results.
547 | #
548 | # @param nodes [Array]
549 | # @return [Array]
550 | #
551 | # source://ast//lib/ast/processor/mixin.rb#274
552 | def process_all(nodes); end
553 | end
554 |
555 | # This simple module is very useful in the cases where one needs
556 | # to define deeply nested ASTs from Ruby code, for example, in
557 | # tests. It should be used like this:
558 | #
559 | # describe YourLanguage::AST do
560 | # include Sexp
561 | #
562 | # it "should correctly parse expressions" do
563 | # YourLanguage.parse("1 + 2 * 3").should ==
564 | # s(:add,
565 | # s(:integer, 1),
566 | # s(:multiply,
567 | # s(:integer, 2),
568 | # s(:integer, 3)))
569 | # end
570 | # end
571 | #
572 | # This way the amount of boilerplate code is greatly reduced.
573 | #
574 | # source://ast//lib/ast/sexp.rb#20
575 | module AST::Sexp
576 | # Creates a {Node} with type `type` and children `children`.
577 | # Note that the resulting node is of the type AST::Node and not a
578 | # subclass.
579 | # This would not pose a problem with comparisons, as {Node#==}
580 | # ignores metadata.
581 | #
582 | # source://ast//lib/ast/sexp.rb#26
583 | def s(type, *children); end
584 | end
585 |
--------------------------------------------------------------------------------
/sorbet/rbi/gems/zeitwerk@2.6.0.rbi:
--------------------------------------------------------------------------------
1 | # typed: true
2 |
3 | # DO NOT EDIT MANUALLY
4 | # This is an autogenerated file for types exported from the `zeitwerk` gem.
5 | # Please instead update this file by running `bin/tapioca gem zeitwerk`.
6 |
7 | # source://zeitwerk//lib/zeitwerk/kernel.rb#3
8 | module Kernel
9 | private
10 |
11 | # source://zeitwerk//lib/zeitwerk/kernel.rb#24
12 | def require(path); end
13 |
14 | class << self
15 | # source://zeitwerk//lib/zeitwerk/kernel.rb#24
16 | def require(path); end
17 | end
18 | end
19 |
20 | # source://zeitwerk//lib/zeitwerk.rb#3
21 | module Zeitwerk
22 | class << self
23 | # This is a dangerous method.
24 | #
25 | # source://zeitwerk//lib/zeitwerk.rb#19
26 | def with_loader; end
27 | end
28 | end
29 |
30 | # source://zeitwerk//lib/zeitwerk/error.rb#4
31 | class Zeitwerk::Error < ::StandardError; end
32 |
33 | # Centralizes the logic for the trace point used to detect the creation of
34 | # explicit namespaces, needed to descend into matching subdirectories right
35 | # after the constant has been defined.
36 | #
37 | # The implementation assumes an explicit namespace is managed by one loader.
38 | # Loaders that reopen namespaces owned by other projects are responsible for
39 | # loading their constant before setup. This is documented.
40 | #
41 | # source://zeitwerk//lib/zeitwerk/explicit_namespace.rb#11
42 | module Zeitwerk::ExplicitNamespace
43 | extend ::Zeitwerk::RealModName
44 |
45 | class << self
46 | # Maps constant paths that correspond to explicit namespaces according to
47 | # the file system, to the loader responsible for them.
48 | #
49 | # @private
50 | #
51 | # source://zeitwerk//lib/zeitwerk/explicit_namespace.rb#20
52 | def cpaths; end
53 |
54 | # @private
55 | #
56 | # source://zeitwerk//lib/zeitwerk/explicit_namespace.rb#24
57 | def mutex; end
58 |
59 | # Asserts `cpath` corresponds to an explicit namespace for which `loader`
60 | # is responsible.
61 | #
62 | # @private
63 | #
64 | # source://zeitwerk//lib/zeitwerk/explicit_namespace.rb#35
65 | def register(cpath, loader); end
66 |
67 | # @private
68 | #
69 | # source://zeitwerk//lib/zeitwerk/explicit_namespace.rb#28
70 | def tracer; end
71 |
72 | # @private
73 | #
74 | # source://zeitwerk//lib/zeitwerk/explicit_namespace.rb#46
75 | def unregister_loader(loader); end
76 |
77 | private
78 |
79 | # source://zeitwerk//lib/zeitwerk/explicit_namespace.rb#54
80 | def disable_tracer_if_unneeded; end
81 |
82 | # source://zeitwerk//lib/zeitwerk/explicit_namespace.rb#61
83 | def tracepoint_class_callback(event); end
84 | end
85 | end
86 |
87 | # source://zeitwerk//lib/zeitwerk/gem_inflector.rb#5
88 | class Zeitwerk::GemInflector < ::Zeitwerk::Inflector
89 | # @return [GemInflector] a new instance of GemInflector
90 | #
91 | # source://zeitwerk//lib/zeitwerk/gem_inflector.rb#6
92 | def initialize(root_file); end
93 |
94 | # source://zeitwerk//lib/zeitwerk/gem_inflector.rb#13
95 | def camelize(basename, abspath); end
96 | end
97 |
98 | # @private
99 | #
100 | # source://zeitwerk//lib/zeitwerk/gem_loader.rb#7
101 | class Zeitwerk::GemLoader < ::Zeitwerk::Loader
102 | # @return [GemLoader] a new instance of GemLoader
103 | #
104 | # source://zeitwerk//lib/zeitwerk/gem_loader.rb#17
105 | def initialize(root_file, warn_on_extra_files:); end
106 |
107 | # source://zeitwerk//lib/zeitwerk/gem_loader.rb#30
108 | def setup; end
109 |
110 | private
111 |
112 | # source://zeitwerk//lib/zeitwerk/gem_loader.rb#38
113 | def warn_on_extra_files; end
114 |
115 | class << self
116 | # @private
117 | #
118 | # source://zeitwerk//lib/zeitwerk/gem_loader.rb#12
119 | def _new(root_file, warn_on_extra_files:); end
120 | end
121 | end
122 |
123 | # source://zeitwerk//lib/zeitwerk/inflector.rb#4
124 | class Zeitwerk::Inflector
125 | # Very basic snake case -> camel case conversion.
126 | #
127 | # inflector = Zeitwerk::Inflector.new
128 | # inflector.camelize("post", ...) # => "Post"
129 | # inflector.camelize("users_controller", ...) # => "UsersController"
130 | # inflector.camelize("api", ...) # => "Api"
131 | #
132 | # Takes into account hard-coded mappings configured with `inflect`.
133 | #
134 | # source://zeitwerk//lib/zeitwerk/inflector.rb#15
135 | def camelize(basename, _abspath); end
136 |
137 | # Configures hard-coded inflections:
138 | #
139 | # inflector = Zeitwerk::Inflector.new
140 | # inflector.inflect(
141 | # "html_parser" => "HTMLParser",
142 | # "mysql_adapter" => "MySQLAdapter"
143 | # )
144 | #
145 | # inflector.camelize("html_parser", abspath) # => "HTMLParser"
146 | # inflector.camelize("mysql_adapter", abspath) # => "MySQLAdapter"
147 | # inflector.camelize("users_controller", abspath) # => "UsersController"
148 | #
149 | # source://zeitwerk//lib/zeitwerk/inflector.rb#32
150 | def inflect(inflections); end
151 |
152 | private
153 |
154 | # Hard-coded basename to constant name user maps that override the default
155 | # inflection logic.
156 | #
157 | # source://zeitwerk//lib/zeitwerk/inflector.rb#42
158 | def overrides; end
159 | end
160 |
161 | # source://zeitwerk//lib/zeitwerk/loader.rb#6
162 | class Zeitwerk::Loader
163 | include ::Zeitwerk::RealModName
164 | include ::Zeitwerk::Loader::Callbacks
165 | include ::Zeitwerk::Loader::Helpers
166 | include ::Zeitwerk::Loader::Config
167 |
168 | # @return [Loader] a new instance of Loader
169 | #
170 | # source://zeitwerk//lib/zeitwerk/loader.rb#83
171 | def initialize; end
172 |
173 | # We keep track of autoloaded directories to remove them from the registry
174 | # at the end of eager loading.
175 | #
176 | # Files are removed as they are autoloaded, but directories need to wait due
177 | # to concurrency (see why in Zeitwerk::Loader::Callbacks#on_dir_autoloaded).
178 | #
179 | # @private
180 | #
181 | # source://zeitwerk//lib/zeitwerk/loader.rb#39
182 | def autoloaded_dirs; end
183 |
184 | # Maps absolute paths for which an autoload has been set ---and not
185 | # executed--- to their corresponding parent class or module and constant
186 | # name.
187 | #
188 | # "/Users/fxn/blog/app/models/user.rb" => [Object, :User],
189 | # "/Users/fxn/blog/app/models/hotel/pricing.rb" => [Hotel, :Pricing]
190 | # ...
191 | #
192 | # @private
193 | #
194 | # source://zeitwerk//lib/zeitwerk/loader.rb#29
195 | def autoloads; end
196 |
197 | # Eager loads all files in the root directories, recursively. Files do not
198 | # need to be in `$LOAD_PATH`, absolute file names are used. Ignored files
199 | # are not eager loaded. You can opt-out specifically in specific files and
200 | # directories with `do_not_eager_load`, and that can be overridden passing
201 | # `force: true`.
202 | #
203 | # source://zeitwerk//lib/zeitwerk/loader.rb#218
204 | def eager_load(force: T.unsafe(nil)); end
205 |
206 | # Maps constant paths of namespaces to arrays of corresponding directories.
207 | #
208 | # For example, given this mapping:
209 | #
210 | # "Admin" => [
211 | # "/Users/fxn/blog/app/controllers/admin",
212 | # "/Users/fxn/blog/app/models/admin",
213 | # ...
214 | # ]
215 | #
216 | # when `Admin` gets defined we know that it plays the role of a namespace and
217 | # that its children are spread over those directories. We'll visit them to set
218 | # up the corresponding autoloads.
219 | #
220 | # @private
221 | #
222 | # source://zeitwerk//lib/zeitwerk/loader.rb#73
223 | def lazy_subdirs; end
224 |
225 | # @private
226 | #
227 | # source://zeitwerk//lib/zeitwerk/loader.rb#77
228 | def mutex; end
229 |
230 | # @private
231 | #
232 | # source://zeitwerk//lib/zeitwerk/loader.rb#81
233 | def mutex2; end
234 |
235 | # Unloads all loaded code, and calls setup again so that the loader is able
236 | # to pick any changes in the file system.
237 | #
238 | # This method is not thread-safe, please see how this can be achieved by
239 | # client code in the README of the project.
240 | #
241 | # @raise [Zeitwerk::Error]
242 | #
243 | # source://zeitwerk//lib/zeitwerk/loader.rb#202
244 | def reload; end
245 |
246 | # Sets autoloads in the root namespace.
247 | #
248 | # source://zeitwerk//lib/zeitwerk/loader.rb#101
249 | def setup; end
250 |
251 | # Stores metadata needed for unloading. Its entries look like this:
252 | #
253 | # "Admin::Role" => [".../admin/role.rb", [Admin, :Role]]
254 | #
255 | # The cpath as key helps implementing unloadable_cpath? The file name is
256 | # stored in order to be able to delete it from $LOADED_FEATURES, and the
257 | # pair [Module, Symbol] is used to remove_const the constant from the class
258 | # or module object.
259 | #
260 | # If reloading is enabled, this hash is filled as constants are autoloaded
261 | # or eager loaded. Otherwise, the collection remains empty.
262 | #
263 | # @private
264 | #
265 | # source://zeitwerk//lib/zeitwerk/loader.rb#55
266 | def to_unload; end
267 |
268 | # Removes loaded constants and configured autoloads.
269 | #
270 | # The objects the constants stored are no longer reachable through them. In
271 | # addition, since said objects are normally not referenced from anywhere
272 | # else, they are eligible for garbage collection, which would effectively
273 | # unload them.
274 | #
275 | # This method is public but undocumented. Main interface is `reload`, which
276 | # means `unload` + `setup`. This one is avaiable to be used together with
277 | # `unregister`, which is undocumented too.
278 | #
279 | # source://zeitwerk//lib/zeitwerk/loader.rb#127
280 | def unload; end
281 |
282 | # Says if the given constant path would be unloaded on reload. This
283 | # predicate returns `false` if reloading is disabled.
284 | #
285 | # @return [Boolean]
286 | #
287 | # source://zeitwerk//lib/zeitwerk/loader.rb#267
288 | def unloadable_cpath?(cpath); end
289 |
290 | # Returns an array with the constant paths that would be unloaded on reload.
291 | # This predicate returns an empty array if reloading is disabled.
292 | #
293 | # source://zeitwerk//lib/zeitwerk/loader.rb#275
294 | def unloadable_cpaths; end
295 |
296 | # This is a dangerous method.
297 | #
298 | # source://zeitwerk//lib/zeitwerk/loader.rb#283
299 | def unregister; end
300 |
301 | private
302 |
303 | # source://zeitwerk//lib/zeitwerk/loader.rb#397
304 | def autoload_file(parent, cname, file); end
305 |
306 | # @return [Boolean]
307 | #
308 | # source://zeitwerk//lib/zeitwerk/loader.rb#453
309 | def autoload_path_set_by_me_for?(parent, cname); end
310 |
311 | # source://zeitwerk//lib/zeitwerk/loader.rb#376
312 | def autoload_subdir(parent, cname, subdir); end
313 |
314 | # `dir` is the directory that would have autovivified a namespace. `file` is
315 | # the file where we've found the namespace is explicitly defined.
316 | #
317 | # source://zeitwerk//lib/zeitwerk/loader.rb#421
318 | def promote_namespace_from_implicit_to_explicit(dir:, file:, parent:, cname:); end
319 |
320 | # source://zeitwerk//lib/zeitwerk/loader.rb#467
321 | def raise_if_conflicting_directory(dir); end
322 |
323 | # source://zeitwerk//lib/zeitwerk/loader.rb#462
324 | def register_explicit_namespace(cpath); end
325 |
326 | # source://zeitwerk//lib/zeitwerk/loader.rb#491
327 | def run_on_unload_callbacks(cpath, value, abspath); end
328 |
329 | # source://zeitwerk//lib/zeitwerk/loader.rb#432
330 | def set_autoload(parent, cname, abspath); end
331 |
332 | # source://zeitwerk//lib/zeitwerk/loader.rb#333
333 | def set_autoloads_in_dir(dir, parent); end
334 |
335 | # source://zeitwerk//lib/zeitwerk/loader.rb#498
336 | def unload_autoload(parent, cname); end
337 |
338 | # source://zeitwerk//lib/zeitwerk/loader.rb#504
339 | def unload_cref(parent, cname); end
340 |
341 | class << self
342 | # Returns an array with the absolute paths of the root directories of all
343 | # registered loaders. This is a read-only collection.
344 | #
345 | # source://zeitwerk//lib/zeitwerk/loader.rb#325
346 | def all_dirs; end
347 |
348 | # Returns the value of attribute default_logger.
349 | #
350 | # source://zeitwerk//lib/zeitwerk/loader.rb#292
351 | def default_logger; end
352 |
353 | # Sets the attribute default_logger
354 | #
355 | # @param value the value to set the attribute default_logger to.
356 | #
357 | # source://zeitwerk//lib/zeitwerk/loader.rb#292
358 | def default_logger=(_arg0); end
359 |
360 | # Broadcasts `eager_load` to all loaders.
361 | #
362 | # source://zeitwerk//lib/zeitwerk/loader.rb#317
363 | def eager_load_all; end
364 |
365 | # This is a shortcut for
366 | #
367 | # require "zeitwerk"
368 | # loader = Zeitwerk::Loader.new
369 | # loader.tag = File.basename(__FILE__, ".rb")
370 | # loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
371 | # loader.push_dir(__dir__)
372 | #
373 | # except that this method returns the same object in subsequent calls from
374 | # the same file, in the unlikely case the gem wants to be able to reload.
375 | #
376 | # This method returns a subclass of Zeitwerk::Loader, but the exact type
377 | # is private, client code can only rely on the interface.
378 | #
379 | # source://zeitwerk//lib/zeitwerk/loader.rb#309
380 | def for_gem(warn_on_extra_files: T.unsafe(nil)); end
381 | end
382 | end
383 |
384 | # source://zeitwerk//lib/zeitwerk/loader/callbacks.rb#3
385 | module Zeitwerk::Loader::Callbacks
386 | include ::Zeitwerk::RealModName
387 |
388 | # Invoked from our decorated Kernel#require when a managed directory is
389 | # autoloaded.
390 | #
391 | # @private
392 | #
393 | # source://zeitwerk//lib/zeitwerk/loader/callbacks.rb#34
394 | def on_dir_autoloaded(dir); end
395 |
396 | # Invoked from our decorated Kernel#require when a managed file is autoloaded.
397 | #
398 | # @private
399 | #
400 | # source://zeitwerk//lib/zeitwerk/loader/callbacks.rb#10
401 | def on_file_autoloaded(file); end
402 |
403 | # Invoked when a class or module is created or reopened, either from the
404 | # tracer or from module autovivification. If the namespace has matching
405 | # subdirectories, we descend into them now.
406 | #
407 | # @private
408 | #
409 | # source://zeitwerk//lib/zeitwerk/loader/callbacks.rb#73
410 | def on_namespace_loaded(namespace); end
411 |
412 | private
413 |
414 | # source://zeitwerk//lib/zeitwerk/loader/callbacks.rb#84
415 | def run_on_load_callbacks(cpath, value, abspath); end
416 | end
417 |
418 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#6
419 | module Zeitwerk::Loader::Config
420 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#82
421 | def initialize; end
422 |
423 | # Configure directories or glob patterns to be collapsed.
424 | #
425 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#191
426 | def collapse(*glob_patterns); end
427 |
428 | # The actual collection of absolute directory names at the time the collapse
429 | # glob patterns were expanded. Computed on setup, and recomputed on reload.
430 | #
431 | # @private
432 | #
433 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#51
434 | def collapse_dirs; end
435 |
436 | # Absolute paths of directories or glob patterns to be collapsed.
437 | #
438 | # @private
439 | #
440 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#44
441 | def collapse_glob_patterns; end
442 |
443 | # Absolute paths of the root directories. This is a read-only collection,
444 | # please push here via `push_dir`.
445 | #
446 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#143
447 | def dirs; end
448 |
449 | # Let eager load ignore the given files or directories. The constants defined
450 | # in those files are still autoloadable.
451 | #
452 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#173
453 | def do_not_eager_load(*paths); end
454 |
455 | # Absolute paths of files or directories not to be eager loaded.
456 | #
457 | # @private
458 | #
459 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#57
460 | def eager_load_exclusions; end
461 |
462 | # You need to call this method before setup in order to be able to reload.
463 | # There is no way to undo this, either you want to reload or you don't.
464 | #
465 | # @raise [Zeitwerk::Error]
466 | #
467 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#152
468 | def enable_reloading; end
469 |
470 | # Configure files, directories, or glob patterns to be totally ignored.
471 | #
472 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#180
473 | def ignore(*glob_patterns); end
474 |
475 | # Absolute paths of files, directories, or glob patterns to be totally
476 | # ignored.
477 | #
478 | # @private
479 | #
480 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#30
481 | def ignored_glob_patterns; end
482 |
483 | # The actual collection of absolute file and directory names at the time the
484 | # ignored glob patterns were expanded. Computed on setup, and recomputed on
485 | # reload.
486 | #
487 | # @private
488 | #
489 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#38
490 | def ignored_paths; end
491 |
492 | # @private
493 | # @return [Boolean]
494 | #
495 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#269
496 | def ignores?(abspath); end
497 |
498 | # Returns the value of attribute inflector.
499 | #
500 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#23
501 | def inflector; end
502 |
503 | # Sets the attribute inflector
504 | #
505 | # @param value the value to set the attribute inflector to.
506 | #
507 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#23
508 | def inflector=(_arg0); end
509 |
510 | # Logs to `$stdout`, handy shortcut for debugging.
511 | #
512 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#263
513 | def log!; end
514 |
515 | # Returns the value of attribute logger.
516 | #
517 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#80
518 | def logger; end
519 |
520 | # Sets the attribute logger
521 | #
522 | # @param value the value to set the attribute logger to.
523 | #
524 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#80
525 | def logger=(_arg0); end
526 |
527 | # Configure a block to be invoked once a certain constant path is loaded.
528 | # Supports multiple callbacks, and if there are many, they are executed in
529 | # the order in which they were defined.
530 | #
531 | # loader.on_load("SomeApiClient") do |klass, _abspath|
532 | # klass.endpoint = "https://api.dev"
533 | # end
534 | #
535 | # Can also be configured for any constant loaded:
536 | #
537 | # loader.on_load do |cpath, value, abspath|
538 | # # ...
539 | # end
540 | #
541 | # @raise [TypeError]
542 | #
543 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#227
544 | def on_load(cpath = T.unsafe(nil), &block); end
545 |
546 | # User-oriented callbacks to be fired when a constant is loaded.
547 | #
548 | # @private
549 | #
550 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#70
551 | def on_load_callbacks; end
552 |
553 | # Configure a block to be called after setup and on each reload.
554 | # If setup was already done, the block runs immediately.
555 | #
556 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#203
557 | def on_setup(&block); end
558 |
559 | # User-oriented callbacks to be fired on setup and on reload.
560 | #
561 | # @private
562 | #
563 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#63
564 | def on_setup_callbacks; end
565 |
566 | # Configure a block to be invoked right before a certain constant is removed.
567 | # Supports multiple callbacks, and if there are many, they are executed in the
568 | # order in which they were defined.
569 | #
570 | # loader.on_unload("Country") do |klass, _abspath|
571 | # klass.clear_cache
572 | # end
573 | #
574 | # Can also be configured for any removed constant:
575 | #
576 | # loader.on_unload do |cpath, value, abspath|
577 | # # ...
578 | # end
579 | #
580 | # @raise [TypeError]
581 | #
582 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#252
583 | def on_unload(cpath = T.unsafe(nil), &block); end
584 |
585 | # User-oriented callbacks to be fired before constants are removed.
586 | #
587 | # @private
588 | #
589 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#77
590 | def on_unload_callbacks; end
591 |
592 | # Pushes `path` to the list of root directories.
593 | #
594 | # Raises `Zeitwerk::Error` if `path` does not exist, or if another loader in
595 | # the same process already manages that directory or one of its ascendants or
596 | # descendants.
597 | #
598 | # @raise [Zeitwerk::Error]
599 | #
600 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#107
601 | def push_dir(path, namespace: T.unsafe(nil)); end
602 |
603 | # @return [Boolean]
604 | #
605 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#165
606 | def reloading_enabled?; end
607 |
608 | # Absolute paths of the root directories. Stored in a hash to preserve
609 | # order, easily handle duplicates, and also be able to have a fast lookup,
610 | # needed for detecting nested paths.
611 | #
612 | # "/Users/fxn/blog/app/assets" => true,
613 | # "/Users/fxn/blog/app/channels" => true,
614 | # ...
615 | #
616 | # This is a private collection maintained by the loader. The public
617 | # interface for it is `push_dir` and `dirs`.
618 | #
619 | # @private
620 | #
621 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#20
622 | def root_dirs; end
623 |
624 | # Returns the loader's tag.
625 | #
626 | # Implemented as a method instead of via attr_reader for symmetry with the
627 | # writer below.
628 | #
629 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#128
630 | def tag; end
631 |
632 | # Sets a tag for the loader, useful for logging.
633 | #
634 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#135
635 | def tag=(tag); end
636 |
637 | private
638 |
639 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#278
640 | def actual_root_dirs; end
641 |
642 | # @return [Boolean]
643 | #
644 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#295
645 | def collapse?(dir); end
646 |
647 | # @return [Boolean]
648 | #
649 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#290
650 | def excluded_from_eager_load?(abspath); end
651 |
652 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#305
653 | def expand_glob_patterns(glob_patterns); end
654 |
655 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#300
656 | def expand_paths(paths); end
657 |
658 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#317
659 | def recompute_collapse_dirs; end
660 |
661 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#312
662 | def recompute_ignored_paths; end
663 |
664 | # @return [Boolean]
665 | #
666 | # source://zeitwerk//lib/zeitwerk/loader/config.rb#285
667 | def root_dir?(dir); end
668 | end
669 |
670 | # source://zeitwerk//lib/zeitwerk/loader/helpers.rb#3
671 | module Zeitwerk::Loader::Helpers
672 | private
673 |
674 | # @return [Boolean]
675 | #
676 | # source://zeitwerk//lib/zeitwerk/loader/helpers.rb#120
677 | def cdef?(parent, cname); end
678 |
679 | # @raise [NameError]
680 | #
681 | # source://zeitwerk//lib/zeitwerk/loader/helpers.rb#126
682 | def cget(parent, cname); end
683 |
684 | # Symbol#name was introduced in Ruby 3.0. It returns always the same
685 | # frozen object, so we may save a few string allocations.
686 | #
687 | # source://zeitwerk//lib/zeitwerk/loader/helpers.rb#110
688 | def cpath(parent, cname); end
689 |
690 | # @return [Boolean]
691 | #
692 | # source://zeitwerk//lib/zeitwerk/loader/helpers.rb#68
693 | def dir?(path); end
694 |
695 | # @return [Boolean]
696 | #
697 | # source://zeitwerk//lib/zeitwerk/loader/helpers.rb#46
698 | def has_at_least_one_ruby_file?(dir); end
699 |
700 | # @return [Boolean]
701 | #
702 | # source://zeitwerk//lib/zeitwerk/loader/helpers.rb#73
703 | def hidden?(basename); end
704 |
705 | # source://zeitwerk//lib/zeitwerk/loader/helpers.rb#9
706 | def log(message); end
707 |
708 | # source://zeitwerk//lib/zeitwerk/loader/helpers.rb#17
709 | def ls(dir); end
710 |
711 | # @return [Boolean]
712 | #
713 | # source://zeitwerk//lib/zeitwerk/loader/helpers.rb#63
714 | def ruby?(path); end
715 |
716 | # source://zeitwerk//lib/zeitwerk/loader/helpers.rb#101
717 | def strict_autoload_path(parent, cname); end
718 | end
719 |
720 | # source://zeitwerk//lib/zeitwerk/loader.rb#16
721 | Zeitwerk::Loader::MUTEX = T.let(T.unsafe(nil), Thread::Mutex)
722 |
723 | # source://zeitwerk//lib/zeitwerk/error.rb#13
724 | class Zeitwerk::NameError < ::NameError; end
725 |
726 | # source://zeitwerk//lib/zeitwerk/real_mod_name.rb#3
727 | module Zeitwerk::RealModName
728 | # source://zeitwerk//lib/zeitwerk/real_mod_name.rb#14
729 | def real_mod_name(mod); end
730 | end
731 |
732 | # source://zeitwerk//lib/zeitwerk/real_mod_name.rb#4
733 | Zeitwerk::RealModName::UNBOUND_METHOD_MODULE_NAME = T.let(T.unsafe(nil), UnboundMethod)
734 |
735 | # source://zeitwerk//lib/zeitwerk/registry.rb#4
736 | module Zeitwerk::Registry
737 | class << self
738 | # Maps absolute paths to the loaders responsible for them.
739 | #
740 | # This information is used by our decorated `Kernel#require` to be able to
741 | # invoke callbacks and autovivify modules.
742 | #
743 | # @private
744 | #
745 | # source://zeitwerk//lib/zeitwerk/registry.rb#26
746 | def autoloads; end
747 |
748 | # Registers gem loaders to let `for_gem` be idempotent in case of reload.
749 | #
750 | # @private
751 | #
752 | # source://zeitwerk//lib/zeitwerk/registry.rb#17
753 | def gem_loaders_by_root_file; end
754 |
755 | # @private
756 | # @return [Boolean]
757 | #
758 | # source://zeitwerk//lib/zeitwerk/registry.rb#113
759 | def inception?(cpath); end
760 |
761 | # This hash table addresses an edge case in which an autoload is ignored.
762 | #
763 | # For example, let's suppose we want to autoload in a gem like this:
764 | #
765 | # # lib/my_gem.rb
766 | # loader = Zeitwerk::Loader.new
767 | # loader.push_dir(__dir__)
768 | # loader.setup
769 | #
770 | # module MyGem
771 | # end
772 | #
773 | # if you require "my_gem", as Bundler would do, this happens while setting
774 | # up autoloads:
775 | #
776 | # 1. Object.autoload?(:MyGem) returns `nil` because the autoload for
777 | # the constant is issued by Zeitwerk while the same file is being
778 | # required.
779 | # 2. The constant `MyGem` is undefined while setup runs.
780 | #
781 | # Therefore, a directory `lib/my_gem` would autovivify a module according to
782 | # the existing information. But that would be wrong.
783 | #
784 | # To overcome this fundamental limitation, we keep track of the constant
785 | # paths that are in this situation ---in the example above, "MyGem"--- and
786 | # take this collection into account for the autovivification logic.
787 | #
788 | # Note that you cannot generally address this by moving the setup code
789 | # below the constant definition, because we want libraries to be able to
790 | # use managed constants in the module body:
791 | #
792 | # module MyGem
793 | # include MyConcern
794 | # end
795 | #
796 | # @private
797 | #
798 | # source://zeitwerk//lib/zeitwerk/registry.rb#65
799 | def inceptions; end
800 |
801 | # @private
802 | #
803 | # source://zeitwerk//lib/zeitwerk/registry.rb#121
804 | def loader_for(path); end
805 |
806 | # This method returns always a loader, the same instance for the same root
807 | # file. That is how Zeitwerk::Loader.for_gem is idempotent.
808 | #
809 | # @private
810 | #
811 | # source://zeitwerk//lib/zeitwerk/registry.rb#89
812 | def loader_for_gem(root_file, warn_on_extra_files:); end
813 |
814 | # Keeps track of all loaders. Useful to broadcast messages and to prevent
815 | # them from being garbage collected.
816 | #
817 | # @private
818 | #
819 | # source://zeitwerk//lib/zeitwerk/registry.rb#11
820 | def loaders; end
821 |
822 | # @private
823 | #
824 | # source://zeitwerk//lib/zeitwerk/registry.rb#127
825 | def on_unload(loader); end
826 |
827 | # @private
828 | #
829 | # source://zeitwerk//lib/zeitwerk/registry.rb#95
830 | def register_autoload(loader, abspath); end
831 |
832 | # @private
833 | #
834 | # source://zeitwerk//lib/zeitwerk/registry.rb#107
835 | def register_inception(cpath, abspath, loader); end
836 |
837 | # Registers a loader.
838 | #
839 | # @private
840 | #
841 | # source://zeitwerk//lib/zeitwerk/registry.rb#71
842 | def register_loader(loader); end
843 |
844 | # @private
845 | #
846 | # source://zeitwerk//lib/zeitwerk/registry.rb#101
847 | def unregister_autoload(abspath); end
848 |
849 | # @private
850 | #
851 | # source://zeitwerk//lib/zeitwerk/registry.rb#77
852 | def unregister_loader(loader); end
853 | end
854 | end
855 |
856 | # source://zeitwerk//lib/zeitwerk/error.rb#7
857 | class Zeitwerk::ReloadingDisabledError < ::Zeitwerk::Error
858 | # @return [ReloadingDisabledError] a new instance of ReloadingDisabledError
859 | #
860 | # source://zeitwerk//lib/zeitwerk/error.rb#8
861 | def initialize; end
862 | end
863 |
864 | # source://zeitwerk//lib/zeitwerk/version.rb#4
865 | Zeitwerk::VERSION = T.let(T.unsafe(nil), String)
866 |
--------------------------------------------------------------------------------
/sorbet/rbi/gems/diff-lcs@1.5.0.rbi:
--------------------------------------------------------------------------------
1 | # typed: true
2 |
3 | # DO NOT EDIT MANUALLY
4 | # This is an autogenerated file for types exported from the `diff-lcs` gem.
5 | # Please instead update this file by running `bin/tapioca gem diff-lcs`.
6 |
7 | # source://diff-lcs//lib/diff/lcs.rb#3
8 | module Diff; end
9 |
10 | # source://diff-lcs//lib/diff/lcs.rb#51
11 | module Diff::LCS
12 | # Returns the difference set between +self+ and +other+. See Diff::LCS#diff.
13 | #
14 | # source://diff-lcs//lib/diff/lcs.rb#75
15 | def diff(other, callbacks = T.unsafe(nil), &block); end
16 |
17 | # Returns an Array containing the longest common subsequence(s) between
18 | # +self+ and +other+. See Diff::LCS#lcs.
19 | #
20 | # lcs = seq1.lcs(seq2)
21 | #
22 | # A note when using objects: Diff::LCS only works properly when each object
23 | # can be used as a key in a Hash, which typically means that the objects must
24 | # implement Object#eql? in a way that two identical values compare
25 | # identically for key purposes. That is:
26 | #
27 | # O.new('a').eql?(O.new('a')) == true
28 | #
29 | # source://diff-lcs//lib/diff/lcs.rb#70
30 | def lcs(other, &block); end
31 |
32 | # Attempts to patch +self+ with the provided +patchset+. A new sequence based
33 | # on +self+ and the +patchset+ will be created. See Diff::LCS#patch. Attempts
34 | # to autodiscover the direction of the patch.
35 | #
36 | # source://diff-lcs//lib/diff/lcs.rb#101
37 | def patch(patchset); end
38 |
39 | # Attempts to patch +self+ with the provided +patchset+. A new sequence based
40 | # on +self+ and the +patchset+ will be created. See Diff::LCS#patch. Does no
41 | # patch direction autodiscovery.
42 | #
43 | # source://diff-lcs//lib/diff/lcs.rb#109
44 | def patch!(patchset); end
45 |
46 | # Attempts to patch +self+ with the provided +patchset+, using #patch!. If
47 | # the sequence this is used on supports #replace, the value of +self+ will be
48 | # replaced. See Diff::LCS#patch. Does no patch direction autodiscovery.
49 | #
50 | # source://diff-lcs//lib/diff/lcs.rb#123
51 | def patch_me(patchset); end
52 |
53 | # Returns the balanced ("side-by-side") difference set between +self+ and
54 | # +other+. See Diff::LCS#sdiff.
55 | #
56 | # source://diff-lcs//lib/diff/lcs.rb#81
57 | def sdiff(other, callbacks = T.unsafe(nil), &block); end
58 |
59 | # Traverses the discovered longest common subsequences between +self+ and
60 | # +other+ using the alternate, balanced algorithm. See
61 | # Diff::LCS#traverse_balanced.
62 | #
63 | # source://diff-lcs//lib/diff/lcs.rb#94
64 | def traverse_balanced(other, callbacks = T.unsafe(nil), &block); end
65 |
66 | # Traverses the discovered longest common subsequences between +self+ and
67 | # +other+. See Diff::LCS#traverse_sequences.
68 | #
69 | # source://diff-lcs//lib/diff/lcs.rb#87
70 | def traverse_sequences(other, callbacks = T.unsafe(nil), &block); end
71 |
72 | # Attempts to patch +self+ with the provided +patchset+. A new sequence based
73 | # on +self+ and the +patchset+ will be created. See Diff::LCS#patch. Attempts
74 | # to autodiscover the direction of the patch.
75 | #
76 | # source://diff-lcs//lib/diff/lcs.rb#101
77 | def unpatch(patchset); end
78 |
79 | # Attempts to unpatch +self+ with the provided +patchset+. A new sequence
80 | # based on +self+ and the +patchset+ will be created. See Diff::LCS#unpatch.
81 | # Does no patch direction autodiscovery.
82 | #
83 | # source://diff-lcs//lib/diff/lcs.rb#116
84 | def unpatch!(patchset); end
85 |
86 | # Attempts to unpatch +self+ with the provided +patchset+, using #unpatch!.
87 | # If the sequence this is used on supports #replace, the value of +self+ will
88 | # be replaced. See Diff::LCS#unpatch. Does no patch direction autodiscovery.
89 | #
90 | # source://diff-lcs//lib/diff/lcs.rb#134
91 | def unpatch_me(patchset); end
92 |
93 | class << self
94 | # :yields seq1[i] for each matched:
95 | #
96 | # source://diff-lcs//lib/diff/lcs.rb#144
97 | def LCS(seq1, seq2, &block); end
98 |
99 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#52
100 | def callbacks_for(callbacks); end
101 |
102 | # #diff computes the smallest set of additions and deletions necessary to
103 | # turn the first sequence into the second, and returns a description of these
104 | # changes.
105 | #
106 | # See Diff::LCS::DiffCallbacks for the default behaviour. An alternate
107 | # behaviour may be implemented with Diff::LCS::ContextDiffCallbacks. If a
108 | # Class argument is provided for +callbacks+, #diff will attempt to
109 | # initialise it. If the +callbacks+ object (possibly initialised) responds to
110 | # #finish, it will be called.
111 | #
112 | # source://diff-lcs//lib/diff/lcs.rb#168
113 | def diff(seq1, seq2, callbacks = T.unsafe(nil), &block); end
114 |
115 | # :yields seq1[i] for each matched:
116 | #
117 | # source://diff-lcs//lib/diff/lcs.rb#144
118 | def lcs(seq1, seq2, &block); end
119 |
120 | # Applies a +patchset+ to the sequence +src+ according to the +direction+
121 | # (:patch or :unpatch), producing a new sequence.
122 | #
123 | # If the +direction+ is not specified, Diff::LCS::patch will attempt to
124 | # discover the direction of the +patchset+.
125 | #
126 | # A +patchset+ can be considered to apply forward (:patch) if the
127 | # following expression is true:
128 | #
129 | # patch(s1, diff(s1, s2)) -> s2
130 | #
131 | # A +patchset+ can be considered to apply backward (:unpatch) if the
132 | # following expression is true:
133 | #
134 | # patch(s2, diff(s1, s2)) -> s1
135 | #
136 | # If the +patchset+ contains no changes, the +src+ value will be returned as
137 | # either src.dup or +src+. A +patchset+ can be deemed as having no
138 | # changes if the following predicate returns true:
139 | #
140 | # patchset.empty? or
141 | # patchset.flatten(1).all? { |change| change.unchanged? }
142 | #
143 | # === Patchsets
144 | #
145 | # A +patchset+ is always an enumerable sequence of changes, hunks of changes,
146 | # or a mix of the two. A hunk of changes is an enumerable sequence of
147 | # changes:
148 | #
149 | # [ # patchset
150 | # # change
151 | # [ # hunk
152 | # # change
153 | # ]
154 | # ]
155 | #
156 | # The +patch+ method accepts patchsets that are enumerable sequences
157 | # containing either Diff::LCS::Change objects (or a subclass) or the array
158 | # representations of those objects. Prior to application, array
159 | # representations of Diff::LCS::Change objects will be reified.
160 | #
161 | # source://diff-lcs//lib/diff/lcs.rb#624
162 | def patch(src, patchset, direction = T.unsafe(nil)); end
163 |
164 | # Given a set of patchset, convert the current version to the next version.
165 | # Does no auto-discovery.
166 | #
167 | # source://diff-lcs//lib/diff/lcs.rb#734
168 | def patch!(src, patchset); end
169 |
170 | # #sdiff computes all necessary components to show two sequences and their
171 | # minimized differences side by side, just like the Unix utility
172 | # sdiff does:
173 | #
174 | # old < -
175 | # same same
176 | # before | after
177 | # - > new
178 | #
179 | # See Diff::LCS::SDiffCallbacks for the default behaviour. An alternate
180 | # behaviour may be implemented with Diff::LCS::ContextDiffCallbacks. If a
181 | # Class argument is provided for +callbacks+, #diff will attempt to
182 | # initialise it. If the +callbacks+ object (possibly initialised) responds to
183 | # #finish, it will be called.
184 | #
185 | # Each element of a returned array is a Diff::LCS::ContextChange object,
186 | # which can be implicitly converted to an array.
187 | #
188 | # Diff::LCS.sdiff(a, b).each do |action, (old_pos, old_element), (new_pos, new_element)|
189 | # case action
190 | # when '!'
191 | # # replace
192 | # when '-'
193 | # # delete
194 | # when '+'
195 | # # insert
196 | # end
197 | # end
198 | #
199 | # source://diff-lcs//lib/diff/lcs.rb#200
200 | def sdiff(seq1, seq2, callbacks = T.unsafe(nil), &block); end
201 |
202 | # #traverse_balanced is an alternative to #traverse_sequences. It uses a
203 | # different algorithm to iterate through the entries in the computed longest
204 | # common subsequence. Instead of viewing the changes as insertions or
205 | # deletions from one of the sequences, #traverse_balanced will report
206 | # changes between the sequences.
207 | #
208 | # The arguments to #traverse_balanced are the two sequences to traverse and a
209 | # callback object, like this:
210 | #
211 | # traverse_balanced(seq1, seq2, Diff::LCS::ContextDiffCallbacks.new)
212 | #
213 | # #sdiff is implemented with #traverse_balanced.
214 | #
215 | # == Callback Methods
216 | #
217 | # Optional callback methods are emphasized.
218 | #
219 | # callbacks#match:: Called when +a+ and +b+ are pointing to
220 | # common elements in +A+ and +B+.
221 | # callbacks#discard_a:: Called when +a+ is pointing to an
222 | # element not in +B+.
223 | # callbacks#discard_b:: Called when +b+ is pointing to an
224 | # element not in +A+.
225 | # callbacks#change:: Called when +a+ and +b+ are pointing to
226 | # the same relative position, but
227 | # A[a] and B[b] are not
228 | # the same; a change has
229 | # occurred.
230 | #
231 | # #traverse_balanced might be a bit slower than #traverse_sequences,
232 | # noticable only while processing huge amounts of data.
233 | #
234 | # == Algorithm
235 | #
236 | # a---+
237 | # v
238 | # A = a b c e h j l m n p
239 | # B = b c d e f j k l m r s t
240 | # ^
241 | # b---+
242 | #
243 | # === Matches
244 | #
245 | # If there are two arrows (+a+ and +b+) pointing to elements of sequences +A+
246 | # and +B+, the arrows will initially point to the first elements of their
247 | # respective sequences. #traverse_sequences will advance the arrows through
248 | # the sequences one element at a time, calling a method on the user-specified
249 | # callback object before each advance. It will advance the arrows in such a
250 | # way that if there are elements A[i] and B[j] which are
251 | # both equal and part of the longest common subsequence, there will be some
252 | # moment during the execution of #traverse_sequences when arrow +a+ is
253 | # pointing to A[i] and arrow +b+ is pointing to B[j]. When
254 | # this happens, #traverse_sequences will call callbacks#match and
255 | # then it will advance both arrows.
256 | #
257 | # === Discards
258 | #
259 | # Otherwise, one of the arrows is pointing to an element of its sequence that
260 | # is not part of the longest common subsequence. #traverse_sequences will
261 | # advance that arrow and will call callbacks#discard_a or
262 | # callbacks#discard_b, depending on which arrow it advanced.
263 | #
264 | # === Changes
265 | #
266 | # If both +a+ and +b+ point to elements that are not part of the longest
267 | # common subsequence, then #traverse_sequences will try to call
268 | # callbacks#change and advance both arrows. If
269 | # callbacks#change is not implemented, then
270 | # callbacks#discard_a and callbacks#discard_b will be
271 | # called in turn.
272 | #
273 | # The methods for callbacks#match, callbacks#discard_a,
274 | # callbacks#discard_b, and callbacks#change are invoked
275 | # with an event comprising the action ("=", "+", "-", or "!", respectively),
276 | # the indicies +i+ and +j+, and the elements A[i] and B[j].
277 | # Return values are discarded by #traverse_balanced.
278 | #
279 | # === Context
280 | #
281 | # Note that +i+ and +j+ may not be the same index position, even if +a+ and
282 | # +b+ are considered to be pointing to matching or changed elements.
283 | #
284 | # source://diff-lcs//lib/diff/lcs.rb#475
285 | def traverse_balanced(seq1, seq2, callbacks = T.unsafe(nil)); end
286 |
287 | # #traverse_sequences is the most general facility provided by this module;
288 | # #diff and #lcs are implemented as calls to it.
289 | #
290 | # The arguments to #traverse_sequences are the two sequences to traverse, and
291 | # a callback object, like this:
292 | #
293 | # traverse_sequences(seq1, seq2, Diff::LCS::ContextDiffCallbacks.new)
294 | #
295 | # == Callback Methods
296 | #
297 | # Optional callback methods are emphasized.
298 | #
299 | # callbacks#match:: Called when +a+ and +b+ are pointing to
300 | # common elements in +A+ and +B+.
301 | # callbacks#discard_a:: Called when +a+ is pointing to an
302 | # element not in +B+.
303 | # callbacks#discard_b:: Called when +b+ is pointing to an
304 | # element not in +A+.
305 | # callbacks#finished_a:: Called when +a+ has reached the end of
306 | # sequence +A+.
307 | # callbacks#finished_b:: Called when +b+ has reached the end of
308 | # sequence +B+.
309 | #
310 | # == Algorithm
311 | #
312 | # a---+
313 | # v
314 | # A = a b c e h j l m n p
315 | # B = b c d e f j k l m r s t
316 | # ^
317 | # b---+
318 | #
319 | # If there are two arrows (+a+ and +b+) pointing to elements of sequences +A+
320 | # and +B+, the arrows will initially point to the first elements of their
321 | # respective sequences. #traverse_sequences will advance the arrows through
322 | # the sequences one element at a time, calling a method on the user-specified
323 | # callback object before each advance. It will advance the arrows in such a
324 | # way that if there are elements A[i] and B[j] which are
325 | # both equal and part of the longest common subsequence, there will be some
326 | # moment during the execution of #traverse_sequences when arrow +a+ is
327 | # pointing to A[i] and arrow +b+ is pointing to B[j]. When
328 | # this happens, #traverse_sequences will call callbacks#match and
329 | # then it will advance both arrows.
330 | #
331 | # Otherwise, one of the arrows is pointing to an element of its sequence that
332 | # is not part of the longest common subsequence. #traverse_sequences will
333 | # advance that arrow and will call callbacks#discard_a or
334 | # callbacks#discard_b, depending on which arrow it advanced. If both
335 | # arrows point to elements that are not part of the longest common
336 | # subsequence, then #traverse_sequences will advance arrow +a+ and call the
337 | # appropriate callback, then it will advance arrow +b+ and call the appropriate
338 | # callback.
339 | #
340 | # The methods for callbacks#match, callbacks#discard_a, and
341 | # callbacks#discard_b are invoked with an event comprising the
342 | # action ("=", "+", or "-", respectively), the indicies +i+ and +j+, and the
343 | # elements A[i] and B[j]. Return values are discarded by
344 | # #traverse_sequences.
345 | #
346 | # === End of Sequences
347 | #
348 | # If arrow +a+ reaches the end of its sequence before arrow +b+ does,
349 | # #traverse_sequence will try to call callbacks#finished_a with the
350 | # last index and element of +A+ (A[-1]) and the current index and
351 | # element of +B+ (B[j]). If callbacks#finished_a does not
352 | # exist, then callbacks#discard_b will be called on each element of
353 | # +B+ until the end of the sequence is reached (the call will be done with
354 | # A[-1] and B[j] for each element).
355 | #
356 | # If +b+ reaches the end of +B+ before +a+ reaches the end of +A+,
357 | # callbacks#finished_b will be called with the current index and
358 | # element of +A+ (A[i]) and the last index and element of +B+
359 | # (A[-1]). Again, if callbacks#finished_b does not exist on
360 | # the callback object, then callbacks#discard_a will be called on
361 | # each element of +A+ until the end of the sequence is reached (A[i]
362 | # and B[-1]).
363 | #
364 | # There is a chance that one additional callbacks#discard_a or
365 | # callbacks#discard_b will be called after the end of the sequence
366 | # is reached, if +a+ has not yet reached the end of +A+ or +b+ has not yet
367 | # reached the end of +B+.
368 | #
369 | # source://diff-lcs//lib/diff/lcs.rb#285
370 | def traverse_sequences(seq1, seq2, callbacks = T.unsafe(nil)); end
371 |
372 | # Given a set of patchset, convert the current version to the prior version.
373 | # Does no auto-discovery.
374 | #
375 | # source://diff-lcs//lib/diff/lcs.rb#728
376 | def unpatch!(src, patchset); end
377 |
378 | private
379 |
380 | # source://diff-lcs//lib/diff/lcs/internals.rb#4
381 | def diff_traversal(method, seq1, seq2, callbacks, &block); end
382 | end
383 | end
384 |
385 | # An alias for DefaultCallbacks that is used in
386 | # Diff::LCS#traverse_balanced.
387 | #
388 | # Diff::LCS.LCS(seq1, seq2, Diff::LCS::BalancedCallbacks)
389 | #
390 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#50
391 | Diff::LCS::BalancedCallbacks = Diff::LCS::DefaultCallbacks
392 |
393 | # A block is an operation removing, adding, or changing a group of items.
394 | # Basically, this is just a list of changes, where each change adds or
395 | # deletes a single item. Used by bin/ldiff.
396 | #
397 | # source://diff-lcs//lib/diff/lcs/block.rb#6
398 | class Diff::LCS::Block
399 | # @return [Block] a new instance of Block
400 | #
401 | # source://diff-lcs//lib/diff/lcs/block.rb#9
402 | def initialize(chunk); end
403 |
404 | # Returns the value of attribute changes.
405 | #
406 | # source://diff-lcs//lib/diff/lcs/block.rb#7
407 | def changes; end
408 |
409 | # source://diff-lcs//lib/diff/lcs/block.rb#21
410 | def diff_size; end
411 |
412 | # Returns the value of attribute insert.
413 | #
414 | # source://diff-lcs//lib/diff/lcs/block.rb#7
415 | def insert; end
416 |
417 | # source://diff-lcs//lib/diff/lcs/block.rb#25
418 | def op; end
419 |
420 | # Returns the value of attribute remove.
421 | #
422 | # source://diff-lcs//lib/diff/lcs/block.rb#7
423 | def remove; end
424 | end
425 |
426 | # Represents a simplistic (non-contextual) change. Represents the removal or
427 | # addition of an element from either the old or the new sequenced
428 | # enumerable.
429 | #
430 | # source://diff-lcs//lib/diff/lcs/change.rb#6
431 | class Diff::LCS::Change
432 | include ::Comparable
433 |
434 | # @return [Change] a new instance of Change
435 | #
436 | # source://diff-lcs//lib/diff/lcs/change.rb#27
437 | def initialize(*args); end
438 |
439 | # source://diff-lcs//lib/diff/lcs/change.rb#65
440 | def <=>(other); end
441 |
442 | # source://diff-lcs//lib/diff/lcs/change.rb#58
443 | def ==(other); end
444 |
445 | # Returns the action this Change represents.
446 | #
447 | # source://diff-lcs//lib/diff/lcs/change.rb#20
448 | def action; end
449 |
450 | # @return [Boolean]
451 | #
452 | # source://diff-lcs//lib/diff/lcs/change.rb#72
453 | def adding?; end
454 |
455 | # @return [Boolean]
456 | #
457 | # source://diff-lcs//lib/diff/lcs/change.rb#84
458 | def changed?; end
459 |
460 | # @return [Boolean]
461 | #
462 | # source://diff-lcs//lib/diff/lcs/change.rb#76
463 | def deleting?; end
464 |
465 | # Returns the sequence element of the Change.
466 | #
467 | # source://diff-lcs//lib/diff/lcs/change.rb#25
468 | def element; end
469 |
470 | # @return [Boolean]
471 | #
472 | # source://diff-lcs//lib/diff/lcs/change.rb#88
473 | def finished_a?; end
474 |
475 | # @return [Boolean]
476 | #
477 | # source://diff-lcs//lib/diff/lcs/change.rb#92
478 | def finished_b?; end
479 |
480 | # source://diff-lcs//lib/diff/lcs/change.rb#34
481 | def inspect(*_args); end
482 |
483 | # Returns the position of the Change.
484 | #
485 | # source://diff-lcs//lib/diff/lcs/change.rb#23
486 | def position; end
487 |
488 | # source://diff-lcs//lib/diff/lcs/change.rb#38
489 | def to_a; end
490 |
491 | # source://diff-lcs//lib/diff/lcs/change.rb#38
492 | def to_ary; end
493 |
494 | # @return [Boolean]
495 | #
496 | # source://diff-lcs//lib/diff/lcs/change.rb#80
497 | def unchanged?; end
498 |
499 | class << self
500 | # source://diff-lcs//lib/diff/lcs/change.rb#44
501 | def from_a(arr); end
502 |
503 | # @return [Boolean]
504 | #
505 | # source://diff-lcs//lib/diff/lcs/change.rb#15
506 | def valid_action?(action); end
507 | end
508 | end
509 |
510 | # source://diff-lcs//lib/diff/lcs/change.rb#7
511 | Diff::LCS::Change::IntClass = Integer
512 |
513 | # The only actions valid for changes are '+' (add), '-' (delete), '='
514 | # (no change), '!' (changed), '<' (tail changes from first sequence), or
515 | # '>' (tail changes from second sequence). The last two ('<>') are only
516 | # found with Diff::LCS::diff and Diff::LCS::sdiff.
517 | #
518 | # source://diff-lcs//lib/diff/lcs/change.rb#13
519 | Diff::LCS::Change::VALID_ACTIONS = T.let(T.unsafe(nil), Array)
520 |
521 | # Represents a contextual change. Contains the position and values of the
522 | # elements in the old and the new sequenced enumerables as well as the action
523 | # taken.
524 | #
525 | # source://diff-lcs//lib/diff/lcs/change.rb#101
526 | class Diff::LCS::ContextChange < ::Diff::LCS::Change
527 | # @return [ContextChange] a new instance of ContextChange
528 | #
529 | # source://diff-lcs//lib/diff/lcs/change.rb#114
530 | def initialize(*args); end
531 |
532 | # source://diff-lcs//lib/diff/lcs/change.rb#166
533 | def <=>(other); end
534 |
535 | # source://diff-lcs//lib/diff/lcs/change.rb#157
536 | def ==(other); end
537 |
538 | # Returns the new element being changed.
539 | #
540 | # source://diff-lcs//lib/diff/lcs/change.rb#112
541 | def new_element; end
542 |
543 | # Returns the new position being changed.
544 | #
545 | # source://diff-lcs//lib/diff/lcs/change.rb#108
546 | def new_position; end
547 |
548 | # Returns the old element being changed.
549 | #
550 | # source://diff-lcs//lib/diff/lcs/change.rb#110
551 | def old_element; end
552 |
553 | # Returns the old position being changed.
554 | #
555 | # source://diff-lcs//lib/diff/lcs/change.rb#106
556 | def old_position; end
557 |
558 | # source://diff-lcs//lib/diff/lcs/change.rb#122
559 | def to_a; end
560 |
561 | # source://diff-lcs//lib/diff/lcs/change.rb#122
562 | def to_ary; end
563 |
564 | class << self
565 | # source://diff-lcs//lib/diff/lcs/change.rb#132
566 | def from_a(arr); end
567 |
568 | # Simplifies a context change for use in some diff callbacks. '<' actions
569 | # are converted to '-' and '>' actions are converted to '+'.
570 | #
571 | # source://diff-lcs//lib/diff/lcs/change.rb#138
572 | def simplify(event); end
573 | end
574 | end
575 |
576 | # This will produce a compound array of contextual diff change objects. Each
577 | # element in the #diffs array is a "hunk" array, where each element in each
578 | # "hunk" array is a single change. Each change is a Diff::LCS::ContextChange
579 | # that contains both the old index and new index values for the change. The
580 | # "hunk" provides the full context for the changes. Both old and new objects
581 | # will be presented for changed objects. +nil+ will be substituted for a
582 | # discarded object.
583 | #
584 | # seq1 = %w(a b c e h j l m n p)
585 | # seq2 = %w(b c d e f j k l m r s t)
586 | #
587 | # diffs = Diff::LCS.diff(seq1, seq2, Diff::LCS::ContextDiffCallbacks)
588 | # # This example shows a simplified array format.
589 | # # [ [ [ '-', [ 0, 'a' ], [ 0, nil ] ] ], # 1
590 | # # [ [ '+', [ 3, nil ], [ 2, 'd' ] ] ], # 2
591 | # # [ [ '-', [ 4, 'h' ], [ 4, nil ] ], # 3
592 | # # [ '+', [ 5, nil ], [ 4, 'f' ] ] ],
593 | # # [ [ '+', [ 6, nil ], [ 6, 'k' ] ] ], # 4
594 | # # [ [ '-', [ 8, 'n' ], [ 9, nil ] ], # 5
595 | # # [ '+', [ 9, nil ], [ 9, 'r' ] ],
596 | # # [ '-', [ 9, 'p' ], [ 10, nil ] ],
597 | # # [ '+', [ 10, nil ], [ 10, 's' ] ],
598 | # # [ '+', [ 10, nil ], [ 11, 't' ] ] ] ]
599 | #
600 | # The five hunks shown are comprised of individual changes; if there is a
601 | # related set of changes, they are still shown individually.
602 | #
603 | # This callback can also be used with Diff::LCS#sdiff, which will produce
604 | # results like:
605 | #
606 | # diffs = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextCallbacks)
607 | # # This example shows a simplified array format.
608 | # # [ [ [ "-", [ 0, "a" ], [ 0, nil ] ] ], # 1
609 | # # [ [ "+", [ 3, nil ], [ 2, "d" ] ] ], # 2
610 | # # [ [ "!", [ 4, "h" ], [ 4, "f" ] ] ], # 3
611 | # # [ [ "+", [ 6, nil ], [ 6, "k" ] ] ], # 4
612 | # # [ [ "!", [ 8, "n" ], [ 9, "r" ] ], # 5
613 | # # [ "!", [ 9, "p" ], [ 10, "s" ] ],
614 | # # [ "+", [ 10, nil ], [ 11, "t" ] ] ] ]
615 | #
616 | # The five hunks are still present, but are significantly shorter in total
617 | # presentation, because changed items are shown as changes ("!") instead of
618 | # potentially "mismatched" pairs of additions and deletions.
619 | #
620 | # The result of this operation is similar to that of
621 | # Diff::LCS::SDiffCallbacks. They may be compared as:
622 | #
623 | # s = Diff::LCS.sdiff(seq1, seq2).reject { |e| e.action == "=" }
624 | # c = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks).flatten(1)
625 | #
626 | # s == c # -> true
627 | #
628 | # === Use
629 | #
630 | # This callback object must be initialised and can be used by the
631 | # Diff::LCS#diff or Diff::LCS#sdiff methods.
632 | #
633 | # cbo = Diff::LCS::ContextDiffCallbacks.new
634 | # Diff::LCS.LCS(seq1, seq2, cbo)
635 | # cbo.finish
636 | #
637 | # Note that the call to #finish is absolutely necessary, or the last set of
638 | # changes will not be visible. Alternatively, can be used as:
639 | #
640 | # cbo = Diff::LCS::ContextDiffCallbacks.new { |tcbo| Diff::LCS.LCS(seq1, seq2, tcbo) }
641 | #
642 | # The necessary #finish call will be made.
643 | #
644 | # === Simplified Array Format
645 | #
646 | # The simplified array format used in the example above can be obtained
647 | # with:
648 | #
649 | # require 'pp'
650 | # pp diffs.map { |e| e.map { |f| f.to_a } }
651 | #
652 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#223
653 | class Diff::LCS::ContextDiffCallbacks < ::Diff::LCS::DiffCallbacks
654 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#232
655 | def change(event); end
656 |
657 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#224
658 | def discard_a(event); end
659 |
660 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#228
661 | def discard_b(event); end
662 | end
663 |
664 | # This callback object implements the default set of callback events,
665 | # which only returns the event itself. Note that #finished_a and
666 | # #finished_b are not implemented -- I haven't yet figured out where they
667 | # would be useful.
668 | #
669 | # Note that this is intended to be called as is, e.g.,
670 | #
671 | # Diff::LCS.LCS(seq1, seq2, Diff::LCS::DefaultCallbacks)
672 | #
673 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#14
674 | class Diff::LCS::DefaultCallbacks
675 | class << self
676 | # Called when both the old and new values have changed.
677 | #
678 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#32
679 | def change(event); end
680 |
681 | # Called when the old value is discarded in favour of the new value.
682 | #
683 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#22
684 | def discard_a(event); end
685 |
686 | # Called when the new value is discarded in favour of the old value.
687 | #
688 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#27
689 | def discard_b(event); end
690 |
691 | # Called when two items match.
692 | #
693 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#17
694 | def match(event); end
695 | end
696 | end
697 |
698 | # This will produce a compound array of simple diff change objects. Each
699 | # element in the #diffs array is a +hunk+ or +hunk+ array, where each
700 | # element in each +hunk+ array is a single Change object representing the
701 | # addition or removal of a single element from one of the two tested
702 | # sequences. The +hunk+ provides the full context for the changes.
703 | #
704 | # diffs = Diff::LCS.diff(seq1, seq2)
705 | # # This example shows a simplified array format.
706 | # # [ [ [ '-', 0, 'a' ] ], # 1
707 | # # [ [ '+', 2, 'd' ] ], # 2
708 | # # [ [ '-', 4, 'h' ], # 3
709 | # # [ '+', 4, 'f' ] ],
710 | # # [ [ '+', 6, 'k' ] ], # 4
711 | # # [ [ '-', 8, 'n' ], # 5
712 | # # [ '-', 9, 'p' ],
713 | # # [ '+', 9, 'r' ],
714 | # # [ '+', 10, 's' ],
715 | # # [ '+', 11, 't' ] ] ]
716 | #
717 | # There are five hunks here. The first hunk says that the +a+ at position 0
718 | # of the first sequence should be deleted ('-'). The second hunk
719 | # says that the +d+ at position 2 of the second sequence should be inserted
720 | # ('+'). The third hunk says that the +h+ at position 4 of the
721 | # first sequence should be removed and replaced with the +f+ from position 4
722 | # of the second sequence. The other two hunks are described similarly.
723 | #
724 | # === Use
725 | #
726 | # This callback object must be initialised and is used by the Diff::LCS#diff
727 | # method.
728 | #
729 | # cbo = Diff::LCS::DiffCallbacks.new
730 | # Diff::LCS.LCS(seq1, seq2, cbo)
731 | # cbo.finish
732 | #
733 | # Note that the call to #finish is absolutely necessary, or the last set of
734 | # changes will not be visible. Alternatively, can be used as:
735 | #
736 | # cbo = Diff::LCS::DiffCallbacks.new { |tcbo| Diff::LCS.LCS(seq1, seq2, tcbo) }
737 | #
738 | # The necessary #finish call will be made.
739 | #
740 | # === Simplified Array Format
741 | #
742 | # The simplified array format used in the example above can be obtained
743 | # with:
744 | #
745 | # require 'pp'
746 | # pp diffs.map { |e| e.map { |f| f.to_a } }
747 | #
748 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#106
749 | class Diff::LCS::DiffCallbacks
750 | # :yields self:
751 | #
752 | # @return [DiffCallbacks] a new instance of DiffCallbacks
753 | #
754 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#110
755 | def initialize; end
756 |
757 | # Returns the difference set collected during the diff process.
758 | #
759 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#108
760 | def diffs; end
761 |
762 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#133
763 | def discard_a(event); end
764 |
765 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#137
766 | def discard_b(event); end
767 |
768 | # Finalizes the diff process. If an unprocessed hunk still exists, then it
769 | # is appended to the diff list.
770 | #
771 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#125
772 | def finish; end
773 |
774 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#129
775 | def match(_event); end
776 |
777 | private
778 |
779 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#141
780 | def finish_hunk; end
781 | end
782 |
783 | # A Hunk is a group of Blocks which overlap because of the context surrounding
784 | # each block. (So if we're not using context, every hunk will contain one
785 | # block.) Used in the diff program (bin/ldiff).
786 | #
787 | # source://diff-lcs//lib/diff/lcs/hunk.rb#8
788 | class Diff::LCS::Hunk
789 | # Create a hunk using references to both the old and new data, as well as the
790 | # piece of data.
791 | #
792 | # @return [Hunk] a new instance of Hunk
793 | #
794 | # source://diff-lcs//lib/diff/lcs/hunk.rb#16
795 | def initialize(data_old, data_new, piece, flag_context, file_length_difference); end
796 |
797 | # Returns the value of attribute blocks.
798 | #
799 | # source://diff-lcs//lib/diff/lcs/hunk.rb#63
800 | def blocks; end
801 |
802 | # Returns a diff string based on a format.
803 | #
804 | # source://diff-lcs//lib/diff/lcs/hunk.rb#116
805 | def diff(format, last = T.unsafe(nil)); end
806 |
807 | # Returns the value of attribute end_new.
808 | #
809 | # source://diff-lcs//lib/diff/lcs/hunk.rb#65
810 | def end_new; end
811 |
812 | # Returns the value of attribute end_old.
813 | #
814 | # source://diff-lcs//lib/diff/lcs/hunk.rb#65
815 | def end_old; end
816 |
817 | # Returns the value of attribute file_length_difference.
818 | #
819 | # source://diff-lcs//lib/diff/lcs/hunk.rb#66
820 | def file_length_difference; end
821 |
822 | # Change the "start" and "end" fields to note that context should be added
823 | # to this hunk.
824 | #
825 | # source://diff-lcs//lib/diff/lcs/hunk.rb#70
826 | def flag_context; end
827 |
828 | # source://diff-lcs//lib/diff/lcs/hunk.rb#72
829 | def flag_context=(context); end
830 |
831 | # Merges this hunk and the provided hunk together if they overlap. Returns
832 | # a truthy value so that if there is no overlap, you can know the merge
833 | # was skipped.
834 | #
835 | # source://diff-lcs//lib/diff/lcs/hunk.rb#98
836 | def merge(hunk); end
837 |
838 | # @return [Boolean]
839 | #
840 | # source://diff-lcs//lib/diff/lcs/hunk.rb#326
841 | def missing_last_newline?(data); end
842 |
843 | # Determines whether there is an overlap between this hunk and the
844 | # provided hunk. This will be true if the difference between the two hunks
845 | # start or end positions is within one position of each other.
846 | #
847 | # @return [Boolean]
848 | #
849 | # source://diff-lcs//lib/diff/lcs/hunk.rb#110
850 | def overlaps?(hunk); end
851 |
852 | # Returns the value of attribute start_new.
853 | #
854 | # source://diff-lcs//lib/diff/lcs/hunk.rb#64
855 | def start_new; end
856 |
857 | # Returns the value of attribute start_old.
858 | #
859 | # source://diff-lcs//lib/diff/lcs/hunk.rb#64
860 | def start_old; end
861 |
862 | # Merges this hunk and the provided hunk together if they overlap. Returns
863 | # a truthy value so that if there is no overlap, you can know the merge
864 | # was skipped.
865 | #
866 | # source://diff-lcs//lib/diff/lcs/hunk.rb#98
867 | def unshift(hunk); end
868 |
869 | private
870 |
871 | # source://diff-lcs//lib/diff/lcs/hunk.rb#213
872 | def context_diff(last = T.unsafe(nil)); end
873 |
874 | # Generate a range of item numbers to print. Only print 1 number if the
875 | # range has only one item in it. Otherwise, it's 'start,end'
876 | #
877 | # source://diff-lcs//lib/diff/lcs/hunk.rb#293
878 | def context_range(mode, op, last = T.unsafe(nil)); end
879 |
880 | # source://diff-lcs//lib/diff/lcs/hunk.rb#271
881 | def ed_diff(format, _last = T.unsafe(nil)); end
882 |
883 | # source://diff-lcs//lib/diff/lcs/hunk.rb#339
884 | def encode(literal, target_encoding = T.unsafe(nil)); end
885 |
886 | # source://diff-lcs//lib/diff/lcs/hunk.rb#343
887 | def encode_as(string, *args); end
888 |
889 | # Note that an old diff can't have any context. Therefore, we know that
890 | # there's only one block in the hunk.
891 | #
892 | # source://diff-lcs//lib/diff/lcs/hunk.rb#135
893 | def old_diff(_last = T.unsafe(nil)); end
894 |
895 | # source://diff-lcs//lib/diff/lcs/hunk.rb#160
896 | def unified_diff(last = T.unsafe(nil)); end
897 |
898 | # Generate a range of item numbers to print for unified diff. Print number
899 | # where block starts, followed by number of lines in the block
900 | # (don't print number of lines if it's 1)
901 | #
902 | # source://diff-lcs//lib/diff/lcs/hunk.rb#311
903 | def unified_range(mode, last); end
904 | end
905 |
906 | # source://diff-lcs//lib/diff/lcs/hunk.rb#10
907 | Diff::LCS::Hunk::ED_DIFF_OP_ACTION = T.let(T.unsafe(nil), Hash)
908 |
909 | # source://diff-lcs//lib/diff/lcs/hunk.rb#9
910 | Diff::LCS::Hunk::OLD_DIFF_OP_ACTION = T.let(T.unsafe(nil), Hash)
911 |
912 | # source://diff-lcs//lib/diff/lcs/internals.rb#29
913 | module Diff::LCS::Internals
914 | class << self
915 | # This method will analyze the provided patchset to provide a single-pass
916 | # normalization (conversion of the array form of Diff::LCS::Change objects to
917 | # the object form of same) and detection of whether the patchset represents
918 | # changes to be made.
919 | #
920 | # source://diff-lcs//lib/diff/lcs/internals.rb#102
921 | def analyze_patchset(patchset, depth = T.unsafe(nil)); end
922 |
923 | # Examine the patchset and the source to see in which direction the
924 | # patch should be applied.
925 | #
926 | # WARNING: By default, this examines the whole patch, so this could take
927 | # some time. This also works better with Diff::LCS::ContextChange or
928 | # Diff::LCS::Change as its source, as an array will cause the creation
929 | # of one of the above.
930 | #
931 | # source://diff-lcs//lib/diff/lcs/internals.rb#147
932 | def intuit_diff_direction(src, patchset, limit = T.unsafe(nil)); end
933 |
934 | # Compute the longest common subsequence between the sequenced
935 | # Enumerables +a+ and +b+. The result is an array whose contents is such
936 | # that
937 | #
938 | # result = Diff::LCS::Internals.lcs(a, b)
939 | # result.each_with_index do |e, i|
940 | # assert_equal(a[i], b[e]) unless e.nil?
941 | # end
942 | #
943 | # source://diff-lcs//lib/diff/lcs/internals.rb#41
944 | def lcs(a, b); end
945 |
946 | private
947 |
948 | # If +vector+ maps the matching elements of another collection onto this
949 | # Enumerable, compute the inverse of +vector+ that maps this Enumerable
950 | # onto the collection. (Currently unused.)
951 | #
952 | # source://diff-lcs//lib/diff/lcs/internals.rb#286
953 | def inverse_vector(a, vector); end
954 |
955 | # Returns a hash mapping each element of an Enumerable to the set of
956 | # positions it occupies in the Enumerable, optionally restricted to the
957 | # elements specified in the range of indexes specified by +interval+.
958 | #
959 | # source://diff-lcs//lib/diff/lcs/internals.rb#298
960 | def position_hash(enum, interval); end
961 |
962 | # Find the place at which +value+ would normally be inserted into the
963 | # Enumerable. If that place is already occupied by +value+, do nothing
964 | # and return +nil+. If the place does not exist (i.e., it is off the end
965 | # of the Enumerable), add it to the end. Otherwise, replace the element
966 | # at that point with +value+. It is assumed that the Enumerable's values
967 | # are numeric.
968 | #
969 | # This operation preserves the sort order.
970 | #
971 | # source://diff-lcs//lib/diff/lcs/internals.rb#252
972 | def replace_next_larger(enum, value, last_index = T.unsafe(nil)); end
973 | end
974 | end
975 |
976 | # This will produce a simple array of diff change objects. Each element in
977 | # the #diffs array is a single ContextChange. In the set of #diffs provided
978 | # by SDiffCallbacks, both old and new objects will be presented for both
979 | # changed and unchanged objects. +nil+ will be substituted
980 | # for a discarded object.
981 | #
982 | # The diffset produced by this callback, when provided to Diff::LCS#sdiff,
983 | # will compute and display the necessary components to show two sequences
984 | # and their minimized differences side by side, just like the Unix utility
985 | # +sdiff+.
986 | #
987 | # same same
988 | # before | after
989 | # old < -
990 | # - > new
991 | #
992 | # seq1 = %w(a b c e h j l m n p)
993 | # seq2 = %w(b c d e f j k l m r s t)
994 | #
995 | # diffs = Diff::LCS.sdiff(seq1, seq2)
996 | # # This example shows a simplified array format.
997 | # # [ [ "-", [ 0, "a"], [ 0, nil ] ],
998 | # # [ "=", [ 1, "b"], [ 0, "b" ] ],
999 | # # [ "=", [ 2, "c"], [ 1, "c" ] ],
1000 | # # [ "+", [ 3, nil], [ 2, "d" ] ],
1001 | # # [ "=", [ 3, "e"], [ 3, "e" ] ],
1002 | # # [ "!", [ 4, "h"], [ 4, "f" ] ],
1003 | # # [ "=", [ 5, "j"], [ 5, "j" ] ],
1004 | # # [ "+", [ 6, nil], [ 6, "k" ] ],
1005 | # # [ "=", [ 6, "l"], [ 7, "l" ] ],
1006 | # # [ "=", [ 7, "m"], [ 8, "m" ] ],
1007 | # # [ "!", [ 8, "n"], [ 9, "r" ] ],
1008 | # # [ "!", [ 9, "p"], [ 10, "s" ] ],
1009 | # # [ "+", [ 10, nil], [ 11, "t" ] ] ]
1010 | #
1011 | # The result of this operation is similar to that of
1012 | # Diff::LCS::ContextDiffCallbacks. They may be compared as:
1013 | #
1014 | # s = Diff::LCS.sdiff(seq1, seq2).reject { |e| e.action == "=" }
1015 | # c = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks).flatten(1)
1016 | #
1017 | # s == c # -> true
1018 | #
1019 | # === Use
1020 | #
1021 | # This callback object must be initialised and is used by the Diff::LCS#sdiff
1022 | # method.
1023 | #
1024 | # cbo = Diff::LCS::SDiffCallbacks.new
1025 | # Diff::LCS.LCS(seq1, seq2, cbo)
1026 | #
1027 | # As with the other initialisable callback objects,
1028 | # Diff::LCS::SDiffCallbacks can be initialised with a block. As there is no
1029 | # "fininishing" to be done, this has no effect on the state of the object.
1030 | #
1031 | # cbo = Diff::LCS::SDiffCallbacks.new { |tcbo| Diff::LCS.LCS(seq1, seq2, tcbo) }
1032 | #
1033 | # === Simplified Array Format
1034 | #
1035 | # The simplified array format used in the example above can be obtained
1036 | # with:
1037 | #
1038 | # require 'pp'
1039 | # pp diffs.map { |e| e.to_a }
1040 | #
1041 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#301
1042 | class Diff::LCS::SDiffCallbacks
1043 | # :yields self:
1044 | #
1045 | # @return [SDiffCallbacks] a new instance of SDiffCallbacks
1046 | # @yield [_self]
1047 | # @yieldparam _self [Diff::LCS::SDiffCallbacks] the object that the method was called on
1048 | #
1049 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#305
1050 | def initialize; end
1051 |
1052 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#322
1053 | def change(event); end
1054 |
1055 | # Returns the difference set collected during the diff process.
1056 | #
1057 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#303
1058 | def diffs; end
1059 |
1060 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#314
1061 | def discard_a(event); end
1062 |
1063 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#318
1064 | def discard_b(event); end
1065 |
1066 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#310
1067 | def match(event); end
1068 | end
1069 |
1070 | # An alias for DefaultCallbacks that is used in
1071 | # Diff::LCS#traverse_sequences.
1072 | #
1073 | # Diff::LCS.LCS(seq1, seq2, Diff::LCS::SequenceCallbacks)
1074 | #
1075 | # source://diff-lcs//lib/diff/lcs/callbacks.rb#44
1076 | Diff::LCS::SequenceCallbacks = Diff::LCS::DefaultCallbacks
1077 |
1078 | # source://diff-lcs//lib/diff/lcs.rb#52
1079 | Diff::LCS::VERSION = T.let(T.unsafe(nil), String)
1080 |
--------------------------------------------------------------------------------