What is hanami? Where is it going?


#1

After our validation-related discussion I decided to post a bunch of questions I’ve been asking myself for a long time hoping to clarify some things that I’ve found concerning (from my pov at least).

Here we go:

  • Is hanami becoming another rails? What is it exactly that hanami has to offer that will be a game changer? Is it supposed to be a slightly better version of rails?
  • What is the philosophy of this project? Is it becoming a part of the new ruby ecosystem that I thought we’re building or is it a thing of its own, a closed microecosystem that wants to be in control and impose weird constraints for other libs to make them work with hanami?
  • Why does it even need a validation library? Will using any validation library going to be a problem?
  • Why does it have a model/orm lib? Will using any orm going to be a problem?
  • Why does it have hanami-utils, which is essentially ActiveSupport without monkeypatches, are there maybe any missing abstractions that you’re trying to workaround by having “util apis”?
  • Why do you think your opinionated stack will be so much better than rails?
  • Will using hanami feel like it’s “on top of the tool chain”? Will it control the developer or will the developer control it? Will it play nice with others or not? Is that even a concern for the hanami team?

I understand these are hard questions, I’m happy to clarify if something seems confusing. I would love to hear your thoughts.


#2

Hi @solnic, before to reply to all your doubts, I want to point out that you’re questioning if the project worths to exist and at the same time you’re pretending to build a new Ruby ecosystem with us.

You suggest to ditch hanami-validations and hanami-model in favor of dry-validation and ROM. Well, we can do that, but what’s the where Hanami can be useful for this new ecosystem? What we can build together? In the past months I’ve seen you to adopt roda, to build dry-view and than dry-web, which are overlapping hanami-router, hanami-view and hanami gems. Are we still building something together? Or is that collaboration one way?


Here’s my answers:

Is hanami becoming another rails?

What do you mean exactly?

What is it exactly that hanami has to offer that will be a game changer? Is it supposed to be a slightly better version of rails?

I always thought to Hanami as a sane alternative to Rails in terms of light, speed, maintenance and testability. We want to offer the same amount of Rails features. I’m talking about all the facilities that developers find really convenient: think of code generators, migrations, assets.

At the same time, the interfaces aren’t bloated, and a Hanami project is way lighter and faster than one built with Rails.

The architecture is goes beyond flat MVC (“fat models, skinny controllers”), which proved to be painful for maintenance. What we offer is full MVC, where there are more collaborators to assign responsibilities: real view objects, presenters, interactors, etc…

Still on maintenance. I’ve been burnt by Rails major release upgrades: form 1.2 to 2.0, 2.3 to 3 etc… and we want to avoid developers to experience this problem with Hanami. We value the effort (time and money) of companies and individuals that are investing in Hanami projects. I’m so strict with dependencies and interfaces, because this is the way to keep Hanami stable over the time.

Less coupling. We avoided Rails mistakes of components coupling: each layer knows about ActiveRecord. With Hanami you can’t do <%= link_to @book, 'Title' %> and expect it to work, because the routing is completely decoupled from the ORM. Even better, hanami-model isn’t even a dependency, you can drop it from Gemfile and use which data source you want.

Testability. Unit testing is simplified: you have less setup, you can inject dependencies and avoid ugly hacks like any_instance. Everything is an object. If you need to test it, just send a message and verify the output. Hanami is the first web framework that allows to instantiate actions. How testing can be simpler than this? response = Action.new.call({id: 23})

Embrace Ruby (Ecosystem). We grew Hanami without diverging from Ruby: no monkey-patching, nor constant autoloading, the list is longer… We don’t mess with the language. Every time you’re in doubt, you don’t need to ask how would you do something with Hanami, but better to ask how would you do with Ruby.

Since the beginning we wanted Hanami to play well with external libraries. I already mentioned that you can use any data source (or ORM) you want. You can bring any Ruby library that you want (unless it targets Rails specifically). You can use any Rack middleware you want, authorization libraries like pundit, queues like sidekiq.

What is the philosophy of this project? Is it becoming a part of the new ruby ecosystem that I thought we’re building or is it a thing of its own, a closed microecosystem that wants to be in control and impose weird constraints for other libs to make them work with hanami?

I’ve probably already answered to this question with the last point above. In short: we don’t aim to become a closed walled garden.

If we communicated this impression, it’s a big misunderstanding. Our goal is to protect developers from the changes that low level engines may introduce. Think it as a gigantic Dependency Inversion Principle: we want our developers to depend on Hanami abstractions instead of concrete implementations. For rendering a template, developers can rely on stable abstractions of hanami-view's interface, even if its low level engine (tilt) will change.

