Live data from Hacker News

Rails – The Missing Parts – Policies

eng.joingrouper.com

21–30 of 65 posts

Re: Rails – The Missing Parts – Policies

#21

If I can overly generalize, and as a person who generally loves Ruby and Rails, I'll suggest that there are two schools of Rails app development emerging. The first is what I'll term the "Classic School" or "DHH School" characterized by full-blown utilization of all Rails magic such as AR/AC callbacks, pretty skinny but not obsessively skinny controllers, an eschewance of service objects and more. The "Emerging Schoo…

I think there's a lot of miscommunication going on - people are doing the same things but using different vocabulary to describe it - and so I'm not sure I agree with your characterization.

In a nutshell, almost all DCI/service object blog posts I have read seem to have almost all of their issues addressed by a liberal use of `concerns`.

In the end, I think we're all talking about how to separate "concerns/responsibilities/dependencies" into atomic units so our code is easier to reason about; it turns out my user object does a Ton Of Things and it becomes more maintainable once we split it up.

The schism in my mind comes more from random blog post declaring "We have seen the truth! and it is $new_jargon_here" and then DHH pops out of the wilderness and crankily declares "yes, yes, this is why we introduced xyz, jeez."

What strikes me most in the end is that most of the random blog posts seem to lack a coherent theory for why their code has improved, rendering most of the conversation around it moot. DHH on the other hand has strong, and well formed opinion on the subject which is why he can occasionally come through and refactor it along his mental model.

This is all to say: I have rather strong, if currently unarticulated, opinions on how to think about code organization, and I tend to be more on DHH's side when it comes to these debates. Furthermore, I'm increasingly convinced we're all talking about the same things using different words.

Re: Rails – The Missing Parts – Policies

#22
post #2

Oh boy, is DHH going to jump into this too? As an intermediate Rails developer...I don't understand why this if/else forest: if user.blacklisted? # They've been banned for bad-behaviour fail! “You can't book a Grouper at this time” elsif grouper.full? fail! “This grouper is full, please pick another date” elsif grouper.past? fail! “This grouper has already occured!” elsif user.has_existing_grouper?(grouper) fail! “Yo…

This seems like, in essence, a simple function placed in a domain context. For example, in node.js, I would make this a module (a file that exports a function). The module would be named and nested in a directory structure that matches the domain. It seems that the logical ontology is based on ruby's objects. Unfortunately, this makes the solution much more convoluted than a simple function exported in a module.

The solution you describe is isomorphic to the one proposed in the blog post, which I think you recognize?

But both leave a lot to be desired. I personally think in both cases they are a code smell, and we ought to either introduce an object whose explicit job it is to worry about these inter domain interactions or pile it up as a mixin into the appropriate domain models.

Re: Rails – The Missing Parts – Policies

#23

If I can overly generalize, and as a person who generally loves Ruby and Rails, I'll suggest that there are two schools of Rails app development emerging. The first is what I'll term the "Classic School" or "DHH School" characterized by full-blown utilization of all Rails magic such as AR/AC callbacks, pretty skinny but not obsessively skinny controllers, an eschewance of service objects and more. The "Emerging Schoo…

Similar to http://words.steveklabnik.com/rails-has-two-default-stacks ?

Actually I disagree in a big way. I think it's a mistake to conflate choices in tooling like HAML/HTML & mySQL/pg with architecture choices.

Your architecture choices change the way you write your application code and how you utilize the Rails framework. On the other hand HAML is not going to change the way you write markup, it just provides a different syntax that some people prefer. Postgres has features people like that mySQL lacks. MiniTest & Rspec are different syntaxes for testing. These three choices have very little to do with how you use Rails & write your app code, they just happen to be made by the same people who advocate big architecture changes.

I fall in a camp where I use HAML, postgres & Rspec but I also use fat models and skinny controllers. I have a few really small services in my apps but I try to reserve them for cases when the standard Rails pattern is extremely ugly rather than actively seeking out replacements for the Rails way.

Re: Rails – The Missing Parts – Policies

#24

If I can overly generalize, and as a person who generally loves Ruby and Rails, I'll suggest that there are two schools of Rails app development emerging. The first is what I'll term the "Classic School" or "DHH School" characterized by full-blown utilization of all Rails magic such as AR/AC callbacks, pretty skinny but not obsessively skinny controllers, an eschewance of service objects and more. The "Emerging Schoo…

I agree that Rails is polarizing into two schools - Steve Klabnik's blog post details this really well. But I don't think that moving towards more single-responsibility objects means you have to give up all that Rails offers - particularly the preference for concise, human-readable APIs and convention-over-configuration. Ie, we don't need tonnes of boiler-plate just because we want to use more objects. We put this pe…

As I point out here https://news.ycombinator.com/item?id=7437872 I think Steve Klabnik's argument is actually conflating two phenomenona. One that there are a set of tools/gems that are more conducive to productivity than the Rails defaults (HAML/Postgres/Rspec) and another that some developers find the Rails architecture to be a pain point.

They are overlapping camps but not exactly the same.

