News for hackers. Stuff that matters.
Re:My time with Rails is up by tptacek on Sunday May 22, 2016 @01:17PM (#11749369)
This is how Rails works, classic example:

    User.create(params[:user])
You see a simple line of code, and you can immediately say (assuming you know User is an AR model) what it’s doing. The problem here is that people confuse simplicity with convenience. It’s convenient (aka “easy”) to write this in your controller and get the job done, right?

Now, this line of code is not simple, it’s easy to write it, but the code is extremely complicated under the hood because:

- params must often go through db-specific coercions

- params must be validated

- params might be changed through callbacks, including external systems causing side-effects

- invalid state results in setting error messages, which depends on external system (i.e. I18n)

- valid params must be set as object’s state, potentially setting up associated objects too

- a single object or an entire object graph must be stored in the database

This lacks basic separation of concerns, which is always damaging for any complex project. It increases coupling and makes it harder to change and extend code.

This feels like the core of the argument of the whole post. It does not seem correct. In isolation, the call to User#create seems magical. But there's not enough context here to criticize it. We don't know enough to say whether there's inadequate separation of concerns.

No matter how we handle "users", we are going to need to do all 6 things in the list the author presented. No matter how we structure those 6 things, there is going to be a single entrypoint function that kicks them off --- end users won't be pushing 6 different buttons to drive them. So: what's the better design here?

Re:My time with Rails is up by solnic on Sunday May 22, 2016 @01:30PM (#11749426)
The point I made is that this is all happening in your ORM layer, in ActiveRecord these concerns are not separated. Recent addition of Attributes API at least isolated coercion logic, but it's still part of the ORM, which for me is a mistake.
Re:My time with Rails is up by tangue on Sunday May 22, 2016 @01:42PM (#11749473)
This might no be the the best example, but this is not a Medium blogspam, the guy has wrote several widely used gems and we should not stop at this technical example to invalidate the whole points he is trying to make (and I don't consider this code to be the core argument of the post.)
Re:My time with Rails is up by eropple on Sunday May 22, 2016 @01:30PM (#11749430)
> This feels like the core of the argument of the whole post. It does not seem correct. In isolation, the call to User#create seems magical. But there's not enough context here to criticize it. We don't know enough to say whether there's inadequate separation of concerns.

There's enough context for me to start putting up the horns and cheering, because AR is one of the more persistent boils on my ass when I'm building things in Ruby (because I use Ruby for Ruby, rather than Rails, my frontends are all JavaScript and my services are all Grape). His last point is sufficient to me to demonstrate inadequate separation of concerns--the better design is the one that doesn't require an active database connection to know what fields the object has in it. Answering "how do I build an SOA without every service having a line to every database?" inevitably becomes "don't use ActiveRecord, ever".

What solnic has done with Virtus (which I use in all my projects, it's awesome, he's one of my favorite current Ruby people because of it and I'm sad that he may be leaving) is to return to the novel notion that an object knows what's in it. Dumb data objects are in almost every case a better long-term strategy, and solnic's frustration, if not expressed perfectly, is one that inevitably burns even Rails projects that grow to scale. And while this is a single example of Rails's monolithic nature choking out a lot of the Ruby ecosystem, it's not the only one (see his comments on ActiveSupport).

Re:My time with Rails is up by ryanbrunner on Sunday May 22, 2016 @01:50PM (#11749516)
That's a totally valid point, but is 100% orthogonal to what's being discussed. Invoking a single command to validate, run callbacks, persist to the database, etc, etc. a model (and the larger argument of whether conflating DB access and persistence logic is a good thing) has nothing to do with whether the object is aware of it's own properties.

They are both critcisms of ActiveRecord, for sure, but quite a bit different in content. One (AR objects not knowing their own properties) feels, at least to me, like an unfortunate early design decision, while the other (encapsulating everything involved with saving a model in a single statement, "magic avoidance" be damned) is very much in line with the core philosophy of ActiveRecord and Rails.

Re:My time with Rails is up by eropple on Sunday May 22, 2016 @01:53PM (#11749525)
> a single object or an entire object graph must be stored in the database

Hence "the last point" (though you might have caught me mid-edit). I view these as functionally the same problem: not only are you instantiating the object, but it must immediately go to live in the database or it's useless.

But you're right in that it is a philosophical problem. Rails is wrong. =)

Re:My time with Rails is up by qaq on Sunday May 22, 2016 @01:57PM (#11749534)
There are always pros/cons to any approach one can as easily argue that without AR your code is less DRY as you need to keep your models in sync with your DB schema manually. (I am not a fan of AR either)
Re:My time with Rails is up by majewsky on Sunday May 22, 2016 @05:30PM (#11750368)
You could easily build a unit test that compares the set of exposed properties of your model class with the columns of the table in your test DB.
Re:My time with Rails is up by qaq on Sunday May 22, 2016 @05:58PM (#11750465)
That's not really a unit test and not really a good idea to do it this way. You'll still have to keep model in sync with schema you'll just know when they diverge.
Re:My time with Rails is up by eropple on Tuesday May 24, 2016 @01:53AM (#11759232)
If that's a concern, invert it and create tables off of your object model. It's Ruby, that's trivial.
Re:My time with Rails is up by smitherfield on Sunday May 22, 2016 @02:55PM (#11749753)
I agree with the author that abstractions should be optional, whenever possible.

However, just because something is heavily abstracted doesn't mean it's a bad approach, at least from a productivity standpoint. Without certain things being heavily abstracted, we'd go insane.

Just as

  User.create(params[:user])
gets translated to thousands of lines of Ruby, so also

  CoolClass.new('foo', 3)
gets translated to thousands of lines of C, and

  printf("It's %d degrees today in %s.\n", temperature, city);
gets translated to thousands of lines of assembly.

Are they all heavy abstractions? Yes. Is it oftentimes very nice to be able to use heavy abstractions? Also yes.

Re:My time with Rails is up by phamilton on Sunday May 22, 2016 @05:20PM (#11750332)
To me the point isn't so much separation of concerns as it is separation of side-effects.

printf has a well design side effect (printing to stdout). Coolclass.new has an expectation of an instantiation of object in memory. If Coolclass.new also called an external service, then that would be a violation in my opinion. Thankfully, we generally don't do that.

User.create is not only capable of tons of side effects (just like Coolclass.new) but it is encouraged to do so (via callbacks and such).

Re:My time with Rails is up by narag on Sunday May 22, 2016 @03:19PM (#11749856)
There was a recent article about Julia that discussed how a printf in C translate to very few instructions in assembler.

Also I would like to point that "translates" is a little misleading word. There are two ways of implementing an abstraction from a higher level system to a lower level one.

You can generate code or you can write a runtime framework that does the work dynamically. Although from the higher place both may seem the same, there's a huge difference between them that has consequences.

Re:My time with Rails is up by MegaDeKay on Sunday May 22, 2016 @03:58PM (#11750009)
The link to that article is below. printf in C tranlates to a few lines of assembly in preparation for a call to sprintf. That in turn will have a lot more instructions in assembler.

http://zverovich.net/2016/05/13/giving-up-on-julia.html

Re:My time with Rails is up by hackerboos on Sunday May 22, 2016 @07:08PM (#11750682)
Indeed and OP is mixing up language implementation and standard libs with a framework.
Re:My time with Rails is up by smitherfield on Sunday May 22, 2016 @03:27PM (#11749880)
>There was a recent article about Julia that discussed how a printf in C translate to very few instructions in assembler.

I wouldn't doubt it; compilers have become amazing at optimizing. But printf is hundreds of lines of C, at very least: http://opensource.apple.com//source/Libc/Libc-1082.20.4/stdi...

Re:My time with Rails is up by narag on Sunday May 22, 2016 @05:47PM (#11750427)
Well, hundreds is not the same as thousands ;-)

And most important, a call is not the same as the implementation of the called function, my point to begin with, please see my comment in the context of yours.

Re:My time with Rails is up by smitherfield on Sunday May 22, 2016 @07:22PM (#11750725)
Per cloc it's actually 1093 lines of C (plus some more in the printf.c file), and generally speaking hundreds of lines of C is the equivalent of thousands of lines of assembly.

In any case, the complexity of the called function is exactly what we're discussing — it's why the author objects to "User.create(params[:user])"

Re:My time with Rails is up by netghost on Sunday May 22, 2016 @02:05PM (#11749569)
For anyone who wants to know how all these pieces work together, I've been writing a series that actually dives into the source of Rails:

http://www.monkeyandcrow.com/series/reading_rails/

Sure, Rails is complex, but it takes on that complexity to let us be productive. When you run into that complexity, read the source.

Re:My time with Rails is up by erikpukinskis on Sunday May 22, 2016 @07:54PM (#11750854)
My problem is whenever something spooky doesn't work, I never know which part of the source to read through. You need to understand the architecture to even know where to look. Which means you have to go through a huge up front process (like reading a webseries on the subject) to even get started. This front loading is the kind of thing that keeps more people from getting into programming, and is one of the primary ways we keep our salaries high.
Re:My time with Rails is up by braythwayt on Sunday May 22, 2016 @01:25PM (#11749401)
I agree that this is not a good example of simplicity vs ease, but I think his core argument is still correct about what Rails is. (Whether you or I may like/dislike "What Rails Is" is, of course, another matter.)
Re:My time with Rails is up by tptacek on Sunday May 22, 2016 @01:30PM (#11749428)
The other way to look at Rails is that, as a framework, it has made a decision to have the model carry most of the weight for domain logic. In a "fat model skinny controller" framework, you're not surprised to see single lines of "magical" code like this in the controller, any more than you're surprised that each line of the route table masks thousands of lines of code that execute when a URL is matched.

This is very much not Merb's design. But that doesn't make it an invalid design; it just means that understanding a Rails app might best start with the models, not the controllers.

(There's a lot not to like about Rails! It's been years since I worked in it full time and I don't miss it.)

Re:My time with Rails is up by eropple on Sunday May 22, 2016 @01:34PM (#11749441)
> that doesn't make it an invalid design

In 2016, with systems regularly decomposing into services for both perf and code management purposes, I'm trying really hard to find a reason to say this isn't an invalid approach except for what amount to toy problems (the kind where "if you don't know where you're trying to go, any road will get you there"). ActiveRecord being a mudball of "what X is" and "how to get X" is a really, really big problem, and my view of it as an observer--heavy in Ruby, heavy in avoiding Rails at all costs--is that it seems to be sourced from an unwillingness of the Rails community to consider that not everything is Rails. Services become intractable, as I note in my reply to your first post--and I don't even mean "microservice all the things", just the profoundly unsexy and unbloggable "SOA".

Re:My time with Rails is up by braythwayt on Sunday May 22, 2016 @05:36PM (#11750387)
Rails can be used in many contexts, but its first love is the making of integrated systems: Majestic monoliths!--DHH

Source: "The Rails Doctrine" http://rubyonrails.org/doctrine/

Re:My time with Rails is up by eropple on Sunday May 22, 2016 @06:02PM (#11750475)
That DHH might even believe with a straight face enough of that essay to write its contents like an English major who just discovered the adjective doesn't really change that the universe is continuing to shrink the place in which his viewpoint can make sense. And, with it, goes Ruby's relevance, because of the heavy weight of Rails on the Ruby world as a whole.
Re:My time with Rails is up by erikpukinskis on Sunday May 22, 2016 @07:46PM (#11750820)
> No matter how we handle "users", we are going to need to do all 6 things in the list the author presented.

I think maybe your broader thesis is we almost always need all 6 things. And if that's true for you then Rails is a good tool for you.

If we only ever need one out of the 6 things at a time then ActiveRecord is only adding extra complexity for us.

I think OP is basically saying I usually only need one or two and that they'd rather not have the extra layer of indirection... "save(andValidate)" is fine for them. They don't need the orchestration abilities that the declarative "afterSave(validate)" step affords.

DHH's suppress really is a good example. If you are calling save(andValidate) everywhere, you can't really do something like suppress(), because your validation logic is disbursed.

My opinion, and I think maybe OP would agree, is that there's usually a nice, composeable way to get the same problem solved, without resorting to a giant engine that understands all the different parts. But that's just an empirical assertion on our part. Obviously your mileage will vary.

Re:My time with Rails is up by godd2 on Monday May 23, 2016 @05:49AM (#11752808)
> because your validation logic is disbursed

I think you want "dispersed" here.

Re:My time with Rails is up by oneloop on Sunday May 22, 2016 @01:43PM (#11749478)
Just to point out that all those objections could be made about Django as well.
Re:My time with Rails is up by cname on Sunday May 22, 2016 @01:59PM (#11749543)
In Django, the validation & type coercion would typically happen in a Form object (or, if you're using Django REST Framework, a Serializer). That step at least is outside of the ORM and seems much more explicit to me.

Also, in Django models are explicitly defined. There's no ActiveRecord magic of reading the database to define the model. Which is not to say that AR's approach is bad or wrong, but it is pretty significantly different from Django.

Re:My time with Rails is up by draw_down on Sunday May 22, 2016 @08:45PM (#11751039)
"Explicit is better than implicit"
Re:My time with Rails is up by oneloop on Sunday May 22, 2016 @02:30PM (#11749658)
Hm. I was trying to guess what Ruby was doing and it appears I guessed wrong. Thank you.
Re:My time with Rails is up by hiphipjorge on Sunday May 22, 2016 @04:24PM (#11750120)
I haven't worked with Rails, but have worked extensively with Django and this doesn't seem to be the case. Even though Django is very similar to Rails in some ways, to pretty big differences is that Django seems to be much more explicit in general and (I might be wrong about this) Django seems to be easier to use in a SOA since your controllers (views in Django) are not as tightly coupled to your models.
Re:My time with Rails is up by cauterized on Monday May 23, 2016 @07:28AM (#11753136)
That's correct. Around the time that Rails became popular, Django was undergoing a major refactoring to remove "magic" and make its underlying logic more explicit.

Further smaller changes have been made since then for the same purpose: explicit over implicit, code or configuration over convention is part of Django's philosophy.

IMO that also makes it easier to graduate from a beginning to an intermediate level use of the framework because while there are sane defaults in many places, in others you're required to define your specific implementation explicitly from the start. It's a tiny bit more code up front, but all the details are exposed and it's obvious how to override default behavior. And at the same time the framework supports keeping your code reasonably DRY and takes care of much of the tedious, repetitive stuff.

There's still tight coupling in some places (ModelForms, for instance, are arguably part of the controller layer and are tightly coupled to the ORM). But they're reasonably optional (you can use plain Django Forms instead of ModelForms in your views or not use the Forms library at all.)

Re:My time with Rails is up by jbbarth on Monday May 23, 2016 @08:12AM (#11753319)
This argument often comes up and I'm genuinely curious why people think Django is less "magic" than Rails, in my experience it has been the opposite.

My experience with Django (for instance: class-based views and admin parts) is actually that you have dozens of magical classes or methods and if you don't know them all, you're screwed. Often when I try to do something, many aswers on StackOverflow boil down to "just override a_method_that_is_hidden_somewhere()" or "just use ListButWithSpecificBehaviorView(), easy you see?".

Are there any specific example you have in mind where Rails do something magical that requires more code in Django but where Django is more explicit?

PS: not trying to start a flamewar, both are OK-frameworks, bla bla bla ;-)

Re:My time with Rails is up by jaxondu on Sunday May 22, 2016 @02:10PM (#11749592)
I had a hard time understand how Rails works under the hood by reading the source. No such issue when reading Django source code. Rails magic is really powerful and I got overwhelmed. Maybe its just my Ruby-fu is not strong.
Re:My time with Rails is up by protomyth on Sunday May 22, 2016 @02:38PM (#11749684)
I get the feeling its not your Ruby-fu, but Rails is a lot like trying to jump into the show Supernatural in Season 10. I did some Rails sites early, and now have very little clue what's going on when I read new code. I'm sure I could get it, but its going to be a time investment. This seems to be the way of opinionated frameworks.
Re:My time with Rails is up by ben_jones on Sunday May 22, 2016 @07:22PM (#11750723)
It's been awhile since I looked at either but I remember as a beginner I had trouble parsing both the Django source and the Rails source. The rails source was the hardest because I didn't understand ruby and the Django source was hard because I didn't understand python.

I feel like people really forget what it's like to be a beginner when they say "read the source".

Re:My time with Rails is up by afarrell on Sunday May 22, 2016 @09:54PM (#11751278)
"Oh, if you are a beginner, you should work on documentation!"
Re:My time with Rails is up by askyourmother on Sunday May 22, 2016 @03:12PM (#11749829)
Better design? In all seriousness Django - they get this right. It seems to hurt rails developers that it means typing a bit more, or is a little less 'magic', but then python did always attract software engineers, whereas Rails always seemed to attract code "ninjas" at best.