Easy HTTP errors
Create HTTP error objects
TypeScript definitions for http-errors
Actions Http Client
Easy as cake e-mail sending from your Node.js applications
TypeScript definitions for serve-static
Node Rest and Http Clients for use with TypeScript
A simple cache for a few of the JS Error constructors.
Recognizes certain classes of webpack errors and cleans, aggregates and prioritizes them to provide a better Developer Experience
Extract meaning from JS Errors
CSV parsing implementing the Node.js `stream.Transform` API
richer JavaScript errors
CSV parsing implementing the Node.js `stream.Transform` API
A lightweight toolset for writing styles in Javascript.
TypeScript definitions for koa
A React component to crop images/videos with easy interactions
the complete solution for node.js command-line programs
Recognizes certain classes of webpack errors and cleans, aggregates and prioritizes them to provide a better Developer Experience
An easy-to-use OCSP client for Node.js
An evented streaming XML parser in JavaScript
Simple JS stack with auto run for node and browsers
It makes simple throw qualified errors.
JSON.parse with context information on error
HTTP server mocking and expectations library for Node.js
Easy way to raise HTTP errors from your ruby application
The Chimera http client offers an easy to learn interface and consistent error handling. It is lightweight, fast and enables you to queue HTTP requests to run them in parallel for better performance and simple aggregating of distributed data. Despite it's simple interface it allows for advanced features like using custom deserializers, loggers, caching requests individiually, and instrumentation support (soon to be implemented).
Library providing a collection of easy-to-use modules, methods, and helpers for interacting with Paystack’s API endpoints. Includes built-in error handling, response parsing and logging. See official paystack docs https://paystack.com/docs/api/
ShipEngine's easy-to-use REST API lets you manage all of your shipping needs without worrying about the complexities of different carrier APIs and protocols. We handle all the heavy lifting so you can focus on providing a first-class shipping experience for your customers at the best possible prices. Each of ShipEngine's features can be used by itself or in conjunction with each other to build powerful shipping functionality into your application or service. ## Getting Started If you're new to REST APIs then be sure to read our [introduction to REST](https://www.shipengine.com/docs/rest/) to understand the basics. Learn how to [authenticate yourself to ShipEngine](https://www.shipengine.com/docs/auth/), and then use our [sandbox environment](https://www.shipengine.com/docs/sandbox/) to kick the tires and get familiar with our API. If you run into any problems, then be sure to check the [error handling guide](https://www.shipengine.com/docs/errors/) for tips. Here are some step-by-step **tutorials** to get you started: - [Learn how to create your first shipping label](https://www.shipengine.com/docs/labels/create-a-label/) - [Calculate shipping costs and compare rates across carriers](https://www.shipengine.com/docs/rates/) - [Track packages on-demand or in real time](https://www.shipengine.com/docs/tracking/) - [Validate mailing addresses anywhere on Earth](https://www.shipengine.com/docs/addresses/validation/) ## Shipping Labels for Every Major Carrier ShipEngine makes it easy to [create shipping labels for any carrier](https://www.shipengine.com/docs/labels/create-a-label/) and [download them](https://www.shipengine.com/docs/labels/downloading/) in a [variety of file formats](https://www.shipengine.com/docs/labels/formats/). You can even customize labels with your own [messages](https://www.shipengine.com/docs/labels/messages/) and [images](https://www.shipengine.com/docs/labels/branding/). ## Real-Time Package Tracking With ShipEngine you can [get the current status of a package](https://www.shipengine.com/docs/tracking/) or [subscribe to real-time tracking updates](https://www.shipengine.com/docs/tracking/webhooks/) via webhooks. You can also create [custimized tracking pages](https://www.shipengine.com/docs/tracking/branded-tracking-page/) with your own branding so your customers will always know where their package is. ## Compare Shipping Costs Across Carriers Make sure you ship as cost-effectively as possible by [comparing rates across carriers](https://www.shipengine.com/docs/rates/get-shipment-rates/) using the ShipEngine Rates API. Or if you don't know the full shipment details yet, then you can [get rate estimates](https://www.shipengine.com/docs/rates/estimate/) with limited address info. ## Worldwide Address Validation ShipEngine supports [address validation](https://www.shipengine.com/docs/addresses/validation/) for virtually [every country on Earth](https://www.shipengine.com/docs/addresses/validation/countries/), including the United States, Canada, Great Britain, Australia, Germany, France, Norway, Spain, Sweden, Israel, Italy, and over 160 others.
ShipEngine's easy-to-use REST API lets you manage all of your shipping needs without worrying about the complexities of different carrier APIs and protocols. We handle all the heavy lifting so you can focus on providing a first-class shipping experience for your customers at the best possible prices. Each of ShipEngine's features can be used by itself or in conjunction with each other to build powerful shipping functionality into your application or service. ## Getting Started If you're new to REST APIs then be sure to read our [introduction to REST](https://www.shipengine.com/docs/rest/) to understand the basics. Learn how to [authenticate yourself to ShipEngine](https://www.shipengine.com/docs/auth/), and then use our [sandbox environment](https://www.shipengine.com/docs/sandbox/) to kick the tires and get familiar with our API. If you run into any problems, then be sure to check the [error handling guide](https://www.shipengine.com/docs/errors/) for tips. Here are some step-by-step **tutorials** to get you started: - [Learn how to create your first shipping label](https://www.shipengine.com/docs/labels/create-a-label/) - [Calculate shipping costs and compare rates across carriers](https://www.shipengine.com/docs/rates/) - [Track packages on-demand or in real time](https://www.shipengine.com/docs/tracking/) - [Validate mailing addresses anywhere on Earth](https://www.shipengine.com/docs/addresses/validation/) ## Shipping Labels for Every Major Carrier ShipEngine makes it easy to [create shipping labels for any carrier](https://www.shipengine.com/docs/labels/create-a-label/) and [download them](https://www.shipengine.com/docs/labels/downloading/) in a [variety of file formats](https://www.shipengine.com/docs/labels/formats/). You can even customize labels with your own [messages](https://www.shipengine.com/docs/labels/messages/) and [images](https://www.shipengine.com/docs/labels/branding/). ## Real-Time Package Tracking With ShipEngine you can [get the current status of a package](https://www.shipengine.com/docs/tracking/) or [subscribe to real-time tracking updates](https://www.shipengine.com/docs/tracking/webhooks/) via webhooks. You can also create [custimized tracking pages](https://www.shipengine.com/docs/tracking/branded-tracking-page/) with your own branding so your customers will always know where their package is. ## Compare Shipping Costs Across Carriers Make sure you ship as cost-effectively as possible by [comparing rates across carriers](https://www.shipengine.com/docs/rates/get-shipment-rates/) using the ShipEngine Rates API. Or if you don't know the full shipment details yet, then you can [get rate estimates](https://www.shipengine.com/docs/rates/estimate/) with limited address info. ## Worldwide Address Validation ShipEngine supports [address validation](https://www.shipengine.com/docs/addresses/validation/) for virtually [every country on Earth](https://www.shipengine.com/docs/addresses/validation/countries/), including the United States, Canada, Great Britain, Australia, Germany, France, Norway, Spain, Sweden, Israel, Italy, and over 160 others.
google-api-customization is a Ruby wrapper around the Google Places API that makes it easy to integrate place discovery into Rails applications. Features: - Place detail lookup by place ID (name, address, phone, rating, hours, photos, reviews) - Text search for places by query string with optional location bias - Nearby search by coordinates and radius - Autocomplete for place names and addresses - Photo URL resolution - Full error handling (OverQueryLimitError, RequestDeniedError, NotFoundError, etc.) - Configurable retry logic with delay Built on HTTParty for HTTP and supports the legacy Google Places API endpoints.
This is an improved input function for Ruby. Using this gem, you can easily get user input with real-time type validation, ensuring that the data provided is as expected, among other things. If you have any questions, check the documentation: https://github.com/barrosflavio/ruby_better_input Features: - Ask, receive and check input in a single line - Support for types such as integer, float and boolean. - Input validation with clear error messages. - Intuitive and easy-to-use function.
# COM # COM is an object-oriented wrapper around WIN32OLE. COM makes it easy to add behavior to WIN32OLE objects, making them easier to work with from Ruby. ## Usage ## Using COM is rather straightforward. There’s basically four concepts to keep track of: 1. COM objects 2. Instantiable COM objects 3. COM events 4. COM errors Let’s look at each concept separately, using the following example as a base. module Word end class Word::Application < COM::Instantiable def without_interaction with_properties('displayalerts' => Word::WdAlertsNone){ yield } end def documents Word::Documents.new(com.documents) end def quit(saving = Word::WdDoNotSaveChanges, *args) com.quit saving, *args end end ### COM Objects ### A COM::Object is a wrapper around a COM object. It provides error specialization, which is discussed later and a few utility methods. You typically use it to wrap COM objects that are returned by COM methods. If we take the example given in the introduction, Word::Documents is a good candidate: class Word::Documents < COM::Object DefaultOpenOptions = { 'confirmconversions' => false, 'readonly' => true, 'addtorecentfiles' => false, 'visible' => false }.freeze def open(path, options = {}) options = DefaultOpenOptions.merge(options) options['filename'] = Pathname(path).to_com Word::Document.new(com.open(options)) end end Here we override the #open method to be a bit easier to use, providing sane defaults for COM interaction. Worth noting is the use of the #com method to access the actual COM object to invoke the #open method on it. Also note that Word::Document is also a COM::Object. COM::Object provides a convenience method called #with_properties, which is used in the #without_interaction method above. It lets you set properties on the COM::Object during the duration of a block, restoring them after it exits (successfully or with an error). ### Instantiable COM Objects ### Instantiable COM objects are COM objects that we can connect to and that can be created. The Word::Application object can, for example, be created. Instantiable COM objects should inherit from COM::Instantiable. Instantiable COM objects can be told what program ID to use, whether or not to allow connecting to an already running object, and to load its associated constants upon creation. The program ID is used to determine what instantiable COM object to connect to. By default the name of the COM::Instantiable class’ name is used, taking the last two double-colon-separated components and joining them with a dot. For Word::Application, the program ID is “Word.Application”. The program ID can be set by using the .program_id method: class IDontCare::ForConventions < COM::Instantiable program_id 'Word.Application' end The program ID can be accessed with the same method: Word::Application.program_id # ⇒ 'Word.Application' Connecting to an already running COM object is not done by default, but is sometimes desirable: the COM object might take a long time to create, or some common state needs to be accessed. If the default for a certain instantiable COM object should be to connect, this can be done using the .connect method: class Word::Application < COM::Instantiable connect end If no running COM object is available, then a new COM object will be created in its stead. Whether or not a class uses the connection method can be queried with the .connect? method: Word::Application.connect? # ⇒ true Whether or not to load constants associated with an instantiable COM object is set with the .constants method: class Word::Application < COM::Instantiable constants true end and can similarly be checked: Word::Application.constants? # ⇒ true Constants are loaded by default. When an instance of the instantiable COM object is created, a check is run to see if constants should be loaded and whether or not they already have been loaded. If they should be loaded and they haven’t already been loaded, they’re, you guessed it, loaded. The constants are added to the module containing the COM::Instantiable. Thus, for Word::Application, the Word module will contain all the constants. Whether or not the constants have already been loaded can be checked with .constants_loaded?: Word::Application.constants_loaded # ⇒ false That concludes the class-level methods. Let’s begin with the #connected? method among the instance-level methods. This method queries whether or not this instance connected to an already running COM object: Word::Application.new.connected? # ⇒ false This can be very important in determining how shutdown of a COM object should be done. If you connected to an already COM object it might be foolish to shut it down if someone else is using it. The #initialize method takes a couple of options: * connect: whether or not to connect to a running instance * constants: whether or not to load constants These options will, when given, override the class-level defaults. ### Events ### COM events are easily dealt with: class Word::Application < COM::Instantiable def initialize(options = {}) super @events = COM::Events.new(com, 'ApplicationEvents', 'OnQuit') end def quit(saving = Word::WdDoNotSaveChanges, *args) @events.observe('OnQuit', proc{ com.quit saving, *args }) do yield if block_given? end end end To tell you the truth this API sucks and will most likely be rewritten. The reason that it is the way it is is that WIN32OLE, which COM wraps, sucks. It’s event API is horrid and the implementation is buggy. It will keep every registered event block in memory for ever, freeing neither the blocks nor the COM objects that yield the events. ### Errors ### All errors generated by COM methods descend from COM::Error, except for those cases where a Ruby error already exists. The following HRESULT error codes are turned into Ruby errors: HRESULT Error Code | Error Class -------------------|------------ 0x80004001 | NotImplementedError 0x80020005 | TypeError 0x80020006 | NoMethodError 0x8002000e | ArgumentError 0x800401e4 | ArgumentError There are also a couple of other HRESULT error codes that are turned into more specific errors than COM::Error: HRESULT Error Code | Error Class -------------------|------------ 0x80020003 | MemberNotFoundError 0x800401e3 | OperationUnavailableError Finally, when a method results in any other error, a COM::MethodInvocationError will be raised, which can be queried for the specifics, specifically #message, #method, #server, #code, #hresult_code, and #hresult_message. ### Pathname ### The Pathname object receives an additional method, #to_com. This method is useful for when you want to pass a Pathname object to a COM method. Simply call #to_com to turn it into a String of the right encoding for COM: Word::Application.new.documents.open(Pathname('a.docx').to_com) # ⇒ Word::Document ## Installation ## Install COM with % gem install com ## License ## You may use, copy and redistribute this library under the same [terms][1] as Ruby itself. [1]: http://www.ruby-lang.org/en/LICENSE.txt ## Contributors ## * Nikolai Weibull
Celerbrake provides robust, cloud-based exception tracking for any Ruby application. It lets you review errors, tie an error to an individual piece of code, and trace the cause back to recent changes. The Celerbrake dashboard provides easy categorization, searching, and prioritization of exceptions so that when errors occur, your team can quickly determine the root cause. This gem includes integrations with popular libraries and frameworks such as Rails, Sinatra, Resque, Sidekiq, Delayed Job, Shoryuken, ActiveJob and many more. It builds on the celerbrake-ruby notifier. Celerbrake began as a fork of the airbrake gem (https://github.com/airbrake/airbrake). It remains wire-compatible with the Airbrake v3 notice API, but reports to a Celerbrake instance you control rather than a third-party service.
== Confidently Build Terminal Apps Rooibos[https://rooibos.run] helps you build interactive terminal applications. Keep your code understandable and testable as it scales. Rooibos handles keyboard, mouse, and async work so you can focus on behavior and user experience. gem install rooibos <i>Currently in beta. APIs may change before 1.0.</i> === Get Started in Seconds rooibos new my_app cd my_app rooibos run That's it. You have a working app with keyboard navigation, mouse support, and clickable buttons. Open <tt>lib/my_app.rb</tt> to make it your own. --- === The Pattern \Rooibos uses Model-View-Update, the architecture behind Elm[https://guide.elm-lang.org/architecture/], Redux[https://redux.js.org/], and {Bubble Tea}[https://github.com/charmbracelet/bubbletea]. State lives in one place. Updates flow in one direction. The runtime handles rendering and runs background work for you. --- === Hello, MVU The simplest \Rooibos app. Press any key to increment the counter. Press <tt>Ctrl</tt>+<tt>C</tt> to quit. require "rooibos" module Counter # Init: How do you create the initial model? Init = -> { 0 } # View: What does the user see? View = -> (model, tui) { tui.paragraph(text: <<~END) } Current count: #{model}. Press any key to increment. Press Ctrl+C to quit. END # Update: What happens when things change? Update = -> (message, model) { if message.ctrl_c? Rooibos::Command.exit elsif message.key? model + 1 end } end Rooibos.run(Counter) That's the whole pattern: Model holds state, Init creates it, View renders it, and Update changes it. The runtime handles everything else. --- === Your First Real Application A file browser in sixty lines. It opens files, navigates directories, handles errors, styles directories and hidden files differently, and supports vim-style keyboard shortcuts. If you can do this much with this little code, imagine how easy _your_ app will be to build. require "rooibos" module FileBrowser # Model: What state does your app need? Model = Data.define(:path, :entries, :selected, :error) Init = -> { path = Dir.pwd entries = Entries[path] Ractor.make_shareable( # Ensures thread safety Model.new(path:, entries:, selected: entries.first, error: nil)) } View = -> (model, tui) { tui.block( titles: [model.error || model.path, { content: KEYS, position: :bottom, alignment: :right}], borders: [:all], border_style: if model.error then tui.style(fg: :red) else nil end, children: [tui.list(items: model.entries.map(&ListItem[model, tui]), selected_index: model.entries.index(model.selected), highlight_symbol: "", highlight_style: tui.style(modifiers: [:reversed]))] ) } Update = -> (message, model) { return model.with(error: ERROR) if message.error? model = model.with(error: nil) if model.error && message.key? if message.ctrl_c? || message.q? then Rooibos::Command.exit elsif message.home? || message.g? then model.with(selected: model.entries.first) elsif message.end? || message.G? then model.with(selected: model.entries.last) elsif message.up_arrow? || message.k? then Select[:-, model] elsif message.down_arrow? || message.j? then Select[:+, model] elsif message.enter? then Open[model] elsif message.escape? then Navigate[File.dirname(model.path), model] end } private # Lines below this are implementation details KEYS = "↑/↓/Home/End: Select | Enter: Open | Esc: Navigate Up | q: Quit" ERROR = "Sorry, opening the selected file failed." ListItem = -> (model, tui) { -> (name) { modifiers = name.start_with?(".") ? [:dim] : [] fg = :blue if name.end_with?("/") tui.list_item(content: name, style: tui.style(fg:, modifiers:)) } } Select = -> (operator, model) { new_index = model.entries.index(model.selected).public_send(operator, 1) model.with(selected: model.entries[new_index.clamp(0, model.entries.length - 1)]) } Open = -> (model) { full = File.join(model.path, model.selected.delete_suffix("/")) model.selected.end_with?("/") ? Navigate[full, model] : Rooibos::Command.open(full) } Navigate = -> (path, model) { entries = Entries[path] model.with(path:, entries:, selected: entries.first, error: nil) } Entries = -> (path) { Dir.children(path).map { |name| File.directory?(File.join(path, name)) ? "#{name}/" : name }.sort_by { |name| [name.end_with?("/") ? 0 : 1, name.downcase] } } end Rooibos.run(FileBrowser) --- === Batteries Included ==== Commands Applications fetch data, run shell commands, and set timers. \Rooibos Commands run off the main thread and send results back as messages. <b>HTTP requests:</b> Update = -> (message, model) { case message in :fetch_users [model.with(loading: true), Rooibos::Command.http(:get, "/api/users", :got_users)] in { type: :http, envelope: :got_users, status: 200, body: } model.with(loading: false, users: JSON.parse(body)) in { type: :http, envelope: :got_users, status: } model.with(error: "HTTP #{status}") end } <b>Shell commands:</b> Update = -> (message, model) { case message in :list_files Rooibos::Command.system("ls -la", :listed_files) in { type: :system, envelope: :listed_files, stdout:, status: 0 } model.with(files: stdout.lines.map(&:chomp)) in { type: :system, envelope: :listed_files, stderr:, status: } model.with(error: stderr) end } <b>Timers:</b> Update = -> (message, model) { case message in { type: :timer, envelope: :tick, elapsed: } [model.with(frame: model.frame + 1), Rooibos::Command.wait(1.0 / 24, :tick)] end } <b>And more!</b> \Rooibos includes <tt>all</tt>, <tt>batch</tt>, <tt>bubble</tt>, <tt>cancel</tt>, <tt>custom</tt>, <tt>deliver</tt>, <tt>exit</tt>, <tt>http</tt>, <tt>map</tt>, <tt>open</tt>, <tt>system</tt>, <tt>tick</tt>, and <tt>wait</tt> commands. You can also define your own custom commands for complex orchestration. Every command produces a message, and Update handles it the same way. ==== Testing \Rooibos makes TUIs so easy to test, you'll save more time by writing tests than by not testing. <b>Unit test Update, View, and Init.</b> No terminal needed. Test helpers included. def test_moves_selection_down_with_j model = Ractor.make_shareable(FileBrowser::Model.new( path: "/", entries: %w[bin exe lib], selected: "bin", error: nil)) message = RatatuiRuby::Event::Key.new(code: "j") result = FileBrowser::Update.call(message, model) assert_equal "exe", result.selected end <b>Style assertions.</b> Draw to a headless terminal, verify colors and modifiers. def test_directories_are_blue with_test_terminal(60, 10) do model = Ractor.make_shareable(FileBrowser::Model.new( path: "/", entries: %w[file.txt subdir/], selected: "file.txt", error: nil)) widget = FileBrowser::View.call(model, RatatuiRuby::TUI.new) RatatuiRuby.draw { |frame| frame.render_widget(widget, frame.area) } assert_blue(1, 2) # "subdir/" at column 1, row 2 end end <b>System tests.</b> Inject events, run the full app, snapshot the result. def test_selection_moves_down with_test_terminal(120, 30) do Dir.mktmpdir do |dir| FileUtils.touch(File.join(dir, "a")) FileUtils.touch(File.join(dir, "b")) FileUtils.touch(File.join(dir, "c")) inject_key(:down) inject_key(:ctrl_c) # Tests use explicit params to inject deterministic initial state. Rooibos.run( model: Ractor.make_shareable(FileBrowser::Model.new( path: dir, entries: %w[a b c], selected: "a", error: nil)), view: FileBrowser::View, update: FileBrowser::Update ) assert_snapshots("selection_moved_down") do |lines| title = "┌/tmp/test#{'─' * 107}┐" lines.map do |l| l.gsub(/┌#{Regexp.escape(dir)}[^┐]*┐/, title) end end end end end Snapshots record both plain text and ANSI colors. Normalization blocks mask dynamic content (timestamps, temp paths) for cross-platform reproducibility. Run <tt>UPDATE_SNAPSHOTS=1 rake test</tt> to regenerate baselines. ==== Scale Up Large applications decompose into fragments. Each fragment has its own Model, View, Update, and Init. Parents compose children. The pattern scales. The Router DSL eliminates boilerplate: module Dashboard include Rooibos::Router route :stats, to: StatsPanel route :network, to: NetworkPanel receive_events :ctrl_c, -> { Rooibos::Command.exit } only when: -> (_message, model) { !model.modal_open } do receive_events :q, -> { Rooibos::Command.exit } forward_events :s, to: :stats, as: :fetch forward_events :p, to: :network, as: :ping end Update = from_router # ... Model, Init, View below end Declare routes and event handlers. The router generates Update for you. Use guards to ignore messages when needed. ==== CLI The <tt>rooibos</tt> command scaffolds projects and runs applications. rooibos new my_app # Generate project structure rooibos run # Run the app in current directory Generated apps include tests, type signatures, and a working welcome screen with keyboard and mouse support. --- === The Ecosystem \Rooibos builds on RatatuiRuby[https://www.ratatui-ruby.dev], a Rubygem built on Ratatui[https://ratatui.rs]. You get native performance with the joy of Ruby. \Rooibos is one way to manage state and composition. Kit is another. ==== Rooibos[https://www.rooibos.run] Model-View-Update architecture. Inspired by Elm, Bubble Tea, and React + Redux. Your UI is a pure function of state. - Functional programming with MVU - Commands work off the main thread - Messages, not callbacks, drive updates ==== {Kit}[https://sr.ht/~kerrick/ratatui_ruby/#chapter-3-the-object-path--kit] (Coming Soon) Component-based architecture. Encapsulate state, input handling, and rendering in reusable pieces. - OOP with stateful components - Separate UI state from domain logic - Built-in focus management & click handling Both use the same widget library and rendering engine. Pick the paradigm that fits your brain. --- === Links [Get Started] {Getting Started}[https://www.rooibos.run/docs/trunk/doc/getting_started/index_md.html], {Tutorial}[https://www.rooibos.run/docs/trunk/doc/tutorial/index_md.html], {Examples}[https://www.rooibos.run/docs/trunk/examples/app_fractal_dashboard/README_md.html] [Coming From...] {React/Redux}[https://www.rooibos.run/docs/trunk/doc/getting_started/for_react_developers_md.html], {BubbleTea}[https://www.rooibos.run/docs/trunk/doc/getting_started/for_go_developers_md.html], {Textual}[https://www.rooibos.run/docs/trunk/doc/getting_started/for_python_developers_md.html] [Learn More] {Essentials}[https://www.rooibos.run/docs/trunk/doc/essentials/index_md.html], {Scaling Up}[https://www.rooibos.run/docs/trunk/doc/scaling_up/index_md.html], {Best Practices}[https://www.rooibos.run/docs/trunk/doc/best_practices/index_md.html], {Troubleshooting}[https://www.rooibos.run/docs/trunk/doc/troubleshooting/index_md.html] [Community] {Forum}[https://forum.setdef.com/c/rooibos], {Announcements}[https://forum.setdef.com/tags/c/rooibos/announcement], {Bug Tracker}[https://forum.setdef.com/tags/c/rooibos/bug], {Contribution Guide}[https://github.com/setdef/Rooibos/blob/trunk/CONTRIBUTING.md], {Code of Conduct}[https://github.com/setdef/Rooibos/blob/trunk/CODE_OF_CONDUCT.md] --- [Website] https://rooibos.run [Source] https://github.com/setdef/Rooibos [RubyGems] https://rubygems.org/gems/rooibos © 2026 Kerrick Long · Library: LGPL-3.0-or-later · Website: CC-BY-NC-ND-4.0 · Snippets: MIT-0
E11y (Easy Telemetry) - Observability for Rails developers who hate noise. UNIQUE FEATURES: • Request-scoped debug buffering - buffers debug logs in memory, flushes ONLY on errors • Zero-config SLO tracking - automatic Service Level Objectives for HTTP endpoints and jobs • Schema-validated events - catch bugs before production with dry-schema DEVELOPER EXPERIENCE: • Minimal setup — one config block, works with stdout out of the box • Auto-metrics from events (no manual Yabeda.increment) • Rails-first design (follows Rails conventions) • Pluggable adapters (Loki, Sentry, OpenTelemetry, custom backends) COST SAVINGS: • Reduce log storage costs by 90% (request-scoped buffering) • Replace expensive APM SaaS ($500-5k/month → infra costs only) • Own your observability data (no vendor lock-in) PRODUCTION-READY: • Thread-safe for multi-threaded Rails + Sidekiq • Adaptive sampling (error-based, load-based, value-based) • PII filtering (GDPR-compliant masking/hashing) • Performance optimized (hash-based events, minimal allocations) Perfect for Rails 7.0+ teams who need observability without complexity or high costs.
Contentful API wrapper library exposing an ActiveRecord-like interface
No description provided.
No description provided.
No description provided.
No description provided.