Re: Rails – The Missing Parts – Policies

#25
post #18

Earlier quoted context omitted.

We've been using Pundit with great success in our organization. We use it along with a few other objects described below. We have abilities, which is just a table full of strings like "modify users". The policy (from Pundit) looks these up to find whether an actor (user, typically) can do something to a model. We have roles, which is just a user-definable collection of abilities, so you might define an 'admin' role t…

Curious is you have some more details about this written up anywhere or some code on github? I'm just using Pundit in a "real" app for the first time where strict user types won't be enough and I need to have a role based approach. I'm doing something similar to what you have now, but a couple steps back in my design process, and the managable concern sounds promising.

That would be a good idea for a writeup. I'll have to get that done. For now, I have a couple more details.

We're using the closure_tree gem to give a hierarchy to the permissions; every permission can have a parent, which may further limit which abilities are allowed on a managable by a manager. This means that if user A has abilities "1,2,3" on an object, when he grants a permission to user B, while he can create the permission with whatever abilities he wants (maybe abilities "2,3,4"), the parent of that user B's permission is user A's permission, which restricts the real abilities user B has so that user B effectively has only abilities "2,3" on that object. To do this, we get the whole ancestor chain and take the intersection of all the abilities of each permission.

We also have organization objects; these are the owners of everything in the system - they own the users, the roles, the permissions, etc. Organizations may also be a manager or a managable; in fact, an organization automatically gets a permission with itself as both the manger and the managable, with every ability. We also use closure_tree with organizations: when an organization is the managable part of a permission, that permission applies to everything the organization owns, including all descendants.

So we have permissions with a hierarchy where we look upward at its ancestors to limit its abilities, and organizations with a hierarchy where we look down to find the scope that permission operates on.

Re: Rails – The Missing Parts – Policies

#26
post #2

Oh boy, is DHH going to jump into this too? As an intermediate Rails developer...I don't understand why this if/else forest: if user.blacklisted? # They've been banned for bad-behaviour fail! “You can't book a Grouper at this time” elsif grouper.full? fail! “This grouper is full, please pick another date” elsif grouper.past? fail! “This grouper has already occured!” elsif user.has_existing_grouper?(grouper) fail! “Yo…

I think ultimately it's a philosophical difference; the problem with very many large Rails deployments is that the ActiveRecord models are the core of the application, and DHH's conception of Rails seems to encourage this. This tends to cause a lot of problems - it's hard to exercise a single model in tests without having a very large number of associated objects present. This also tends to require them to be in the…

It sounds like you're letting "ease of test maintenance" drive the architecture of the rest of your app; I'm inclined to accept that incentivizing test creation will lead to more confidence in the face of changes but am somewhat unconvinced it's 1:1 with "less coupled, more maintainable" a priori.

Case in point, I'm really uncomfortable with class names with verbs. Typing CanConfirmTicketPolicy.new(ticket: ticket).allowed? smells bad to my fingers.

It strikes me that you haven't eliminated the coupling? because the CanConfirmTicketPolicy still depends on different domain objects. Kind of by definition, you can't remove it because it's explicitly operating on User, Group and Ticket; the main difference to me seems that they're easier to mock?

I would argue that this ought to be either an explicit inter domain class that models the interaction between Users, Group and Tickets - some appropriate noun like Purchase or GroupActivity or whatever - that can then be solely responsible and reused as appropriate.

You have a bit of text that explains your decisions,

>We could move the logic to a controller mixin, or define the method on the ApplicationController, but it would still not be available in our view

Could you not just define a helper method, then? Helpers are available in both views and controllers if memory serves, are mixed in by default, are equally easy to test as your verbed policy object and have the (minor) added benefit of not polluting your namespace while being equally easy to reason about - it's unclear to me how can_confirm_ticket?(ticket) would necessarily be inferior.

If you're really interested in putting these in models (which is also acceptable) then a regular concern namespaced to your preferred model would work just as well.

Am I missing something? I would like to understand your use case but I don't seem to get it.

Re: Rails – The Missing Parts – Policies

#27
post #26

Earlier quoted context omitted.

I think ultimately it's a philosophical difference; the problem with very many large Rails deployments is that the ActiveRecord models are the core of the application, and DHH's conception of Rails seems to encourage this. This tends to cause a lot of problems - it's hard to exercise a single model in tests without having a very large number of associated objects present. This also tends to require them to be in the…

It sounds like you're letting "ease of test maintenance" drive the architecture of the rest of your app; I'm inclined to accept that incentivizing test creation will lead to more confidence in the face of changes but am somewhat unconvinced it's 1:1 with "less coupled, more maintainable" a priori. Case in point, I'm really uncomfortable with class names with verbs. Typing CanConfirmTicketPolicy.new(ticket: ticket).al…

I have no problem with verbs in class (or module) names - I'd actually say we should encapsulate more of this "activity" or "verb" behaviour in single-responsibility objects, and not within ActiveRecord models.

