{"id":13878728,"url":"https://github.com/okuramasafumi/tiny_hooks","last_synced_at":"2025-07-14T03:42:31.813Z","repository":{"id":45275726,"uuid":"356277057","full_name":"okuramasafumi/tiny_hooks","owner":"okuramasafumi","description":"Hook control for developers, by a developer","archived":false,"fork":false,"pushed_at":"2024-11-11T13:33:14.000Z","size":50,"stargazers_count":33,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-07-14T02:18:30.586Z","etag":null,"topics":["callbacks","hooks","ruby","tiny"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/okuramasafumi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-04-09T13:16:16.000Z","updated_at":"2024-11-11T13:33:18.000Z","dependencies_parsed_at":"2025-01-14T16:16:57.671Z","dependency_job_id":"e198759f-30fa-40fa-9f06-f8aaa9bef204","html_url":"https://github.com/okuramasafumi/tiny_hooks","commit_stats":{"total_commits":51,"total_committers":4,"mean_commits":12.75,"dds":0.07843137254901966,"last_synced_commit":"e39dcb1345d7ded51b27f00ffec062491f8d90b8"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/okuramasafumi/tiny_hooks","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/okuramasafumi%2Ftiny_hooks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/okuramasafumi%2Ftiny_hooks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/okuramasafumi%2Ftiny_hooks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/okuramasafumi%2Ftiny_hooks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/okuramasafumi","download_url":"https://codeload.github.com/okuramasafumi/tiny_hooks/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/okuramasafumi%2Ftiny_hooks/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265237790,"owners_count":23732520,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["callbacks","hooks","ruby","tiny"],"created_at":"2024-08-06T08:01:57.922Z","updated_at":"2025-07-14T03:42:31.779Z","avatar_url":"https://github.com/okuramasafumi.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"[![Ruby](https://github.com/okuramasafumi/tiny_hooks/actions/workflows/main.yml/badge.svg)](https://github.com/okuramasafumi/tiny_hooks/actions/workflows/main.yml)\n\n# TinyHooks\n\nA tiny gem to define hooks.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'tiny_hooks'\n```\n\nAnd then execute:\n\n    $ bundle install\n\nOr install it yourself as:\n\n    $ gem install tiny_hooks\n\n## Usage\n\n`include TinyHooks` in your class/module and you're all set to use `define_hook`!\n\n```ruby\nclass MyClass\n  include TinyHooks\n\n  def my_method\n    puts 'my method'\n  end\n\n  define_hook :before, :my_method do\n    puts 'my before hook'\n  end\nend\n\nMyClass.new.my_method\n# =\u003e \"my before hook\\nmy method\\n\"\n```\n\nYou can also call `define_hook` with method name as a third argument.\n\n```ruby\nclass MyClass\n  include TinyHooks\n\n  def my_method\n    puts 'my method'\n  end\n\n  def my_before_hook\n    puts 'my before hook'\n  end\n\n  define_hook :before, :my_method, :my_before_hook\nend\n\nMyClass.new.my_method\n# =\u003e \"my before hook\\nmy method\\n\"\n```\n\nYou can define hooks for class methods as well.\n\n```ruby\nclass MyClass\n  include TinyHooks\n\n  def self.my_method\n    puts 'my method'\n  end\n\n  define_hook :before, :my_method, class_method: true do\n    puts 'my before hook'\n  end\nend\n\nMyClass.my_method\n# =\u003e \"my before hook\\nmy method\\n\"\n```\n\nTinyHooks shines when the class/module is the base class/module of your library and your users will inherit/include it. In these cases, end users can define hooks to the methods you provide. The only thing you have to do is to provide the list of methods.\n\n### Halting\n\nYou can halt hook and method body execution by `throw`ing `:abort`.\n\n```ruby\nclass MyClass\n  include TinyHooks\n\n  def my_method\n    puts 'my method'\n  end\n\n  define_hook :before, :my_method do\n    throw :abort\n    puts 'my before hook'\n  end\nend\n\nMyClass.new.my_method\n# =\u003e \"\"\n```\n\nYou can change how to halt from two options: throwing `:abort` and returning `false`. This can be done via `terminator` option.\n\n```ruby\nclass MyClass\n  include TinyHooks\n\n  def my_method\n    puts 'my method'\n  end\n\n  define_hook :before, :my_method, terminator: :return_false do\n    false\n  end\nend\n\nMyClass.new.my_method\n# =\u003e \"\"\n```\n\n### Targeting for hooks\n\nYou can limit the targets for hooks in two ways. You can enable hooks for public methods only by using `public_only!` method and include/exclude targets with Regexp pattern by using `targets!` method.\n\n```ruby\nclass MyClass\n  include TinyHooks\n\n  def my_method\n    puts 'my method'\n  end\n\n  private\n\n  def my_private_method\n    puts 'my private method'\n  end\nend\n\nclass MyClassWithPublicOnly \u003c MyClass\n  public_only!\n\n  define_hook :before, :my_private_method do\n    puts 'my_private_method'\n  end\n  # =\u003e This causes PrivateError\nend\n\nclass MyClassWithExclude \u003c MyClass\n  target! exclude_pattern: /my_method/\n\n  define_hook :before, :my_method do\n    puts 'my_method'\n  end\n  # =\u003e This causes TargetError\nend\n```\n\nYou can call `include_private!` method to disable the effect of `public_only!`.\n\n### Conditional hooks\n\nYou can add `if` option to `define_hook` call. `if` option must be a Proc and is evaluated in context of an instance.\n\n```ruby\nclass MyClass\n  include TinyHooks\n\n  def initialize(hook_enabled = true)\n    @hook_enabled = hook_enabled\n  end\n\n  def my_method\n    puts 'my method'\n  end\n\n  def hook_enabled?\n    @hook_enabled\n  end\n\n  define_hook :before, :my_method, if: -\u003e { hook_enabled? } do\n    puts 'my before hook'\n  end\nend\n\nMyClass.new(true).my_method\n# =\u003e \"my before hook\\nmy method\\n\"\n\nMyClass.new(false).my_method\n# =\u003e \"my method\\n\"\n```\n\n## Differences between TinyHooks and ActiveSupport::Callbacks\n\nWhile `TinyHooks` and `ActiveSupport::Callbacks` share the same purpose, there are a few major differences.\n\n### Differences in usage\n\n* While `ActiveSupport::Callbacks` has a set of methods for callbacks to work, `TinyHooks` has only one method.\n* You can apply callbacks/hooks into any existing methods without any changes with `TinyHooks`, while you need to change methods to call `run_callbacks` method within them to apply callbacks with `ActiveSupport::Callbacks`.\n\n### Differences in performance\n\nAccording to the [benchmark](https://github.com/okuramasafumi/tiny_hooks/blob/main/benchmark/compare_to_as_callbacks.rb), `TinyHooks` is 1.6 times as fast as `ActiveSupport::Callbacks` when before and after callbacks are applied, and twice as fast when no callbacks are applied.\n\nThe result on my machine:\n\n```\nWarming up --------------------------------------\n       ActiveSupport   246.181k i/s -    256.956k times in 1.043769s (4.06μs/i)\n           TinyHooks   282.834k i/s -    293.502k times in 1.037719s (3.54μs/i)\nCalculating -------------------------------------\n       ActiveSupport   230.196k i/s -    738.542k times in 3.208320s (4.34μs/i)\n           TinyHooks   373.057k i/s -    848.501k times in 2.274453s (2.68μs/i)\n\nComparison:\n           TinyHooks:    373057.2 i/s\n       ActiveSupport:    230195.9 i/s - 1.62x  slower\n\nWarming up --------------------------------------\nActiveSupport no callback set     1.992M i/s -      2.096M times in 1.052258s (501.99ns/i)\n    TinyHooks no callback set     3.754M i/s -      3.791M times in 1.009753s (266.39ns/i)\n                        Plain     3.852M i/s -      3.955M times in 1.026654s (259.57ns/i)\nCalculating -------------------------------------\nActiveSupport no callback set     2.005M i/s -      5.976M times in 2.980861s (498.79ns/i)\n    TinyHooks no callback set     4.025M i/s -     11.262M times in 2.798054s (248.46ns/i)\n                        Plain     3.765M i/s -     11.557M times in 3.069944s (265.63ns/i)\n\nComparison:\n    TinyHooks no callback set:   4024854.4 i/s\n                        Plain:   3764695.4 i/s - 1.07x  slower\nActiveSupport no callback set:   2004848.9 i/s - 2.01x  slower\n```\n\n### Differences in functionality\n\nThere are few things TinyHooks doesn't cover. For example, TinyHooks doesn't support `unless` option in `define_hook` method or Symbol as a callback body since they are just syntax sugar.\n\nOne of the features TinyHooks doesn't have is `reset_callbacks` which resets all callbacks with given condition. In order to do this, you must call `restore_original` method in iteration.\n\n### Conclusion\n\nIn short, in most cases, TinyHooks is simpler, easier and faster solution.\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/okuramasafumi/tiny_hooks. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/okuramasafumi/tiny_hooks/blob/main/CODE_OF_CONDUCT.md).\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n\n## Code of Conduct\n\nEveryone interacting in the TinyHooks project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/okuramasafumi/tiny_hooks/blob/main/CODE_OF_CONDUCT.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fokuramasafumi%2Ftiny_hooks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fokuramasafumi%2Ftiny_hooks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fokuramasafumi%2Ftiny_hooks/lists"}