I’m new to Hanami. I’m trying to convert a Rails application over to a Hanami application. In the rails app we have a health page that can be viewed in either html or json.
What would be the Hanami way to implement show both formats?
Also I want to share the logic for the data. I put the shared logic in the view, but I am guessing that was also not quite right. Where should the shared logic go?
I forked your repo and I was able to combine the two actions into one: at /health
See me work here:
I’ll work to update the docs to illustrate this use case, since it’s a common one.
I started working on getting health.json to go to the same endpoint as well, but I don’t have time to finish it right now.
And yes, your intuition is right that the actions should not include a Deps from a view. That inverts the dependency graph in a messy way. An action automatically renders a view (for HTML, when you directly set response.body, it doesn’t do that).
As for a better way to do it: I’d make a Status domain object (with Data.define personally but a Plain Old Ruby class is fine) that the OrcidAPIStatus operation returns, instead of serializing to JSON from that operation. Then, I’d serialize that in the action to JSON, and the HTML view can access the Status domain object directly in the view (passed from the action). I’ll work on this later, to fully illustrate what I mean as an example for others, as well.
I thought I should be able to do what you have shown, but could not quite get it to work.
For the domain object where would that live? Is it something like an Operation? I added the service folder for some POROs that we had in our Rails app, but I have been trying to exercise the “correct” concepts in Hanami.
I’m excited to see the complete example.
As you can see my Rails app and Subsequent Hanami app will be open source, so if you like they can be utilized by the community for an example conversion (assuming I succeed). I also did a test migration, which may also be helpful (If I did it correctly).
If you only need to support it with the correct JSON headers then just adding the second route to the same endpoint works fine. I imagine you want to support it even for default web requests too, though. Hanami itself isn’t aware of the file extension so doesn’t map it to a ‘format’. The current logic is documented here: V2.2: Formats and MIME types | Hanami Guides
We could consider adding the file extension to format mapping.
There are a number of ways to map the health.json endpoint to be treated as JSON format, regardless of the headers passed in. I went for a simple approach: just checking if the path ends with “.json”
You could put this in your app’s base action if you need to do it other places.
You could also handle it at the router level or a middleware, if it’s something you need to do throughout your app.
Inverting dependencies
I moved the API status check to the action. Since the JSON response returns without going to the view, this is a better place for it. Then you pass the response from the operation “up” to the view, which is then exposed to the template.
I spent a little time trying to illustrate my point about creating a “domain object”. I think APIStatus is a better name, to be more explicit. However, I wasn’t sure exactly what you were trying to map out from the underlying API service call. But, to answer your question about the location, I’d just put it directly in app/ to start. Later it could go into a directory as needed. It’s also find to use a Hash, I just prefer modeling things as objects whenever I can.
Open source examples
That’s great to hear! That’s a super valuable experience and I hope you can document your work. Since Hanami and Rails both work on top of the Rack standard, you could mount Rails inside the Hanami app (or vice-versa). It might be a bit of upfront work, but then you could deploy Hanami sooner and there’s less risk of code drift. The idea would be that you could start serving some end-points with Hanami in production, and gradually move over as needed. You’d also be able to move more incrementally, by continuing to use your ActiveRecord models from Hanami. This is known as the “Strangler Fig” pattern.
We’d love to see you succeed in the effort and are happy to help. Princeton Libraries and ORCID would be tremendously valuable names to have as an example of a live Hanami app
Thank you so much for your help. I hope it is ok, I created a PR from your code (updated a couple things) and merged it. So you now have commits in our Princeton Library repository. If this is not ok I can rewrite the git history to get you out of there. Just let me know.
I would be really interested to know how to mount a Hanami app inside a Rails application. We have talked about a smaller migration for larger projects.
Luckily the ORCID application is not actively being developed at the moment and is a relatively small application, so I am fairly happy tackling this as a whole migration.
In terms of mounting a Hanami app inside a Rails app, I haven’t done it but Hanami.app is a Rack-compatible app.
So, if you want to mount it based on a special distinct path, you could edit your config.ru file with something like:
# Require the Hanami app
# Adjust the path as needed
require_relative '../my_hanami_app/config/environment' # or wherever Hanami.app is defined
# Mount the apps
map '/new' do
run Hanami.app
end
(And likely switch to using rackup instead of rails server, so it loads it properly).
Though you’d have to be careful about constants having naming collisions. Hanami puts everything inside namespaces, so hopefully it wouldn’t be too bad.
Additionally, @stephan recently mounted a single Hanami slice inside a Rails app recently and worked through some of the rough edges: Proof of Concept: A Hanami slice in a Rails app It’s worth going through his posts to see how he resolved some of the issues he found. The main Hanami.app which holds Slices is also a slice itself (though a special one) so much of it should be applicable for mounting a whole app.