Eg, a ConfirmGrouper.perform object (http://eng.joingrouper.com/blog/2014/03/03/rails-the-missing...)

Uncle Bob's talk gives good reasons for this pattern: http://www.confreaks.com/videos/759-rubymidwest2011-keynote-...

Basically, your data-persistence objects (ActiveRecord models) become more composable because they do fewer things and rely less on associated objects being present to work properly.

Your "interactor" objects which perform "verb"-style activities can also be broken down into very small units with single-responsibility and then composed with each other to perform more complex interactions.

Putting all of this logic into models makes it very very hard to break apart units of behaviour and re-compose them.

This is what I mean by "less coupled, more maintainable"

Re: Rails – The Missing Parts – Policies

#28
post #21

If I can overly generalize, and as a person who generally loves Ruby and Rails, I'll suggest that there are two schools of Rails app development emerging. The first is what I'll term the "Classic School" or "DHH School" characterized by full-blown utilization of all Rails magic such as AR/AC callbacks, pretty skinny but not obsessively skinny controllers, an eschewance of service objects and more. The "Emerging Schoo…

I think there's a lot of miscommunication going on - people are doing the same things but using different vocabulary to describe it - and so I'm not sure I agree with your characterization. In a nutshell, almost all DCI/service object blog posts I have read seem to have almost all of their issues addressed by a liberal use of `concerns`. In the end, I think we're all talking about how to separate "concerns/responsibi…

I disagree - I think we're talking about two competing philosophies.

DHH / standard-Rails seems to encourage you to fit things into Models, Views or Controllers. Models got too fat, and so Concerns were introduced. This is problematic, because you still have massive god-models, but now their code is split between a dozen different files, called "Concerns". The main problem is that by making ActiveRecord models the core of your application, you generally give each model too much responsibility & too much knowledge of its associated objects. This means it's very hard to break down & recompose the different functionality into new objects. A model only functions correctly if all of its associated objects are present. This is tight coupling.

The alternative view is to introduce several new single-responsibility objects like Interactors/Service-Objects (and Policies, as here) which should contain the core of your application's business logic. These Service Objects are the opposite of concerns - they do not extend the API of models. They exist to control the interactions between the models. The models become dumb interfaces that deal with data validation and persistence. Because they know hardly anything about the other objects in the system, and have very limited APIs, they can be passed around easily, and re-composed into more complex interactions. They are also incredibly simple to test. The same applies to Interactors - they should be broken down into small units of behaviour with very limited APIs, which can then be passed around easily and composed into more complex interactions. This is loose coupling.

Re: Rails – The Missing Parts – Policies

#29
post #26

Earlier quoted context omitted.

It sounds like you're letting "ease of test maintenance" drive the architecture of the rest of your app; I'm inclined to accept that incentivizing test creation will lead to more confidence in the face of changes but am somewhat unconvinced it's 1:1 with "less coupled, more maintainable" a priori. Case in point, I'm really uncomfortable with class names with verbs. Typing CanConfirmTicketPolicy.new(ticket: ticket).al…

I have no problem with verbs in class (or module) names - I'd actually say we should encapsulate more of this "activity" or "verb" behaviour in single-responsibility objects, and not within ActiveRecord models. Eg, a ConfirmGrouper.perform object ( http://eng.joingrouper.com/blog/2014/03/03/rails-the-missing... ) Uncle Bob's talk gives good reasons for this pattern: http://www.confreaks.com/videos/759-rubymidwest2011…

>Basically, your data-persistence objects (ActiveRecord models) become more composable because they do fewer things and rely less on associated objects being present to work properly.

I agree with this entirely, and elsewhere in this thread have said so, and I'm not advocating fat models.

I'm more questioning the precise way in which you've chosen to perform your refactoring. To me, that-method-in-partcular seems like a code smell, and I've not been convinced by your blog post compared to the alternate methods I've suggested in my comment. Does that make sense?

Re: Rails – The Missing Parts – Policies

#30
post #26

Earlier quoted context omitted.

I think ultimately it's a philosophical difference; the problem with very many large Rails deployments is that the ActiveRecord models are the core of the application, and DHH's conception of Rails seems to encourage this. This tends to cause a lot of problems - it's hard to exercise a single model in tests without having a very large number of associated objects present. This also tends to require them to be in the…

It sounds like you're letting "ease of test maintenance" drive the architecture of the rest of your app; I'm inclined to accept that incentivizing test creation will lead to more confidence in the face of changes but am somewhat unconvinced it's 1:1 with "less coupled, more maintainable" a priori. Case in point, I'm really uncomfortable with class names with verbs. Typing CanConfirmTicketPolicy.new(ticket: ticket).al…

> It sounds like you're letting "ease of test maintenance" drive the architecture of the rest of your app; I'm inclined to accept that incentivizing test creation will lead to more confidence in the face of changes but am somewhat unconvinced it's 1:1 with "less coupled, more maintainable" a priori.

I've thought of making a similar comment on _many_ other articles but never have. This is actually the first time I've ever even _seen_ anyone else saying it. I wonder if we're relatively unique or if many other people feel the same way and simply (like myself) don't actually comment on it.

Post reply on HN