{"id":15608895,"url":"https://github.com/serradura/u-observers","last_synced_at":"2025-09-09T06:53:54.065Z","repository":{"id":47578596,"uuid":"298568213","full_name":"serradura/u-observers","owner":"serradura","description":"Simple and powerful implementation of the observer pattern.","archived":false,"fork":false,"pushed_at":"2021-08-23T14:50:49.000Z","size":194,"stargazers_count":31,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-29T08:23:46.670Z","etag":null,"topics":["activemodel","activerecord","observer-pattern","pubsub","ruby"],"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/serradura.png","metadata":{"files":{"readme":"README.md","changelog":null,"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}},"created_at":"2020-09-25T12:36:42.000Z","updated_at":"2023-06-03T02:41:27.000Z","dependencies_parsed_at":"2022-08-30T08:41:25.110Z","dependency_job_id":null,"html_url":"https://github.com/serradura/u-observers","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/serradura%2Fu-observers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/serradura%2Fu-observers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/serradura%2Fu-observers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/serradura%2Fu-observers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/serradura","download_url":"https://codeload.github.com/serradura/u-observers/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249707601,"owners_count":21313882,"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":["activemodel","activerecord","observer-pattern","pubsub","ruby"],"created_at":"2024-10-03T05:40:25.771Z","updated_at":"2025-04-19T13:50:44.547Z","avatar_url":"https://github.com/serradura.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ch1 align=\"center\"\u003e👀 μ-observers\u003c/h1\u003e\n  \u003cp align=\"center\"\u003e\u003ci\u003eSimple and powerful implementation of the observer pattern.\u003c/i\u003e\u003c/p\u003e\n  \u003cbr\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/ruby-\u003e%3D%202.2.0-ruby.svg?colorA=99004d\u0026colorB=cc0066\" alt=\"Ruby\"\u003e\n\n  \u003ca href=\"https://rubygems.org/gems/u-observers\"\u003e\n    \u003cimg alt=\"Gem\" src=\"https://img.shields.io/gem/v/u-observers.svg?style=flat-square\"\u003e\n  \u003c/a\u003e\n\n  \u003ca href=\"https://github.com/serradura/u-observers/actions/workflows/ci.yml\"\u003e\n    \u003cimg alt=\"Build Status\" src=\"https://github.com/serradura/u-observers/actions/workflows/ci.yml/badge.svg\"\u003e\n  \u003c/a\u003e\n\n  \u003ca href=\"https://codeclimate.com/github/serradura/u-observers/maintainability\"\u003e\n    \u003cimg alt=\"Maintainability\" src=\"https://api.codeclimate.com/v1/badges/e72ffa84bc95c59823f2/maintainability\"\u003e\n  \u003c/a\u003e\n\n  \u003ca href=\"https://codeclimate.com/github/serradura/u-observers/test_coverage\"\u003e\n    \u003cimg alt=\"Test Coverage\" src=\"https://api.codeclimate.com/v1/badges/e72ffa84bc95c59823f2/test_coverage\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nThis gem implements the observer pattern [[1]](https://en.wikipedia.org/wiki/Observer_pattern)[[2]](https://refactoring.guru/design-patterns/observer) (also known as publish/subscribe). It provides a simple mechanism for one object to inform a set of interested third-party objects when its state changes.\n\nRuby's standard library [has an abstraction](https://ruby-doc.org/stdlib-2.7.1/libdoc/observer/rdoc/Observable.html) that enables you to use this pattern. But its design can conflict with other mainstream libraries, like the [`ActiveModel`/`ActiveRecord`](https://api.rubyonrails.org/classes/ActiveModel/Dirty.html#method-i-changed), which also has the [`changed`](https://ruby-doc.org/stdlib-2.7.1/libdoc/observer/rdoc/Observable.html#method-i-changed) method. In this case, the behavior of the Stdlib will be compromised.\n\nBecause of this issue, I decided to create a gem that encapsulates the pattern without changing the object's implementation so much. The `Micro::Observers` includes just one instance method in the target class (its instance will be the observed subject/object).\n\n\u003e **Note:** Você entende português? 🇧🇷\u0026nbsp;🇵🇹 Verifique o [README traduzido em pt-BR](https://github.com/serradura/u-observers/blob/main/README.pt-BR.md).\n\n# Table of contents \u003c!-- omit in toc --\u003e\n- [Installation](#installation)\n- [Compatibility](#compatibility)\n  - [Usage](#usage)\n    - [Sharing a context with your observers](#sharing-a-context-with-your-observers)\n    - [Sharing data when notifying the observers](#sharing-data-when-notifying-the-observers)\n    - [What is a `Micro::Observers::Event`?](#what-is-a-microobserversevent)\n    - [Using a callable as an observer](#using-a-callable-as-an-observer)\n    - [Calling the observers](#calling-the-observers)\n    - [Notifying observers without marking them as changed](#notifying-observers-without-marking-them-as-changed)\n    - [Defining observers that execute only once](#defining-observers-that-execute-only-once)\n      - [`observers.attach(*args, perform_once: true)`](#observersattachargs-perform_once-true)\n      - [`observers.once(event:, call:, ...)`](#observersonceevent-call-)\n    - [Defining observers using blocks](#defining-observers-using-blocks)\n      - [`observers.on()`](#observerson)\n      - [`observers.once()`](#observersonce)\n      - [Replacing a block by a `lambda`/`proc`](#replacing-a-block-by-a-lambdaproc)\n    - [Detaching observers](#detaching-observers)\n    - [ActiveRecord and ActiveModel integrations](#activerecord-and-activemodel-integrations)\n      - [`.notify_observers_on()`](#notify_observers_on)\n      - [`.notify_observers()`](#notify_observers)\n  - [Development](#development)\n  - [Contributing](#contributing)\n  - [License](#license)\n  - [Code of Conduct](#code-of-conduct)\n\n# Installation\n\nAdd this line to your application's Gemfile and `bundle install`:\n\n```ruby\ngem 'u-observers'\n```\n\n# Compatibility\n\n| u-observers | branch  | ruby     | activerecord  |\n| ----------- | ------- | -------- | ------------- |\n| unreleased  | main    | \u003e= 2.2.0 | \u003e= 3.2, \u003c 6.1 |\n| 2.3.0       | v2.x    | \u003e= 2.2.0 | \u003e= 3.2, \u003c 6.1 |\n| 1.0.0       | v1.x    | \u003e= 2.2.0 | \u003e= 3.2, \u003c 6.1 |\n\n\u003e **Note**: The ActiveRecord isn't a dependency, but you could add a module to enable some static methods that were designed to be used with its [callbacks](https://guides.rubyonrails.org/active_record_callbacks.html).\n\n[⬆️ \u0026nbsp; Back to Top](#table-of-contents-)\n\n## Usage\n\nAny class with `Micro::Observers` module included can notify events to attached observers.\n\n```ruby\nrequire 'securerandom'\n\nclass Order\n  include Micro::Observers\n\n  attr_reader :code\n\n  def initialize\n    @code, @status = SecureRandom.alphanumeric, :draft\n  end\n\n  def canceled?\n    @status == :canceled\n  end\n\n  def cancel!\n    return self if canceled?\n\n    @status = :canceled\n\n    observers.subject_changed!\n    observers.notify(:canceled) and return self\n  end\nend\n\nmodule OrderEvents\n  def self.canceled(order)\n    puts \"The order #(#{order.code}) has been canceled.\"\n  end\nend\n\norder = Order.new\n#\u003cOrder:0x00007fb5dd8fce70 @code=\"X0o9yf1GsdQFvLR4\", @status=:draft\u003e\n\norder.observers.attach(OrderEvents)  # attaching multiple observers. e.g. observers.attach(A, B, C)\n# \u003c#Micro::Observers::Set @subject=#\u003cOrder:0x00007fb5dd8fce70\u003e @subject_changed=false @subscribers=[OrderEvents]\u003e\n\norder.canceled?\n# false\n\norder.cancel!\n# The message below will be printed by the observer (OrderEvents):\n# The order #(X0o9yf1GsdQFvLR4) has been canceled\n\norder.canceled?\n# true\n\norder.observers.detach(OrderEvents)  # detaching multiple observers. e.g. observers.detach(A, B, C)\n# \u003c#Micro::Observers::Set @subject=#\u003cOrder:0x00007fb5dd8fce70\u003e @subject_changed=false @subscribers=[]\u003e\n\norder.canceled?\n# true\n\norder.observers.subject_changed!\norder.observers.notify(:canceled) # nothing will happen, because there are no observers attached.\n```\n\n**Highlights of the previous example:**\n\nTo avoid an undesired behavior, you need to mark the subject as changed before notifying your observers about some event.\n\nYou can do this when using the `#subject_changed!` method. It will automatically mark the subject as changed.\n\nBut if you need to apply some conditional to mark a change, you can use the `#subject_changed` method. e.g. `observers.subject_changed(name != new_name)`\n\nThe `#notify` method always requires an event to make a broadcast. So, if you try to use it without one or more events (symbol values) you will get an exception.\n\n```ruby\norder.observers.notify\n# ArgumentError (no events (expected at least 1))\n```\n\n[⬆️ \u0026nbsp; Back to Top](#table-of-contents-)\n\n### Sharing a context with your observers\n\nTo share a context value (any kind of Ruby object) with one or more observers, you will need to use the `:context` keyword as the last argument of the `#attach` method. This feature gives you a unique opportunity to share a value in the attaching moment.\n\nWhen the observer method receives two arguments, the first one will be the subject, and the second one an instance of `Micro::Observers::Event` that will have the given context value.\n\n```ruby\nclass Order\n  include Micro::Observers\n\n  def cancel!\n    observers.subject_changed!\n    observers.notify(:canceled)\n    self\n  end\nend\n\nmodule OrderEvents\n  def self.canceled(order, event)\n    puts \"The order #(#{order.object_id}) has been canceled. (from: #{event.context[:from]})\" # event.ctx is an alias for event.context\n  end\nend\n\norder = Order.new\norder.observers.attach(OrderEvents, context: { from: 'example #2' }) # attaching multiple observers. e.g. observers.attach(A, B, context: {hello: :world})\norder.cancel!\n# The message below will be printed by the observer (OrderEvents):\n# The order #(70196221441820) has been canceled. (from: example #2)\n```\n\n[⬆️ \u0026nbsp; Back to Top](#table-of-contents-)\n\n### Sharing data when notifying the observers\n\nAs previously mentioned, the [`event context`](#sharing-a-context-with-your-observers) is a value that is stored when you attach your observer. But sometimes, it will be useful to send some additional data when broadcasting an event to the observers. The `event data` gives you this unique opportunity to share some value at the the notification moment.\n\n```ruby\nclass Order\n  include Micro::Observers\nend\n\nmodule OrderHandler\n  def self.changed(order, event)\n    puts \"The order #(#{order.object_id}) received the number #{event.data} from #{event.ctx[:from]}.\"\n  end\nend\n\norder = Order.new\norder.observers.attach(OrderHandler, context: { from: 'example #3' })\norder.observers.subject_changed!\norder.observers.notify(:changed, data: 1)\n# The message below will be printed by the observer (OrderHandler):\n# The order #(70196221441820) received the number 1 from example #3.\n```\n\n[⬆️ \u0026nbsp; Back to Top](#table-of-contents-)\n\n### What is a `Micro::Observers::Event`?\n\nThe `Micro::Observers::Event` is the event payload. Follow below all of its properties:\n\n- `#name` will be the broadcasted event.\n- `#subject` will be the observed object.\n- `#context` will be [the context data](#sharing-a-context-with-your-observers) that was defined at the moment that you attach the observer.\n- `#data` will be [the value that was shared in the observers' notification](#sharing-data-when-notifying-the-observers).\n- `#ctx` is an alias for the `#context` method.\n- `#subj` is an alias for the `#subject` method.\n\n[⬆️ \u0026nbsp; Back to Top](#table-of-contents-)\n\n### Using a callable as an observer\n\nThe `observers.on()` method enables you to attach a callable as an observer.\n\nUsually, a callable has a well-defined responsibility (do only one thing), because of this, it tends to be more [SRP (Single-responsibility principle)](https://en.wikipedia.org/wiki/Single-responsibility_principle) friendly than a conventional observer (that could have N methods to respond to different kinds of notification).\n\nThis method receives the below options:\n1. `:event` the expected event name.\n2. `:call` the callable object itself.\n3. `:with` (optional) it can define the value which will be used as the callable object's argument. So, if it is a `Proc`, a `Micro::Observers::Event` instance will be received as the `Proc` argument, and its output will be the callable argument. But if this option wasn't defined, the `Micro::Observers::Event` instance will be the callable argument.\n4. `:context` will be the context data that was defined in the moment that you attach the observer.\n\n```ruby\nclass Person\n  include Micro::Observers\n\n  attr_reader :name\n\n  def initialize(name)\n    @name = name\n  end\n\n  def name=(new_name)\n    return unless observers.subject_changed(new_name != @name)\n\n    @name = new_name\n\n    observers.notify(:name_has_been_changed)\n  end\nend\n\nPrintPersonName = -\u003e (data) do\n  puts(\"Person name: #{data.fetch(:person).name}, number: #{data.fetch(:number)}\")\nend\n\nperson = Person.new('Rodrigo')\n\nperson.observers.on(\n  event: :name_has_been_changed,\n  call: PrintPersonName,\n  with: -\u003e event { {person: event.subject, number: event.context} },\n  context: rand\n)\n\nperson.name = 'Serradura'\n# The message below will be printed by the observer (PrintPersonName):\n# Person name: Serradura, number: 0.5018509191706862\n```\n\n[⬆️ \u0026nbsp; Back to Top](#table-of-contents-)\n\n### Calling the observers\n\nYou can use a callable (a class, module, or object that responds to the call method) to be your observers.\nTo do this, you only need to make use of the method `#call` instead of `#notify`.\n\n```ruby\nclass Order\n  include Micro::Observers\n\n  def cancel!\n    observers.subject_changed!\n    observers.call # in practice, this is a shortcut to observers.notify(:call)\n    self\n  end\nend\n\nNotifyAfterCancel = -\u003e (order) { puts \"The order #(#{order.object_id}) has been canceled.\" }\n\norder = Order.new\norder.observers.attach(NotifyAfterCancel)\norder.cancel!\n# The message below will be printed by the observer (NotifyAfterCancel):\n# The order #(70196221441820) has been canceled.\n```\n\n\u003e **Note**: The `observers.call` can receive one or more events, but in this case, the default event (`call`) won't be transmitted.\n\n[⬆️ \u0026nbsp; Back to Top](#table-of-contents-)\n\n### Notifying observers without marking them as changed\n\nThis feature needs to be used with caution!\n\nIf you use the methods `#notify!` or `#call!` you won't need to mark observers with `#subject_changed`.\n\n[⬆️ \u0026nbsp; Back to Top](#table-of-contents-)\n\n### Defining observers that execute only once\n\nThere are two ways to attach an observer and define it to be performed only once.\n\nThe first way to do this is passing the `perform_once: true` option to the `observers.attach()` method. e.g.\n\n#### `observers.attach(*args, perform_once: true)`\n\n```ruby\nclass Order\n  include Micro::Observers\n\n  def cancel!\n    observers.notify!(:canceled)\n  end\nend\n\nmodule OrderNotifications\n  def self.canceled(order)\n    puts \"The order #(#{order.object_id}) has been canceled.\"\n  end\nend\n\norder = Order.new\norder.observers.attach(OrderNotifications, perform_once: true) # you can also pass an array of observers with this option\n\norder.observers.some? # true\norder.cancel!         # The order #(70291642071660) has been canceled.\n\norder.observers.some? # false\norder.cancel!         # Nothing will happen because there aren't observers.\n```\n\n#### `observers.once(event:, call:, ...)`\n\nThe second way to achieve this is using `observers.once()` that has the same API of [`observers.on()`](#using-a-callable-as-an-observer). But the difference of the `#once()` method is that it will remove the observer after its execution.\n\n```ruby\nclass Order\n  include Micro::Observers\n\n  def cancel!\n    observers.notify!(:canceled)\n  end\nend\n\nmodule NotifyAfterCancel\n  def self.call(event)\n    puts \"The order #(#{event.subject.object_id}) has been canceled.\"\n  end\nend\n\norder = Order.new\norder.observers.once(event: :canceled, call: NotifyAfterCancel)\n\norder.observers.some? # true\norder.cancel!         # The order #(70301497466060) has been canceled.\n\norder.observers.some? # false\norder.cancel!         # Nothing will happen because there aren't observers.\n```\n\n[⬆️ \u0026nbsp; Back to Top](#table-of-contents-)\n\n### Defining observers using blocks\n\nThe methods `#on()` and `#once()` can receive an event (`symbol`) and a block to define observers.\n\n#### `observers.on()`\n\n```ruby\nclass Order\n  include Micro::Observers\n\n  def cancel!\n    observers.notify!(:canceled)\n  end\nend\n\norder = Order.new\norder.observers.on(:canceled) do |event|\n  puts \"The order #(#{event.subject.object_id}) has been canceled.\"\nend\n\norder.observers.some? # true\n\norder.cancel!         # The order #(70301497466060) has been canceled.\n\norder.observers.some? # true\n```\n\n#### `observers.once()`\n\n```ruby\nclass Order\n  include Micro::Observers\n\n  def cancel!\n    observers.notify!(:canceled)\n  end\nend\n\norder = Order.new\norder.observers.once(:canceled) do |event|\n  puts \"The order #(#{event.subject.object_id}) has been canceled.\"\nend\n\norder.observers.some? # true\n\norder.cancel!         # The order #(70301497466060) has been canceled.\n\norder.observers.some? # false\n```\n\n#### Replacing a block by a `lambda`/`proc`\n\nRuby allows you to replace any block with a `lambda`/`proc`. So, it will be possible to use this kind of feature to define your observers. e.g.\n\n```ruby\nclass Order\n  include Micro::Observers\n\n  def cancel!\n    observers.notify!(:canceled)\n  end\nend\n\nNotifyAfterCancel = -\u003e event { puts \"The order #(#{event.subject.object_id}) has been canceled.\" }\n\norder = Order.new\norder.observers.once(:canceled, \u0026NotifyAfterCancel)\n\norder.observers.some? # true\norder.cancel!         # The order #(70301497466060) has been canceled.\n\norder.observers.some? # false\norder.cancel!         # Nothing will happen because there aren't observers.\n```\n\n[⬆️ \u0026nbsp; Back to Top](#table-of-contents-)\n\n### Detaching observers\n\nAs shown in the first example, you can use the `observers.detach()` to remove observers.\n\nBut, there is an alternative method to remove observer objects or remove callables by their event names. The method to do this is: `observers.off()`.\n\n```ruby\nclass Order\n  include Micro::Observers\nend\n\nNotifyAfterCancel = -\u003e {}\n\nmodule OrderNotifications\n  def self.canceled(_order)\n  end\nend\n\norder = Order.new\norder.observers.on(:canceled) { |_event| }\norder.observers.on(event: :canceled, call: NotifyAfterCancel)\norder.observers.attach(OrderNotifications)\n\norder.observers.some? # true\norder.observers.count # 3\n\norder.observers.off(:canceled) # removing the callable (NotifyAfterCancel).\norder.observers.some? # true\norder.observers.count # 1\n\norder.observers.off(OrderNotifications)\norder.observers.some? # false\norder.observers.count # 0\n```\n\n[⬆️ \u0026nbsp; Back to Top](#table-of-contents-)\n\n### ActiveRecord and ActiveModel integrations\n\nTo make use of this feature you need to require an additional module.\n\nGemfile example:\n```ruby\ngem 'u-observers', require: 'u-observers/for/active_record'\n```\n\nThis feature will expose modules that could be used to add macros (static methods) that were designed to work with `ActiveModel`/`ActiveRecord` callbacks. e.g:\n\n#### `.notify_observers_on()`\n\nThe `notify_observers_on` allows you to define one or more `ActiveModel`/`ActiveRecord` callbacks, that will be used to notify your observers.\n\n```ruby\nclass Post \u003c ActiveRecord::Base\n  include ::Micro::Observers::For::ActiveRecord\n\n  notify_observers_on(:after_commit) # using multiple callbacks. e.g. notify_observers_on(:before_save, :after_commit)\n\n  # The method above does the same as the commented example below.\n  #\n  # after_commit do |record|\n  #  record.subject_changed!\n  #  record.notify(:after_commit)\n  # end\nend\n\nmodule TitlePrinter\n  def self.after_commit(post)\n    puts \"Title: #{post.title}\"\n  end\nend\n\nmodule TitlePrinterWithContext\n  def self.after_commit(post, event)\n    puts \"Title: #{post.title} (from: #{event.context[:from]})\"\n  end\nend\n\nPost.transaction do\n  post = Post.new(title: 'Hello world')\n  post.observers.attach(TitlePrinter, TitlePrinterWithContext, context: { from: 'example #6' })\n  post.save\nend\n# The message below will be printed by the observers (TitlePrinter, TitlePrinterWithContext):\n# Title: Hello world\n# Title: Hello world (from: example #6)\n```\n\n[⬆️ \u0026nbsp; Back to Top](#table-of-contents-)\n\n#### `.notify_observers()`\n\nThe `notify_observers` allows you to define one or more *events*, that will be used to notify after the execution of some `ActiveModel`/`ActiveRecord` callback.\n\n```ruby\nclass Post \u003c ActiveRecord::Base\n  include ::Micro::Observers::For::ActiveRecord\n\n  after_commit(\u0026notify_observers(:transaction_completed))\n\n  # The method above does the same as the commented example below.\n  #\n  # after_commit do |record|\n  #  record.subject_changed!\n  #  record.notify(:transaction_completed)\n  # end\nend\n\nmodule TitlePrinterWithContext\n  def self.transaction_completed(post, event)\n    puts(\"Title: #{post.title} (from: #{event.ctx[:from]})\")\n  end\nend\n\nPost.transaction do\n  post = Post.new(title: 'Olá mundo')\n\n  post.observers.on(:transaction_completed) { |event| puts(\"Title: #{event.subject.title}\") }\n\n  post.observers.attach(TitlePrinterWithContext, context: { from: 'example #7' })\n\n  post.save\nend\n# The message below will be printed by the observers (TitlePrinter, TitlePrinterWithContext):\n# Title: Olá mundo\n# Title: Olá mundo (from: example #5)\n```\n\n\u003e **Note**: You can use `include ::Micro::Observers::For::ActiveModel` if your class only makes use of the `ActiveModel` and all the previous examples will work.\n\n[⬆️ \u0026nbsp; Back to Top](#table-of-contents-)\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 tags, 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/serradura/u-observers. 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/serradura/u-observers/blob/master/CODE_OF_CONDUCT.md).\n\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 `Micro::Observers` project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/serradura/u-observers/blob/master/CODE_OF_CONDUCT.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fserradura%2Fu-observers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fserradura%2Fu-observers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fserradura%2Fu-observers/lists"}