Why does it even need a validation library?

For the same reason, even if we adopt dry-validation as backend, hanami-validations still makes sense to exist.

The reason why I started a validation library is simple: I didn’t had chance at the time. The first release (v0.1.0) is dated Oct 2014, at that time DRY wasn’t there yet. The only alternative was to use activemodel, but that wasn’t possible because of different visions with that Rails world.

Will using any validation library going to be a problem?

It depends. If you expect params validations to have a swappable backend the answer is no. For any other usage the answer is yes. For instance, you can use dry-validation like this:

module Web::Controllers::Books
  class Create
    include Hanami::Action

    def call(params)
      schema = Dry::Validation.Form do
        # ...
      end

      result = schema.call(params.to_h)
      # do something interesting
    end
  end
end

So it’s NOT gonna be a problem in general, because Hanami it’s just Ruby.

Why does it have a model/orm lib?

Same reasons of the validations: protect from changes and because at the time (Apr 2014), ROM wasn’t ready yet.
There is also another reason to keep hanami-model: migrations and database tasks (create, drop, prepare).

Will using any orm going to be a problem?

Again no. Here’s the how to do:

hanami new bookshelf
cd bookshelf
rm -rf lib
vim Gemfile # remove `hanami-model` in favor of anything else
bundle

Why does it have hanami-utils, which is essentially ActiveSupport without monkeypatches, are there maybe any missing abstractions that you’re trying to workaround by having “util apis”?

This is a wrong question. Sorry, but that means you haven’t understood hanami-utils at all.

Most of the times, It provides objects for internal Hanami usage: deprecation warnings, class attributes, callbacks. These utilities aren’t exposed to developers, we just needed a place (gem) to share common code across Hanami libraries.

You’re talking about “missing abstraction”, which is another way to ask if it suffers from Primitive Obsession. Short answer: no.

Long answer: There are a couple of cases (Utils::String and Utils::Hash) that may lead you to think about Primitive Obsession. But we don’t suggest developers to use them. We don’t even use them directly in the framework implementation.

Here’s a concrete example: application name. When you create a new application, Hanami translates the name you pass as input into a Ruby constant name string (eg. "cool_app" into "CoolApp"). To do that we use Hanami::ApplicationName, which is an abstraction that internally uses Utils::String.

