{"id":14956049,"url":"https://github.com/tarellel/crossbeam","last_synced_at":"2025-10-23T17:01:27.155Z","repository":{"id":61016409,"uuid":"504912085","full_name":"tarellel/crossbeam","owner":"tarellel","description":"An easy way to create and run service objects with callbacks, validations, errors, and responses","archived":false,"fork":false,"pushed_at":"2024-08-04T17:45:49.000Z","size":51,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-01-12T13:55:10.984Z","etag":null,"topics":["ruby","rubygem","rubyonrails","service-object"],"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/tarellel.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":"2022-06-18T17:45:50.000Z","updated_at":"2024-08-04T17:44:50.000Z","dependencies_parsed_at":"2024-01-06T21:26:28.685Z","dependency_job_id":"476bebbc-ac22-4c90-b223-161df5e904e8","html_url":"https://github.com/tarellel/crossbeam","commit_stats":{"total_commits":5,"total_committers":2,"mean_commits":2.5,"dds":"0.19999999999999996","last_synced_commit":"0dde62c3b0cac9c605297fc23ccefc73257108f4"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarellel%2Fcrossbeam","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarellel%2Fcrossbeam/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarellel%2Fcrossbeam/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarellel%2Fcrossbeam/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tarellel","download_url":"https://codeload.github.com/tarellel/crossbeam/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234862691,"owners_count":18898398,"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":["ruby","rubygem","rubyonrails","service-object"],"created_at":"2024-09-24T13:12:14.231Z","updated_at":"2025-10-06T10:31:31.722Z","avatar_url":"https://github.com/tarellel.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Crossbeam\n\nCrossbeam is a gem to making it easy to create and run ruby service objects. It allows you to use validations, errors, etc to take away the troubles associated with service classes.\n\n## Installation\n\nInstall the gem and add to the application's Gemfile by executing:\n\n```shell\nbundle add crossbeam\n```\n\nIf bundler is not being used to manage dependencies, install the gem by executing:\n\n```shell\ngem install crossbeam\n```\n\n## Usage\n\nIn order to too use the Crossbeam service class you will need to add `include Crossbeam` to the class you wish to make a service object.\n\n### Initializers\n\nYou can call and initialize a Crossbeam service call like any other ruby object and \n\n```ruby\nclass ServiceClass\n  include Crossbeam\n\n  def initialize(name, age, other: nil)\n    @age = age\n    @name = name\n    @other = other\n  end\n\n  def call\n    do_something\n  end\n\n  private\n\n  def do_something\n    # .....\n  end\nend\n\n# Calling the service class\nServiceClass.call('James', 12)\n```\n\nCrossbeam also includes [dry-initializer], which allows you to quickly initialize object parameters.\nThis allows you to bypass having to setup an initialize method in order to assign all attributes to instance variables.\n\n```ruby\nclass OtherService\n  include Crossbeam\n\n  param :name, proc(\u0026:to_s)\n  param :age, proc(\u0026:to_i)\n  option :other, default: proc { nil }\n\n  def call\n    do_something\n  end\n\n  private\n\n  def do_something\n    # .....\n    return \"#{@name} is a minor\" if @age \u003c 18\n\n    \"#{@name} is #{@age}\"\n  end\nend\n\n# Calling the service class\nOtherService.call('James', 12)\n```\n\n### Output\n\nIf you want skip assigning the last attribute returned from call to results you can specify a specific attribute to result reference after `#call` has been ran. This can be done by assigning an attribute to be assigned as the results with `output :attribute_name`.\n\n```ruby\nclass OutputSample\n  include Crossbeam\n\n  param  :name, proc(\u0026:to_s)\n  param  :age, default: proc { 10 }\n\n  # Attribute/Instance Variable to return\n  output :age\n\n  def call\n    @age += 1\n    \"Hello, #{name}! You are #{age} years old.\"\n  end\nend\n\noutput = OutputSample.call('James', 12)\noutput.results\n```\n\n### Callbacks\n\nSimilar to Rails actions or models Crossbeam allows you to have before/after callbacks for before `call` is ran. They are completely optional and either one can be used without the other. They before/after references can either by a symbol or a block.\n\n```ruby\nclass SampleClass\n  include Crossbeam\n\n  # Callbacks that will be called before/after #call is referenced\n  before do\n    # setup for #call\n  end\n\n  after :cleanup_script\n\n  def call\n    # .....\n  end\n\n  private\n\n  def cleanup_script\n    # .....\n  end\nend\n\nSampleClass.call\n```\n\n### Errors and Validations\n\n#### Errors\n\n```ruby\nclass ErrorClass\n  include Crossbeam\n\n  def initialize(name, age)\n    @name = name\n    @age = age\n  end\n\n  def call\n    errors.add(:age, \"#{@name} is a minor\") if @age \u003c 18\n    errors.add(:base, 'something something something')\n  end\nend\n\ntest = ErrorClass.call('James', 10)\ntest.errors\n# =\u003e {:age=\u003e[\"James is a minor\"], :base=\u003e[\"something something something\"]}\ntest.errors.full_messages\n# =\u003e [\"Age James is a minor\", \"something something something\"]\ntest.errors.to_s\n# =\u003e Age James is a minor\n# =\u003e something something something\n```\n\n#### Validations\n\n```ruby\nrequire_relative 'crossbeam'\n\nclass AgeCheck\n  include Crossbeam\n\n  option :age, default: proc { 0 }\n\n  validates :age, numericality: { greater_than_or_equal_to: 18, less_than_or_equal_to: 65 }\n\n  def call\n    return 'Minor' unless valid?\n\n    'Adult'\n  end\nend\n\nputs AgeCheck.call(age: 15).errors.full_messages\n# =\u003e [\"Age must be greater than or equal to 18\"]\nputs AgeCheck.call(age: 20).results\n# =\u003e Adult\n```\n\n```ruby\nrequire_relative 'crossbeam'\n\nclass Bar\n  include Crossbeam\n\n  option :age, default: proc { 0 }\n  option :drink\n  option :description, default: proc { '' }\n\n  validates :age, numericality: { greater_than_or_equal_to: 21, less_than_or_equal_to: 65 }\n  validates :drink, inclusion: { in: %w(beer wine whiskey) }\n  validates :description, length: { minimum: 7, message: 'is required' }\n\n  def call\n    return 'Minor' unless valid?\n\n    'Adult'\n  end\nend\n\nafter_hours = Bar.call(age: 15, drink: 'tanqueray')\nputs after_hours.errors.full_messages if after_hours.errors?\n# =\u003e Age must be greater than or equal to 21\n# =\u003e Drink is not included in the list\n# =\u003e Description is required\n```\n\n### Fail\n\nIf a particular condition is come across you may want to cause a service call to fail. This causes any further action within the service call to not be called and the classes result to be set as nil.\n\n```ruby\nclass Something\n  include Crossbeam\n\n  def call\n    fail!('1 is less than 2') unless 1 \u003e 2\n\n    true\n  end\nend\n\ntest = Something.call\ntest.failure? # =\u003e true\nputs test.errors.full_messages # =\u003e ['1 is less than 2']\ntest.result # =\u003e nil\n```\n\nWhen calling `fail!` you need to supply a message/context very similar to an exception description. And when the service call is forced to fail no results should be returned.\n\n### Generators (Rails)\n\nThe Crossbeam service class generator is only available when used with a rails application.\n\nWhen running the generator you will specify the class name for the service object.\n\n```shell\nrails g crossbeam AgeCheck\n```\n\nRunning this will generate a file `app/services/age_check.rb` with the following contents\n\n```ruby\n# frozen_string_literal: true\n\nclass AgeCheck\n  include Crossbeam\n\n  def call\n    # ...\n  end\nend\n```\n\nYou can also specify attributes that you want use with the class.\n\n`rails g crossbeam IdentityCheck address age dob name`\n\n```ruby\nclass IdentityCheck\n  include Crossbeam\n\n  option :address\n  option :age\n  option :dob\n  option :name\n\n  def call\n    # ...\n  end\nend\n```\n\nNot everyone wants to have their classes output to `app/services/**` sometimes you may prefer the class to be output to `app/commands/**`. Depending on the functionality you working on command classes are very regularly used design pattern.\n\nThe generator option in order to generate your classes as a command can be flagged with `--command`.\n\n```shell\nrails g crossbream AgeCheck --command\n```\n\nRunning this will generate a file in `app/commands/age_check.rb` that you can use as a command class.\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/tarellel/crossbeam.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n\nThis project is intended to be a safe, welcoming space for collaboration, and everyone interacting in the project’s codebase and issue tracker is expected to adhere to the [Contributor Covenant code of conduct](https://github.com/tarellel/crossbeam/main/CODE_OF_CONDUCT.md).\n\n## Inspired by\n\n* [Actor](https://github.com/sunny/actor)\n* [Callee](https://github.com/dreikanter/callee)\n* [CivilService](https://github.com/actblue/civil_service)\n* [SimpleCommand](https://github.com/nebulab/simple_command)\n* [u-case](https://github.com/serradura/u-case)\n\n[dry-initializer]: \u003chttps://github.com/dry-rb/dry-initializer\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftarellel%2Fcrossbeam","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftarellel%2Fcrossbeam","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftarellel%2Fcrossbeam/lists"}