{"id":15407433,"url":"https://github.com/wojtha/dry-roda-playground","last_synced_at":"2025-03-28T02:27:34.512Z","repository":{"id":18024769,"uuid":"69877542","full_name":"wojtha/dry-roda-playground","owner":"wojtha","description":"Playground for web stack based on Roda and dry-rb","archived":false,"fork":false,"pushed_at":"2023-03-16T05:39:39.000Z","size":11,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-02T03:44:14.151Z","etag":null,"topics":["roda","ruby","web"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wojtha.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"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":"2016-10-03T14:18:08.000Z","updated_at":"2024-10-09T16:58:25.000Z","dependencies_parsed_at":"2024-10-19T12:49:58.235Z","dependency_job_id":null,"html_url":"https://github.com/wojtha/dry-roda-playground","commit_stats":{"total_commits":5,"total_committers":2,"mean_commits":2.5,"dds":0.4,"last_synced_commit":"d1391a17080bc784951a467911289497e9513049"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wojtha%2Fdry-roda-playground","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wojtha%2Fdry-roda-playground/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wojtha%2Fdry-roda-playground/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wojtha%2Fdry-roda-playground/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wojtha","download_url":"https://codeload.github.com/wojtha/dry-roda-playground/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245956747,"owners_count":20700152,"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":["roda","ruby","web"],"created_at":"2024-10-01T16:28:46.370Z","updated_at":"2025-03-28T02:27:34.485Z","avatar_url":"https://github.com/wojtha.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Dry-System \u0026 Roda Stack\n\nThis is demo of web stack based on [dry-rb](http://dry-rb.org) and [roda](http://roda.jeremyevans.net/index.html) routing tree web toolkit. It is extraction and simplification of [dry-web](https://github.com/dry-rb/dry-web) stack and [Berg](https://github.com/icelab/berg) (the company website of the one of the main dry-rb contributors) which is build with dry-rb, rom-rb and Roda.\n\nI was building this from scratch or better say by copying bits and pieces from there and there to learn how these pieces of stack are wired together, but you can actually use prepared [dry-web-roda](https://github.com/dry-rb/dry-web-roda) stack which is also able to generate skeleton app.\n\n### How to run this\n\nBefore I'll show the stack, here is just little snippet to make it work if you want just to see it action and dove into the internals by yourself:\n\n```\ngit clone git@github.com:wojtha/dry-roda-playground.git\ncd dry-roda-playground\nbundle install\nshotgun\n```\n\n### Web layer\n\nWeb layer is responsible for Roda can be switched to whatever routing system. I've picked it for several reasons: \n\n1. Routing is **much faster** than in Sinatra.\n2. Has **nice plugin system** with [dozens of built-in plugins](http://roda.jeremyevans.net/documentation.html), so the core is very lean and it allows you pick just the stuff you need, so there is no additional and unwanted processing in the middleware layer during request or response.\n3. The **routing tree concept** seemed to me completely weird, so I want to try it out 🤓.\n\n### Application or bussiness logic layer\n\nApplication layer includes entities, validation, business rules and processes. I've choose following dry-rb libraries:\n\n1. [dry-system](http://dry-rb.org/gems/dry-system) is actually combination of [dry-container](http://dry-rb.org/gems/dry-container) and [dry-auto_inject](http://dry-rb.org/gems/dry-auto_inject). This allows you to create **IoC containers with autoloading (!) 🍻** and easily inject all dependecies defined in the container anywhere in the model or service layer.  \n2. [dry-types](http://dry-rb.org/gems/dry-types/) and [dry-struct](http://dry-rb.org/gems/dry-struct/) are used to load the app configuration, but it can be used later anywhere in the app when some type checks and coercion is needed, this two-gems-combo is basically modern replacement of the famous [Virtus](https://github.com/solnic/virtus) gem (and mostly from the same author actually).\n3. [dry-configurable](http://dry-rb.org/gems/dry-configurable/) is used internally by `dry-system` but we can use it anywhere in the stack to make any class configurable.\n\n### Persistence layer\n\n[Sequel](http://sequel.jeremyevans.net/) is used just in configuration to show how to bootstrap the DB or ORM properly but it is actually not used anywhere.\n\n### Bundler require\n\nUnlike inner app dependencies `Bundler.require` is now intentionally omitted so you have to require all external dependencies manually.\n\n## Interesting bits\n\nHere, there are some interesting pieces of code which demostrates how all the stack is wired together.\n\n1. [Booting container and app](#booting)\n2. [Injecting autoloaded dependencies](#injecting)\n3. [Using container inside Roda app](#container)\n\n###Booting container and app\u003ca name=\"booting\"\u003e\u003c/a\u003e\n\n**config.ru** - standard Rackup config file\n\n```ruby\nrequire_relative 'system/boot'\nrun Main::Application.freeze.app\n```\n\n**system/boot.rb** - main boot loader, first it initializes the container and then it loads the app;\n\n```ruby\nrequire_relative 'container'\nMain::Container.finalize!\nrequire_relative 'application'\n```\n\n**system/main/container.rb** - main container of the application; here we can register various settings and services; main feature in this example is `auto_register`, which registeres all the objects in given folders automatically, but objects has to follow the naming convention;\n\n```ruby\nrequire 'dry/system/container'\n\nmodule Main\n  class Container \u003c Dry::System::Container\n    setting :env, ENV.fetch('RACK_ENV', 'development').to_sym\n\n    configure do |config|\n      config.auto_register = %w(lib)\n    end\n\n    load_paths!('lib')\n  end\nend\n```\n\n**system/main/import.rb** - here we define injector mixing for given container, see `Operations::Foo` below for example usage; it uses `dry-auto_inject` gem under hood;\n\n```ruby\nrequire_relative 'container'\n\nmodule Main\n  Import = Container.injector\nend\n```\n\n### Injecting autoloaded dependencies\u003ca name=\"injecting\"\u003e\u003c/a\u003e\n\n**lib/operations/foo.rb** - operation with automatically injected dependencies; injector defines constructor and sets the dependencies as instance variables via the the constructor\n\n```ruby\nrequire 'main/import'\n\nmodule Operations\n  class Foo\n    include Main::Import['services.bar', 'services.baz']\n\n    def call(params)\n      \"Foo #{bar.call} #{baz.call}: #{params}\"\n    end\n  end\nend\n```\n\n**lib/services/bar.rb** and **lib/services/baz.rb**\n\n```ruby\nmodule Services\n  class Bar # or Baz\n    def call\n      \"Bar\" # or Baz\n    end\n  end\nend\n```\n\n### Using container inside Roda app\u003ca name=\"container\"\u003e\u003c/a\u003e\n\n**lib/configurable_roda.rb** - mixing the `roda` and `dry-configurable` which allows us to set `Dry::System::Container` as class level property; so we can access the container from inside the `roda` app later;\n\n```ruby\nrequire 'roda'\nrequire 'dry-configurable'\n\nclass ConfigurableRoda\n  extend Dry::Configurable\n\n  setting :container\n\n  def self.resolve(name)\n    config.container[name]\n  end\n\n  def self.[](name)\n    resolve(name)\n  end\nend\n```\n\n**system/main/application.rb** - finally the actual Roda application!\n\n```ruby\nrequire_relative 'container'\n\nmodule Main\n  class Application \u003c ConfigurableRoda\n    configure do |config|\n      config.container = Container # same as ::Main::Container\n    end\n    \n    # With those two plugins, Hash and Array output is automatically encoded as JSON\n    plugin :default_headers, 'Content-Type' =\u003e 'application/json'\n    plugin :json\n\n    route do |r|\n      r.root do\n        # From Roda you can return content like from standard ruby method.\n        { message: 'ok!' }\n      end\n\n      r.get 'foo' do\n        # Call the 'operations.foo' object which is autoloaded and registered\n        # in the container. Temp variables are used for more clarity.\n        operation = self.class['operations.foo']\n        result = operation.call(request.params) \n        { foo: result }\n      end\n    end\n  end\nend\n```\n\n## Links\n\n* [Introduction to Roda](https://twin.github.io/introduction-to-roda/) by Janko Marohnić, 19 Aug 2015\n* [Put HTTP in its place with Roda](https://www.icelab.com.au/notes/put-http-in-its-place-with-roda/) (and the whole series below the article) by Tim Riley, 24 May 2016\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwojtha%2Fdry-roda-playground","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwojtha%2Fdry-roda-playground","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwojtha%2Fdry-roda-playground/lists"}