hanami-utils has other abstractions like LoadPaths (instead of an array of pathnames, which are primitives), PathPrefix (instead of a Utils::String#to_path_prefix primitive).

Why do you think your opinionated stack will be so much better than rails?

Already answered at the beginning. Who share that values with us, will probably appreciate Hanami.

Will using hanami feel like it’s “on top of the tool chain”? Will it control the developer or will the developer control it? Will it play nice with others or not? Is that even a concern for the hanami team?

It’s a concern for us! For sure! We don’t want to be at the top of the chain. Have you ever tried to mount Hanami into Rails? I’m running it in production. Have you ever tried to do the opposite? You can’t.

I haven’t tried with other Rack based frameworks, but theoretically you can entirely remove the shell around a Hanami project and it still works as it’s just a Rack application.


I hope to have answered to all your questions. Feel free to ask more and don’t forget to reply to mine at the top. Thanks


#3

[quote=“jodosha, post:2, topic:222”]
You suggest to ditch hanami-validations and hanami-model in favor of dry-validation and ROM. [/quote]

Validations - yes, I don’t think additional abstractions are needed. Just reuse what exists already and matches your requirements.

Model - I honestly don’t know, this part is so complex that some integration code could be useful.

Router, view, assets, integration into a fullstack solution, etc.

Personally, I am interested in all data-related stuff, so (surprise surprise) validation, coercion, mapping and persistence, but I’m sure there’s much more that other people from both communities would like to collaborate on.

I know readmes don’t mention that but the goal of dry-web is a web stack with pluggable components based on dry-component. I already built a PoC called lotuskase a while ago which uses lotus-router and lotus-view. I don’t actually care that much whether people will use roda or hanami or dry-view or cells or whatever else will be created in the meantime. What interests me the most is dry-component-based architecture.

We are not and have not been building anything together in terms of collaborating on the same projects which I always hoped that will change one day. Hanami, rom-rb, dry-rb and trb communities are all working on something new and better, I simply hope we could turn it into a cross-community effort and identify what’s common and how we can help ourselves.

Thanks for your answers, I appreciate the time you took to answer them, and that you did that so quickly. I need to read them again and get back with more feedback.


#4

@jodosha I think that this is the main point on which dry-rb and Hanami philosophies differ, what you described in your example sounds like an inflection to me, and the community as a whole could benefit from the code you wrote to make that inflection - if it sat in a stand-alone library, however, because that code sits in Hanami utils with a lot of other unrelated behaviour, it would not make sense to pull all that into a project just to handle a few inflections. I’m sure there are other examples of this in Hanami utils, although I can’t claim to have read the code.

This is why I don’t see the problem with having a large number of gems bundled in a project, as long as those gems tackle a problem that I need to address in my domain. This is what dry-rb tries to do, for example, the first library introduced to the dry-rb organisation was dry-configurable, this came about because I was writing a plugin for Roda and wanted the plugin to be configurable, naturally I searched for a gem to mix-in configuration, but I was surprised to find that no such gem existed, I found a lot of libraries that implemeneted configuration behaviour, and each had their own implementation with similar, or exactly matching interfaces, which seemed absurd to me, but upon thinking about it more, this seems to be the rule with Ruby libraries rather than the exception.

dry-rb libraries try to handle the 95% use-case, they are intended to be flexible and extendible enough to be used stand-alone or as part of a toolkit. We never got around to expanding on our philiosophies on the website, as it seems to be something that is hard to verbalise in a succinct manner, but I will try to list a few of them here:

  • No monkey-patching
  • Minimal pollution to the global namespace (In an ideal world, the Dry module would be all that is introduced)
  • Thread-safe out of the box, and zero mutable runtime state
  • Small libraries that handle one concern and handle it well
  • Easily extendable (to fit specialisations and some of the other 5%)

The hope is that in following these priniciples, we can create an ecosystem with little to no overlap in time and effort spent implementing the same behaviour in different places, which should in turn solidify the libraries on both sides of the relationship.

With regards to the one-way collaboration, I don’t think something like a router can be encapsulated like this, the foundations of a router are based on the opionions you have about what a router should do, i.e. do you want a Rails style pattern matching router or a routing tree implementation like Roda. And as for views, I know @solnic had a pretty unique idea about how he wanted dry-web views to operate. I know these were just examples, I think the main problem is with the differing philosophies, i.e. if you had implemented hanami-inflector rather than bundling it into Hanami utils, I’m sure @solnic (and many others) would be happy to make use of it.


#5

@AMHOL Thanks for joining this conversation :slightly_smiling:

If you look at ApplicationName, it’s really specific on what it does. The risk here is to create let another leftpad case.


#6

As someone new to the Hanami community, I would like to offer a few thoughts. Some brief background – I am the developer of Noteshare.io, a four-year-old Rails project which eventually became hard to maintain and improve. On October 11, 2016, I made my first commit of a rewrite in Hanami (then Lotus). It is now running at Scripta.io and I am one week into a new project, Shoobox.io.

The clean architecture of Hanami has made the transition of Noteshare to Scripta both pleasant and productive. Scripta has long since surpassed Noteshare in performance and features: Scripta runs MUCH, MUCH faster, and changes to the code, fixes, and improvements are much more easily made. It is the well-thought out architecture that has made the difference. To me, this is Hanami’s strongest selling point. That clarity of structure must be preserved. That is why philosophy is important, as is being faithful to that philosophy.

Some specific points:

  • The model-repository structure of Hanami is a feature that was especially important during the transition. Please don’t abandon this! I used Sequel extensively and it was essential to the success of the project (Four months to rewrite and improve a four-year-old project)

  • Views as objects and interactors. It took me a while to understand these, but once I did, productivity, code health, and developer happiness went way up. Another example of where the right architecture make a huge difference. (Perhaps I should say “a right architecture”. There could be more than one, but the important thing is to have one that works – to stick with it and avoid feature bloat).

Turning to the future, I would identify the following as important principles:

  • Keep things simple and clean. Value stability.
  • Don’t add too much – offer essential features but make it easy for developers to swap in their own components if they want to. An example is the current model-repository structure. IMHO, it works and therefore shouldn’t be fixed. I looked at ROM at one point, but it was too complex for me and my needs. But for another person, it may be exactly the right thing.
  • Value speed and lightness. I think this is a huge point in favor of Hanami.
  • Don’t increase the learning curve. I’d say it is about right as is. But make it possible to go deeper-farther for those who need to or want to.

– Jim

PS. The documentation is very good. I could not have gotten started without it.
If you need a native Engish speaker to help do final edits, I can try to help out.


#7

In terms of philosophy. But you answered this down below saying that you don’t aim to build a closed ecosystem. This is very good news for me and it was my biggest fear.

Building anything, anything in Ruby, comes with the frustration that it must work with Rails, and 99% of the cases you’re gonna have trouble…“oh, it doesn’t work with spring”, “oh, this AS patch causes issues”, “oh you can’t use it with ActiveSomething because X Y Z” etc.

I guess that’s one of the things that defines a full-stack framework. I see it as something that helps in bootstrapping a project and simplifies the learning curve, although I have to say that in the long term things like code generators are quickly becoming an anti-pattern. Ie I stopped using rails generators as they always caused a lot of bloat in my codebases. This is probably much more valuable for less experienced devs, which is of course fine.

Great to know you’re focused on narrowing down the interfaces. It’s one of the worst things about Rails and its components with huge interfaces.

I’ve been confused about MVC in general, at some point people pointed out Rails is not MVC and it’s Model2.

Anyhow, hanami providing various facilities like view objects, presenters and interactors feels like an improvement, but I’d be careful with introducing framework-specific abstractions, as at the end of the day they won’t meet all the requirements for all the people out there (btw I hope interactors are no longer mutable, they were last time I checked ;)).

