11 ms·
An Overview of Ruby on Rails 7.1 Features. Part III
- revskill 4y agoRoR is nice, but it lacks one of the most important abstraction layer: The schema layer for HTTP. Currently the default is to mix model params with http params. It's bad default. Of course you can use dry-schema for that purpose, but i do think the framework should enforce that for the whole ecosystem to follow.
- hakunin 4y agoI've had great success building form objects with portrayal (https://github.com/scottscheapflights/portrayal https://github.com/scottscheapflights/portrayal) and ActiveModel. You can put validations into them, and auto-filter the params by making a `from_params(params)`-type constructor. In it you can call require/permit with `portrayal.keywords`. You're right that it'd be nice to have this out of the box, but this is just a few lines of glue code to setup. I'm in the process of making a new app template that does that.
- revskill 4y agoThis gem is just for construction, no validation ? Schema layer purpose is to enforce invariant at the boundary of HTTP.
- hakunin 4y agoThe gem is just for declaring struct keywords (slightly more featureful than vanilla structs). Validation is left up to ActiveModel::Model. I would do a setup like this: class ApplicationForm extend Portrayal include ActiveModel::Model class << self def from_params(params) new(**filter_params(params)) end def filter_params(params) params .require(model_name.param_key) .permit(*portrayal.keywords) .to_hash .transform_keys(&:to_sym) end end end class MyForm < ApplicationForm keyword :first_name keyword :last_name validates :first_name, :last_name, presence: true end Can use it in a controller action like form = MyForm.from_params(params) if form.valid?… # the usual One of the good things about this approach is that you can also use this object in form_for/form_with in your views.
- hakunin 4y agoThat said, there are similar objects one can make without using the "form" moniker, for API submissions for example. Granted, I also wish a solution was built-in.
- deleted 4y ago[deleted]
- patrickdavey 4y agoHave you looked at ActiveModel::Attributes ? That coupled with ActiveModel::Model makes for simple form objects quite nicely. Also, you can write your _own_ ActiveModel::Type so you can require that a particular attribute is a particular (business) model. I've been finding it a very nice way to write form objects. It's a bit dated now, but, I did a short presentation for my local ruby meetup and the slides are here [1] 1. https://slides.com/patrickdavey/rails-5-2-attributes-api#/25 https://slides.com/patrickdavey/rails-5-2-attributes-api#/25
- hakunin 4y agoThanks for the suggestion. I actually explored switching to Attributes for a new Rails app, but 3 reasons made me come back to portrayal gem: 1) attributes don't support defaults. 2) I prefer not to set types on form object attributes, since they are supposed to deal with messy user input. Instead I lean on validations. And 3) I like read-onlyness, freeze, equality, duplication, and introspection features of portrayal gem.
- patrickdavey 4y agoAttributes do support defaults. You can absolutely do: attribute :my_time_at, :datetime, default: -> { Time.now } attribute :persisted, :boolean, default: false What I like about the types is that it will automatically coerce the messy user data automatically (this is what AR is doing under the hood anyway right?), you can just be more intentional about it. Anyway, glad you're happy with portrayal :) I definitely agree with you on the "freeze, read-onlyness" etc. being good things to have!
- deleted 4y ago[deleted]
- jbverschoor 4y agoYeah, the whole permitted params is actually is pretty shitty workaround against something else. We're missing the 'form' part. Validations should be done against forms, not models. Default forms can easily be derived from models, which is nice for simple apps, but this is really something that's lacking in rails. Permitted params exists because there is no form (dry-schema for example). There's ActiveModel, but it's not the same, and it's not ad-hoc / bound to the view/controller layer of things.
- revskill 4y agoIt's the main reason for bad legacy of many of Rails applications in the wild (noone wants to maintain the model code inside the view/controller). And it's HARD to understand why ? Omasake is bad omasake when you mix "unrelated concerns" together.
- canadianfella 4y ago[dead]
- deleted 4y ago[deleted]
- rubyfan 4y agoSounds like you want MVVM vs. the default MVC pattern Rails ships. I think there are a few options out there which are bolt on for Rails but nothing out of the box.
- bvirb 4y agoI inherited a project where all the validations were in the forms and it meant that you couldn't reliably use the model layer without the forms layer (you'd end up saving invalid data). We eventually moved the validations to the model layer and dropped the forms abstraction. It didn't feel like the right separation of concerns when we had to pretend data was moving through forms to reliably interact directly with the model layer. The enforcement of allowed params (and type casting from strings) definitely seemed like it was in the right place per-action in the control layer though, so we kept that from the forms even though we implemented it in the controllers. I wonder if we just didn't understand the idea though? Or maybe if the original implementers didn't implement it right?
- Spivak 4y agoYeah, I'm pretty sure every Rails shop has invented some flavor of APIObjectSerializer based on JSON-API, REST, or whatever because doing it ad-hoc will make you pull your hair out.
- ezekg 4y agoI also wasn't a fan of how Rails handles params, so I ended up writing my own lib. And I'm actually working on open sourcing it and extracting into a gem. typed_params { param :data, type: :hash do param :first_name, type: :string, optional: true param :last_name, type: :string, optional: true param :email, type: :string, format: { with: /@/ } param :password, type: :string, length: { minimum: 8 } with if: -> { current_bearer&.admin? } do param :metadata, type: :hash, allow_blank: true, optional: true param :role, type: :string, inclusion: { in: %w[admin user] }, optional: true, transform: -> (_, role) { [:role_attributes, { role: }] } param :permissions, type: :array, optional: true, if: -> { current_account.ent? } do items type: :string end end end } def create # ... controller code here end Each param schema has to be defined by source, e.g. request body vs query string. And params aren't intermixed (including routing params).
- revskill 4y agoWe want a generic interface: Think Rack for params parsing/serializing, then you could easily parse any params into your own data structure. Stringly types are out of scope.
- ezekg 4y agoWhy do you need something so generic? Can you share a use case?
- fknorangesite 4y agoI'm very ambivalent about Grape, but this very much reminds me of something I do really like about it: the param validation. https://github.com/ruby-grape/grape#parameters https://github.com/ruby-grape/grape#parameters
- ezekg 4y agoYep! I was very inspired by Grape’s param system.
- gregmolnar 4y agoSo many great features. Rails still seems to be the best choice to build a web app.
- dieselgate 4y agoCompletely agree with you! It's kind of an interesting time because my current Org is moving away from a large established Rails codebase - and it makes sense given needs. But Rails is still such a good option for building web apps. It will definitely stick around Edit: i really like seeing "overview" posts like these to get up to speed on new features because many devs seem to rarely work on "up-to-date" versions
- LeonenTheDK 4y agoJust curious, I'm not into Rails or Ruby at all, but given an established codebase, what's the reasoning for a big move like that? My org is doing a similar shift (C#, the legacy .NET Framework -> Elixir) and while I quite like Elixir I don't think the language change itself is doing much for us.
- dieselgate 4y agoWish I was more aware of the decision making process for everything but believe the move is to foster more of a micro frontend architecture where separate js packages/federated components make up the client experience. The Rails side of things is really used as an orchestration layer for the views and to handle business logic while also consuming a .NET database layer for services. It's not really the "normal" way of using Rails but seems to function quite well on a "large scale". I agree it's probably not just the language change itself that's the reason but also due to the agility/advantages of Node compared to a Rails "monolith" - and I'm already starting to speak outside my familiarity of the architectural goals of everything
- LeonenTheDK 4y agoInteresting, thank you for the context. My company's current system is simpler in nature I think (three web servers processing internal and external requests, one DB server, one app server handling everything else, and that's our problem) and scales not at all. We're going from one monolith to another, but set up in a way that can be scaled out arbitrarily, and overall more flexible and robust. Generally I'm interested in stories of these big do-overs because it hasn't been the smoothest of sailing for us.
- siaw23 4y agoRails has matured through the years, I'm convinced it's the right framework for any project if you already know it, you can get by with Hotwire for the FE and not run after React and the rest. Happy to be a Rails dev to be honest.
- jemmyw 4y agoIt's good with React too when you need more interactivity.
- siaw23 4y agoI believe you can do anything React can with Stimulus + Turbo these days. I don't do much FE but I've been tracking Hotwire for some time now.
- sharps_xp 4y agoperhaps for most things but probably not to the extent of something like excalidraw, which uses react
- splitrocket 4y agoYes, but in that case, you can use only the complexity where you need it, instead of globally.
- mokkol 4y agoYou can still use React or whatever on those pages. And for the rest (settings pages, signup pages and all the CRUD pages) you can use standard Rails with Hotwire.
- simpsond 4y agoYes, but there is a whole ecosystem of react components and react developers.
- jacobsenscott 4y ago
- flamebreath447 4y agoMaybe this is a bit close-minded to say. Even with the evolution of Ruby on Rails and adding more rich feature sets, I wouldn’t recommend it as an out of the box Webapp BECAUSE upgrading to newer versions of RoR sucks. The more time I invest in trying to like RoR the more I end up having to fork gems to patch them because upgrades cannot be made smoothly. It so easily integrates ActiveRecord, and heavily encourages it, that when you want to pull out of using it it’s a huge pain. Test suite combines unit and integration testing. It’s been a net loss imho to learn and work with it over the years. I’ve been waiting to be persuaded otherwise. Edit: apparently people disagree with me but haven’t countered the points, is HN just full of Ruby fans?
- hakunin 4y agoNot sure why you're downvoted either, it's a legit concern. One way to address it is by being very conservative and picky with gems. A good example is Hey's Gemfile (the app made by Rails creators): https://gist.github.com/dhh/782fb925b57450da28c1e15656779556 https://gist.github.com/dhh/782fb925b57450da28c1e15656779556. It's a good (albeit slightly outdated) example of not straying too far from defaults, while not reinventing the wheel either.
- deleted 4y ago[deleted]
- Gigachad 4y agoYou have to be super careful with gems because every single one you add risks blocking you from updating rails. Resulting you either having to wait for them to get updated, or if they are abandoned, you have to remove them and rewrite all the code that depends on them. I've spent so long replacing all the code in our app which is built on abandoned gems.
- hakunin 4y agoTrue. I tend to stay away from gems that try to integrate into multiple parts of your app to provide some sort of comprehensive solution. The kinds of gems I recommend are: 1) libraries (you call into them when you need them) 2) mounted apps on a url, isolated from the rest of your app 3) generators (a nice example: https://github.com/lazaronixon/authentication-zero https://github.com/lazaronixon/authentication-zero).
- moomoo11 4y agoI have a question and anecdote. I’ve been on teams in the past that had to deal with rails apps (legacy code usually) and I found ruby to be really difficult to understand. There are so many ways to do one thing, and with the rails addition there are now so many conventions to follow. Then when those conventions don’t work anymore, and the code base goes off the rails (no pun intended) and the out of the box conventions no longer make sense. Granted most of my experience is writing Go, followed by dart, swift, and typescript. I have a hard time navigating codebase of a dynamic language and it’s a pita to have to rely on tests (not everyone writes good tests). I use Go for all my backend tasks and it is not only fast performance, but really easy to be productive in because there is usually one way to do things and the std lib is amazing. Where does rails fit in? Like I can build an entire production ready backend service that can scale in like 2 days with Go that is easy to deploy and maintain, and not need to bring in any big Go framework or libs besides like uuid, managed sdk, or db driver. Why would someone choose rails over Go or even node JS with express and a few middlewares? (In 2023)
- revskill 4y agoRuby is OOP. Go is procedural. They're different.
- deleted 4y ago[deleted]
- moomoo11 4y agoI know. I'm asking why someone would choose rails to build software (a company) than something like Go which is IMO easier to learn and way easier to actually manage.
- revskill 4y agoRuby provides a complete hackability/customization framework for you to use. So library locked-in is out of place. For example, you use a library which gives you 99% of functionalities you want. Now there's a 1% use case you want different behaviour and you can't directly modify library code. There's an escape hatch here, it's Ruby. It's like "fork" on steroid. Why Rails ? Because it's the "only" web framework in Ruby. No second choice for large scale application.