For me what’s most valuable are simple objects that are composable, hence we introduced dry-transaction that allows you to do that with simple callable objects.

I commented on that in the related validation PR, lemme just paste it here for the sake of keeping information in one place:

There’s one more thing I should mention - Luca’s concern is to avoid
upgrade-nightmares with hanami like we’ve all experienced with rails and
3rd party gems. There’s one thing that you might be missing - with
rails and rails-like gems there’s an insane amount of inheritance, most
functionality you mix into your objects or inherit from some base class or do both.
Gems typically don’t provide standalone re-usable objects, they give
you inheritance, mix in who-knows-what-kind-of-behaviors and eventually
your entire system relies on inherited behaviors from 3rd party gems
with very-little-up-to-zero encapsulation on your side. And THIS is the
reason why upgrading apps is so painful, hanami wrapping all kinds of
APIs and doing the additional work to “protect you” from breaking
changes is a questionable approach. Not only does it need heaps of
additional complexity, but also, at the end of the day, it still makes
you couple your code to the framework. The way I see it is this: provide
re-usable objects with trivial interfaces. Inheritance is evil
in majority of the cases. The fact that a DSL in some gem changes is
not a big problem! The actual problem is that when this gem extends your objects so that you end up with tight-coupling between your objects and 3rd-party APIs that provide unpredictable behaviors.

That’s a HUGE improvement over Rails.

This is another fantastic improvement over Rails and it also shows the difference between a framework with decoupled components vs monolith.

This is also very reassuring, although it seems a little bit against having abstractions like “interactors”. Maybe just let people come up with their own abstractions for business logic.

That’s cool but I’d like to mention that when a framework provides its own solution for problem X then alternative solutions solving problem X will become less known. See: ActiveRecord.

This is also related to my comment about coupling with 3rd parties, in many cases it’s better to rely on a simple interface rather than providing additional abstractions and making people couple their code bases with those.

That’s good to know. I was worried seeing things like Hash#symbolize etc. and this made a really bad impression.

FWIW we have rom-support lib which I hate and it’s gonna be sliced into dry-* pieces soon. I understand the need for such libs but I always try to find solid, re-usable abstractions eventually and encapsulate them in small libs rather than having some generic ‘support/utils’ libs.

That’s fantastic news. It’s so important to be aware of this problem. Tools that basically pollute your own code with lots of behaviors rather than providing simple, re-usable objects are a poison in the ruby ecosystem.

Thanks again for taking the time to reply to my questions. It feels like we’re in the same boat, more or less, after all.


#8

Thanks for sharing your positive experience, real world examples are the best examples.

Regarding model/rom concerns you have:

[quote=“jxxcarlson, post:6, topic:222”]
The model-repository structure of Hanami is a feature that was especially important during the transition. Please don’t abandon this![/quote]

Nobody is talking about abandoning models decoupled from persistence. This is how it works in ROM too. The difference is huge though as ROM is way ahead of hanami-model in terms of features and maturity, despite the fact it’s still a rather young system. It’s such a huge effort that just like in case of dry-v I hoped ROM could be used in hanami, to increase adoption and help with development. Both parties could benefit.

rom-sql is powered by Sequel too. I’m actually not 100% happy about that because Sequel is a rather big, AR lib, but it works very well for now.

ROM has been developed bottom-up, so low-level parts have been created first. We are just now introducing simplified, high-level abstractions for simple common CRUD cases. Next release will have repositories with full CRUD support and a simplified AR-like mapping for getting-started quickly with ROM.