Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Doghouse
Oct 22, 2004

I was playing Harvest Moon 64 with this kid who lived on my street and my cows were not doing well and I got so raged up and frustrated that my eyes welled up with tears and my friend was like are you crying dude. Are you crying because of the cows. I didn't understand the feeding mechanic.
Does anyone know of a ruby reference resource I could use for the basics of Ruby, coming from a C#/Java background? I'm having trouble getting a handle on the basic syntax for an assignment. I'm trying to set up a hash as a class variable and I can't even figure out how.

Adbot
ADBOT LOVES YOU

necrotic
Aug 2, 2005
I owe my brother big time for this!

Doghouse posted:

Does anyone know of a ruby reference resource I could use for the basics of Ruby, coming from a C#/Java background? I'm having trouble getting a handle on the basic syntax for an assignment. I'm trying to set up a hash as a class variable and I can't even figure out how.

code:
class Fubar
  attr_reader :hash

  def initialize
    @hash = {
      :foo => :bar
    }
  end
end

c = Fubar.new
c.hash[:foo] # => :bar
http://mislav.uniqpath.com/poignant-guide/book/ is a good starter resource.

EVGA Longoria
Dec 25, 2005

Let's go exploring!

necrotic posted:

code:

class Fubar
  attr_reader :hash

  def initialize
    @hash = {
      :foo => :bar
    }
  end
end

c = Fubar.new
c.hash[:foo] # => :bar

http://mislav.uniqpath.com/poignant-guide/book/ is a good starter resource.

That's an instance variable. A class variable would be @@hash.

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
The answer is don't set a class variable unless you have a really good reason to.

kayakyakr
Feb 16, 2004

Kayak is true

MasterSlowPoke posted:

The answer is don't set a class variable unless you have a really good reason to.

Yeah, better to define a constant.

HonorableTB
Dec 22, 2006
I'm currently learning Ruby in order to further my aspirations to move from QA manual testing into QA automation testing. As my first language, Ruby is incredibly easy for me to understand as long as I comment everything as I go and keep a cheatsheet of functions to remind myself what is what. I'm using the "Learn Ruby the Hard Way" tutorials and I'm on lesson 14 right now, and I'm having an absolute blast playing around with it. My end goal is to be able to use Ruby to make automation scripts for use in Cucumber. Everything in QA is moving towards automation at a rapid pace, and I don't want to get left behind in obsolescence since I'm early in my career (> 1 year, with no CS degree).

What I'm saying is, Ruby loving owns bones.

EVGA Longoria
Dec 25, 2005

Let's go exploring!

kayakyakr posted:

Yeah, better to define a constant.

Constants and class variables serve different purposes, though. Constants should not be changes, class variables can. It makes class variables good for singleton classes, or any case where you are primarily using class methods.

Ruby developers have the weirdest superstitions about really mundane things. Everything has its place.

necrotic
Aug 2, 2005
I owe my brother big time for this!

EVGA Longoria posted:

That's an instance variable. A class variable would be @@hash.

Then here:

code:
class Fubar
  def self.hash; @hash ||= {}; end
end

Fubar.hash
You can use class variables, just be careful. Inheritance is fucky with them:

code:
$ cat test.rb
class Foo
  @@buttes = 'lol'

  def self.buttes; @@buttes; end
end
class Baz < Foo
  @@buttes = 'fart'
end

puts Foo.buttes
puts Baz.buttes
$ ruby test.rb
fart
fart

Cocoa Crispies
Jul 20, 2001

Vehicular Manslaughter!

Pillbug

EVGA Longoria posted:

Constants and class variables serve different purposes, though. Constants should not be changes, class variables can. It makes class variables good for singleton classes, or any case where you are primarily using class methods.

Ruby developers have the weirdest superstitions about really mundane things. Everything has its place.

The Singleton stdlib should be used for singletons :colbert: it uses mutexes to guard against race conditions common to custom singleton implementations.

prom candy
Dec 16, 2005

Only I may dance
I'm glad to see I'm not the only one who uses "fart" and "butts" instead of "foo" and "bar" when I'm writing example/exploratory code.

Doh004
Apr 22, 2007

Mmmmm Donuts...
Is there any other way?

EVGA Longoria
Dec 25, 2005

Let's go exploring!

necrotic posted:

Then here:

code:
class Fubar
  def self.hash; @hash ||= {}; end
end

Fubar.hash
You can use class variables, just be careful. Inheritance is fucky with them:

code:
$ cat test.rb
class Foo
  @@buttes = 'lol'

  def self.buttes; @@buttes; end
end
class Baz < Foo
  @@buttes = 'fart'
end

puts Foo.buttes
puts Baz.buttes
$ ruby test.rb
fart
fart

Class variables usually have a lot of gotchas, so that's not too surprising.

To be honest, I rarely see inheritance outside of Rails. Mix-ins are vastly preferred.

Cocoa Crispies posted:

The Singleton stdlib should be used for singletons :colbert: it uses mutexes to guard against race conditions common to custom singleton implementations.

Nothing wrong with Singleton. But I'd definitely recommend everyone study it and roll their own at least once to appreciate it.

DONT THREAD ON ME
Oct 1, 2002

by Nyc_Tattoo
Floss Finder
Also if you're dealing with load balancers/multiple app instances, or if whatever you're working on is going to be used by workers, class variables may not going to achieve what you want (depending on the case). If you need a class variable and not a constant (say, for something like throttling requests to some external service), you might be better off writing it to your db, or using some other mechanism to update and retrieve the data externally.

It really depends on the case though, if you're just trying to memoize some expensive operation, but don't necessarily need synchronization between app instances, it might be worth using them.

DONT THREAD ON ME fucked around with this message at 00:25 on Jan 25, 2015

Peristalsis
Apr 5, 2004
Move along.
I have a file organization question.

I'm writing a data import facility for our RoR application. I want to allow for the possibility of other importers in the future, so I extracted what core code I could into a mixin module, put that module in the lib directory, included it in the new importer class, and I put that new class in app/models/importers. Rails Console can instantiate the new objects, but when I try to write unit tests, they fail, because the rake testing function won't recognize the constant "Imports". Note that this class is NOT tied to any data table, but app/models seemed like the logical place to put it (please correct me if there's a better place for this entirely).

We have some other code that does something similar, but uses subclasses of a class in the root model directory, instead of a standalone class with a mixin. Do I have to move my importer class file up into the models directory? I really like having the files organized this way, since I anticipate having a couple of more of these import classes, and I don't really want them to clutter up a main code directory with pretty similar functionality. But if I'm breaking Rails's brain, I don't want to have to fight it constantly just for the sake of my directory being pretty.

Am I missing anything obvious that would resolve this nicely?

kayakyakr
Feb 16, 2004

Kayak is true

Peristalsis posted:

I have a file organization question.

I'm writing a data import facility for our RoR application. I want to allow for the possibility of other importers in the future, so I extracted what core code I could into a mixin module, put that module in the lib directory, included it in the new importer class, and I put that new class in app/models/importers. Rails Console can instantiate the new objects, but when I try to write unit tests, they fail, because the rake testing function won't recognize the constant "Imports". Note that this class is NOT tied to any data table, but app/models seemed like the logical place to put it (please correct me if there's a better place for this entirely).

We have some other code that does something similar, but uses subclasses of a class in the root model directory, instead of a standalone class with a mixin. Do I have to move my importer class file up into the models directory? I really like having the files organized this way, since I anticipate having a couple of more of these import classes, and I don't really want them to clutter up a main code directory with pretty similar functionality. But if I'm breaking Rails's brain, I don't want to have to fight it constantly just for the sake of my directory being pretty.

Am I missing anything obvious that would resolve this nicely?

Your lib directory may not be automatically included in tests so it doesn't know where to look. You could simply add it to the autoloaded directories in application or require it at the top of every file you use it in.

Personally, I'd put importers in app/importers (a new folder in the base app directory), and create the mixin module as a concern placed in app/importers/concerns. That is if you're using Rails 4.

DONT THREAD ON ME
Oct 1, 2002

by Nyc_Tattoo
Floss Finder
A lot of it comes down to following the conventions at your workplace, even if they're bad. Module loading is a serious pain point for me with Ruby, and Rails just makes it worse by coming in and getting all cute with everything. I also alternate between working in Rails and working in plain ruby and I can never remember which behavior is a rails thing and which behavior is a ruby thing.

Whenever I'm trying to do what you're trying to do, I just end up using the gem structure:
http://timelessrepo.com/making-ruby-gems

Also, I believe pretty firmly that you should not put things in the models folder unless it's backed by a table. But that's mostly because we have 680 files in our app/models folder (not counting subdirectories!) and it's terrible.

If I'm doing something serious, I build a gem. Otherwise I stick it in lib.

Thalagyrt
Aug 10, 2006

MALE SHOEGAZE posted:

Also, I believe pretty firmly that you should not put things in the models folder unless it's backed by a table. But that's mostly because we have 680 files in our app/models folder (not counting subdirectories!) and it's terrible.

I firmly disagree on this. I have 280 files under app/models right now, however *everything* is namespaced into modules that make sense for the given set of functionality. There are no top-level models whatsoever, and at most I have 50 or so classes in a given module, with an average module size of about 10 classes (not all ActiveRecords). If you have hundreds of files in one directory, sure, I can see how you'd arrive at the conclusion you did. The thing is, when working in Rails, your models, services, lib, etc are all under the same namespace, so separating files out into a ton of directories that all share the same namespace introduces a bunch of gymnastics to make sure that you're not stomping over some other file in another directory with the same class name. Making a new class? Gotta make sure it doesn't already exist in /lib, /app/models, /app/decorators, /app/services, /app/helpers, /app/jobs, /app/use_cases, etc. Merging all of these into /app/models was the single best decision I ever made. Keeping everything together as much as possible and utilizing plain old Ruby modules to organize your code reduces the mental overhead necessary to understand the application a considerable amount.

DONT THREAD ON ME
Oct 1, 2002

by Nyc_Tattoo
Floss Finder

Thalagyrt posted:

I firmly disagree on this. I have 280 files under app/models right now, however *everything* is namespaced into modules that make sense for the given set of functionality. There are no top-level models whatsoever, and at most I have 50 or so classes in a given module, with an average module size of about 10 classes (not all ActiveRecords). If you have hundreds of files in one directory, sure, I can see how you'd arrive at the conclusion you did. The thing is, when working in Rails, your models, services, lib, etc are all under the same namespace, so separating files out into a ton of directories that all share the same namespace introduces a bunch of gymnastics to make sure that you're not stomping over some other file in another directory with the same class name. Making a new class? Gotta make sure it doesn't already exist in /lib, /app/models, /app/decorators, /app/services, /app/helpers, /app/jobs, /app/use_cases, etc. Merging all of these into /app/models was the single best decision I ever made. Keeping everything together as much as possible and utilizing plain old Ruby modules to organize your code reduces the mental overhead necessary to understand the application a considerable amount.

Then all you've done is taken your lib folder and stuck it in models for some reason. There's really no reason to have anything in your app folder that isn't a direct Rails subclass. I mean, you're probably not causing yourself any huge problems, but there's no reason all of that stuff couldn't be in lib, in the same directory structure you have now. The only exception being that your models would have to be in models.

You don't make a lib jobs/workers/services/decorators/etc, that would be ridiculous. You treat it like it's just full of unpacked gems. There's no issue with namespace conflict unless I'm duplicating functionality or giving things really weird names.

Thalagyrt
Aug 10, 2006

MALE SHOEGAZE posted:

Then all you've done is taken your lib folder and stuck it in models for some reason. There's really no reason to have anything in your app folder that isn't a direct Rails subclass. I mean, you're probably not causing yourself any huge problems, but there's no reason all of that stuff couldn't be in lib, in the same directory structure you have now. The only exception being that your models would have to be in models.

You don't make a lib jobs/workers/services/decorators/etc, that would be ridiculous. You treat it like it's just full of unpacked gems. There's no issue with namespace conflict unless I'm duplicating functionality or giving things really weird names.

I'm not making any problems for myself. My codebase is incredibly easy to reason about, and is rather well factored. To me, the whole concept of putting your entire application under the library directory seems more than a bit silly. My lib directory is used for exactly that: external dependencies that I likely will extract into gems at some point. The meat of my application is not an external library. The only time putting your entire application under lib makes sense is if you plan to export the whole thing as a self-contained gem - which, if you're putting your entire DAO layer somewhere outside of what you're extracting to a gem, namely /app, isn't possible, since a gem that depends on the codebase that requires the gem in its Gemfile doesn't make any sense. So I'd argue that putting all your business logic in /lib makes even less sense, as now you have dependencies that cross a barrier in two directions (lib depends on app, app depends on lib) instead of one direction (app depends on lib only) and dependencies should only cross a barrier in one direction. And truly, don't try to kid yourself into thinking that extracting all your services and business logic out of /lib into a gem would make that gem make sense in any context other than when mixed with your specific app's set of ActiveRecords (regardless of the fact that they're DIed into the gem) because it wouldn't. There's still effectively a two way dependency in that type of situation. One makes no sense without the other. So treat it as part of your application, not an external library, or treat your *entire* domain model as an external library - DAOs and all.

Thalagyrt fucked around with this message at 02:27 on Jan 30, 2015

DONT THREAD ON ME
Oct 1, 2002

by Nyc_Tattoo
Floss Finder

Thalagyrt posted:

My codebase is incredibly easy to reason about, and is rather well factored.
:rolleyes: :rolleyes: :rolleyes:

quote:

To me, the whole concept of putting your entire application under the library directory seems more than a bit silly.

By putting most of your code in the app/models folder, you're at least strongly implying that those classes depend on the ruby on rails framework. It's extremely likely that that dependency isn't actually necessary, but nevertheless, all of your code is now tightly coupled to the rails framework, and in fact tightly coupled to the rest of your application.

By pulling that code out of your rails application, and sticking it in the library, you're at least implying that your code should be easily composable throughout the rest of your application. Not only that, but you're setting yourself up to make it easy to break that code out into external services, or to reuse it in other applications.

Basically, the app folder is for implementation code, which means you're not writing interface code, which means you're probably wasting a lot of time.

Or you're doing it right but sticking everything in models because

Thalagyrt posted:

My codebase is incredibly easy to reason about, and is rather well factored.

DONT THREAD ON ME
Oct 1, 2002

by Nyc_Tattoo
Floss Finder
I do agree that writing gems is really a total waste of time but in our case it makes sense because things have just gone too far in the other direction.

Gems make sense if you can truly black box them but yeah, I'm not that good.

Thalagyrt
Aug 10, 2006

MALE SHOEGAZE posted:

:rolleyes: :rolleyes: :rolleyes:


By putting most of your code in the app/models folder, you're at least strongly implying that those classes depend on the ruby on rails framework. It's extremely likely that that dependency isn't actually necessary, but nevertheless, all of your code is now tightly coupled to the rails framework, and in fact tightly coupled to the rest of your application.

By pulling that code out of your rails application, and sticking it in the library, you're at least implying that your code should be easily composable throughout the rest of your application. Not only that, but you're setting yourself up to make it easy to break that code out into external services, or to reuse it in other applications.

Basically, the app folder is for implementation code, which means you're not writing interface code, which means you're probably wasting a lot of time.

Or you're doing it right but sticking everything in models because

The only bits coupled to Rails are the ActiveRecords themselves, which I pretty much entirely treat as DAOs. My entire argument is that extracting the *rest* as a library makes absolutely no sense, as that library does not function without the DAOs. You can DI them all you want, and I do, but even then you're still creating in effect a circular dependency where one part conceptually does not make any sense without the other. May as well treat them as one cohesive unit.

DONT THREAD ON ME
Oct 1, 2002

by Nyc_Tattoo
Floss Finder

Thalagyrt posted:

The only bits coupled to Rails are the ActiveRecords themselves, which I pretty much entirely treat as DAOs. My entire argument is that extracting the *rest* as a library makes absolutely no sense, as that library does not function without the DAOs. You can DI them all you want, and I do, but even then you're still creating in effect a circular dependency where one part conceptually does not make any sense without the other. May as well treat them as one cohesive unit.

I really think we're effectively arguing for the same thing, we just put them in different folders.

Rails lib should probably be called something else, and it should be autoloaded by default.

Thalagyrt
Aug 10, 2006

MALE SHOEGAZE posted:

I do agree that writing gems is really a total waste of time but in our case it makes sense because things have just gone too far in the other direction.

Gems make sense if you can truly black box them but yeah, I'm not that good.

I'd wager none of us here in the thread are Robert Martin levels of good. :p

Edit:

MALE SHOEGAZE posted:

I really think we're effectively arguing for the same thing, we just put them in different folders.

Yeah, I think so too!

DONT THREAD ON ME
Oct 1, 2002

by Nyc_Tattoo
Floss Finder
Honestly, our rails app has been around since rails 1.0 and I dont know where the rails app ends and our weird frankenstein begins.

KoRMaK
Jul 31, 2012



HAhahaahahaha

I use my models directory for stuff that is ACtiveREcord only (backed by a table).

I need to figure out a synonym for other stuff that is models but isn't ActiveREcord backed.

Thalagyrt
Aug 10, 2006

MALE SHOEGAZE posted:

Honestly, our rails app has been around since rails 1.0 and I dont know where the rails app ends and our weird frankenstein begins.

Heh. I say you aren't truly developer until you've been mired by some huge legacy application that's been around for years and is a hodgepodge of the various best practices throughout the years, always shifting toward the new hotness but never refactoring the old and busted out. In the Rails world, this means some fat controllers, some fat models, maybe 30% of your business logic is implemented in services and the rest in huge models... Oh and sprinkle some concerns in for good measure. Maybe one truly talented OO guy stormed through at some point and extracted 20% of your codebase into a gem, so you had that one really well factored piece of business logic where everything's beautifully DIed and reusable, but people have just hacked functionality into the gem over the years and now it blows up if required anywhere other than your Rails app, and oh god why.

Thalagyrt
Aug 10, 2006

KoRMaK posted:

HAhahaahahaha

I use my models directory for stuff that is ACtiveREcord only (backed by a table).

I need to figure out a synonym for other stuff that is models but isn't ActiveREcord backed.

Models is the right word. Model in software engineering doesn't mean ActiveRecord or DAO or some specific type of object, it means domain model. However, in the Rails world, for some reason "model" has become bastardized to mean ActiveRecord, which leads to a lot of confusion. If you take a step back to pretty much anything other than Rails (personally, I did C and Java back in the 90s, .NET in the early 2000s, Python late 2000s, and switched to Ruby in 2012) you'll find that the domain model is a concept that represents the entirety of your application. Losing the conflation that model == ActiveRecord is something that I think would help a lot of Rails guys out.

KoRMaK
Jul 31, 2012



I put stuff into the lib folder under a module that adds new functionality and then include that in the class where it's needed.

Like one thing I have involves controllers and models, so I put it in a module. The code for the controllers and models are separated into their own modules inside the larger. Then I do include MyModule::Controller and include MyModule::Model.

If the code starts getting to big I'll split them out into their own files.

Thalagyrt posted:

Heh. I say you aren't truly developer until you've been mired by some huge legacy application that's been around for years and is a hodgepodge of the various best practices throughout the years, always shifting toward the new hotness but never refactoring the old and busted out. In the Rails world, this means some fat controllers, some fat models, maybe 30% of your business logic is implemented in services and the rest in huge models... Oh and sprinkle some concerns in for good measure. Maybe one truly talented OO guy stormed through at some point and extracted 20% of your codebase into a gem, so you had that one really well factored piece of business logic where everything's beautifully DIed and reusable, but people have just hacked functionality into the gem over the years and now it blows up if required anywhere other than your Rails app, and oh god why.
Yea, things are always moving. I like to keep to to whatever the app started as, so at least that app has some sort of consistency and convention.

Thalagyrt posted:

Models is the right word. Model in software engineering doesn't mean ActiveRecord or DAO or some specific type of object, it means domain model. However, in the Rails world, for some reason "model" has become bastardized to mean ActiveRecord, which leads to a lot of confusion. If you take a step back to pretty much anything other than Rails (personally, I did C and Java back in the 90s, .NET in the early 2000s, Python late 2000s, and switched to Ruby in 2012) you'll find that the domain model is a concept that represents the entirety of your application. Losing the conflation that model == ActiveRecord is something that I think would help a lot of Rails guys out.
Oh I already know this, but Rails has such a specific use for them that when I'm in the models directory I expect every one of those things to have a table. C++/C#/AS3/Fortran 77

DONT THREAD ON ME
Oct 1, 2002

by Nyc_Tattoo
Floss Finder
It's not so much that I think model == active record, it's that rails enforces that relationship strongly enough that I'd rather just opt out of it.

I'd have no issue with you putting your models in application_name/models :)

Thalagyrt
Aug 10, 2006

I kinda just said screw it to Rails conventions a long time ago because I think Rails conventions start to fall apart really quick when you start writing any application that's more complex than a blog. The whole concept of different directories that aren't separate namespaces annoys me to begin with. I'd honestly argue that Rails's directory layout is detrimental. The controllers, models, etc directories should IMO each be actual modules - i.e. YourApp::Controllers, YourApp::Models, etc, not a bunch of directories whose contents are all autoloaded into the same module. Views probably belongs somewhere else. If we got rid of this legacy cruft and fear of namespacing, I think Rails would be a lot better off. I'm pretty excited about Lotus because it actually embraces OO proper and lets you structure your code in modules however you see fit. http://lotusrb.org/

Edit: If I had my way, I'd probably have a MyApp::HTTP module that contains controllers and related frontend classes (datatables, helpers, etc) and MyApp::Application module that contains the actual application, with a strict enforcement that Frontend can depend on Application but not the other way around. I might actually do that, thinking about it more. It's already effectively how I've structured my code in the directory structures anyway.

Thalagyrt fucked around with this message at 02:57 on Jan 30, 2015

DONT THREAD ON ME
Oct 1, 2002

by Nyc_Tattoo
Floss Finder
Yeah I agree completely. The Rails directory structure was my number 1 pet peeve until I got good enough at ruby/the rails magic to just ignore all of it.

Basically everyone here wants to get away from rails at this point. We're probably moving towards SOA because I don't think anyone can figure out a better way to slowly refactor everything in a sane way. Unfortunately, we're never going to get twitter style capital so we can't just rewrite everything, but we're also successful enough that we do actually need to plan for the future.

Thalagyrt
Aug 10, 2006

MALE SHOEGAZE posted:

Yeah I agree completely. The Rails directory structure was my number 1 pet peeve until I got good enough at ruby/the rails magic to just ignore all of it.

Basically everyone here wants to get away from rails at this point. We're probably moving towards SOA because I don't think anyone can figure out a better way to slowly refactor everything in a sane way. Unfortunately, we're never going to get twitter style capital so we can't just rewrite everything, but we're also successful enough that we do actually need to plan for the future.

When I first started designing the vNuc portal (because let's face it, all the billing software in the hosting world blows. Actually, considering cPanel, Plesk, SolusVM, etc, I'll just say all the software in the hosting world blows) I contemplated a strict SOA policy. Actually started implementing it that way. Then I realized I'm probably never ever going to even come close to the scale, both in terms of code complexity (it's a hosting control panel, it handles credit cards, VPSes, support tickets, Exchange accounts, their lifecycles, and that's about it) as well as scaling (again, hosting control panel, I'm never going to have even thousands of simultaneous users) to make it worth the added complexity. Would have been super cool and super fun I'm sure, but in this business the biggest my scaling needs are ever realistically going to get is probably adding a second app server. I'm likely never even going to be 1% of the size of say AWS, so I'm not going to face those problems.

Peristalsis
Apr 5, 2004
Move along.
Thanks for the responses. I'm somewhere between glad and annoyed that there's not a standard answer - glad because it means I'm not just an idiot for not knowing it, and annoyed because now I have to figure out what convention is going to be "right" for this particular situation. I guess I'll try a couple of things, and hope that some layout gives us both code and tests that work, and I'll decree that that's the canonical way to do it from now on. At least my overall approach (a folder of related classes that share a mixin module) didn't elicit rage and scorn. And one of the reasons I wanted to put this in a subfolder of models was to underscore the fact that it's not ActiveRecord, just a collection of classes we use. Honestly, I kind of like the idea of giving it its own directory just under app, or maybe app/misc_classes/imports, but that would undermine the other reason I put this in app/models - namely that we already have a similar, parallel structure for another one of our sets of classes, and I don't really want to refactor the other guy's code out into my new arrangement.

I think I gave the impression that the actual code doesn't work - that's not the case. The module seems to load fine and the class works with it so far - maybe 25% of the functionality is in it right now. The problem is that the unit test won't work. I'm at home so I don't have the error message handy, but I think it was some problem with recognizing "Imports" in Imports::MyImportClass.

Oh, and this app is in Rails 3.something, though we did upgrade to Ruby 2.1.1, I think.

Pollyanna
Mar 5, 2005

Milk's on them.


Actually, that reminds me - a lot of the Rails projects I've made are just straight "models -> database schema -> CRUD ops" setups, with rarely anything much more complicated than Users and Posts. What cases are those where you would actually use non-AR backed models, modules and extensions, and other complicated stuff?

Thalagyrt
Aug 10, 2006

Pollyanna posted:

Actually, that reminds me - a lot of the Rails projects I've made are just straight "models -> database schema -> CRUD ops" setups, with rarely anything much more complicated than Users and Posts. What cases are those where you would actually use non-AR backed models, modules and extensions, and other complicated stuff?

In my case VPS management - should a database access object with the responsibility of storing server details really know about how to create a Xen domain, or install Linux on a server? Billing stuff - it's really not an account DAO's job to know how to talk to the bank, is it? I shouldn't have to talk to the bank every time I want to test something in the application. Same deal goes with Exchange - why should a DAO know how to talk to Active Directory? If you put all of this stuff in a database model you're going to create a nightmare for yourself with the SRP violation and code complexity. These objects that have 10+ responsibilities become hard to test effectively. It's all about decoupling components, then composing them back together to get the needed work done. By decoupling your components, you make it easier to replace components, which makes it easier to test components by replacing their dependencies with mocks that can reliably simulate failure conditions. Also, by decoupling your components you make it easier to extend behavior.

A good example of this was me wanting to give a credit for the remaining time on a server to a customer when they cancel the server. I introduced a CreditingServerTerminator which wraps a ServerTerminator (which can be injected on initialize, but comes with a sane default) and simply adds the credit when the ServerTerminator does this thing. The CreditingServerTerminator is used in the client area TerminationsController's create action. If all of that lived in the Server model itself I'd have two methods that terminate a server, which I suppose could make sense, but again, consider how much responsibility the Server would have in this case. It'd easily be a 4000 line class. This also makes it super easy to test the behavior of crediting on termination. I can mock out the ServerTerminator to just succeed, and assert that the account's ledger gets a credit message sent. No database access necessary to ensure the behavior works.

For a bit more clarity, the ServerTerminator's job is to actually destroy the Xen domain with our backend system, and then update the Server DAO's state to indicate that it's actually terminated. It also kicks off a few background jobs to notify the admins and account users that the server was terminated, and places an entry in the account's audit log. Basically, all the responsibility of what it means to terminate a server goes in there. It's used when a user terminates a server, an admin terminates one (say in the case of abuse) or when automation terminates one, and it's easy to compose behavior on top of as seen above.

The audit logging is another good example of when to inject dependencies and how decoupling into smaller classes makes things easier. Rather than having it simply be Account#log_event(details), I have an EventLogger in the Accounts module, which can be set up with various params. Every service object that uses an EventLogger comes with sane defaults for the default use case, but you can inject an EventLogger as well - so any time one of these services is used in a controller, I inject an EventLogger that's been pre-loaded with the user and request details so that the acting user + IP address gets tagged on any logged audit events.

Thalagyrt fucked around with this message at 19:09 on Jan 30, 2015

Peristalsis
Apr 5, 2004
Move along.

Pollyanna posted:

Actually, that reminds me - a lot of the Rails projects I've made are just straight "models -> database schema -> CRUD ops" setups, with rarely anything much more complicated than Users and Posts. What cases are those where you would actually use non-AR backed models, modules and extensions, and other complicated stuff?

In my case, we have a system that tracks file metadata, and we want to be able to import spreadsheets of metadata for new files. We need to look at the metadata file itself, loop over each row in it, and extract metadata from each row. We also need logic to track down the files on a network drive to which each row's metadata applies. The logic for parsing this metadata file is cumbersome and irrelevant to the rest of the system (at least once the files are located and described). I don't need to record anything about the parser itself to the database. Any information about an individual run of the parser could just be written to a log file, or, at worst, stored in a "parsing_results" table.*

I guess I think of it like this: if I'm writing a substantial program to do something - one that just executes a task rather than being of enough intrinsic interest to put info about it in the database - then I consider making it a non-AR model (or "misc_class" or whatever). The parser above is definitely an object (model?) in the system, but nothing about it is of interest to our users, and nothing about it changes from one use to the next, except the results and possibly the configuration. Changing our importer to do a different kind of work is more aptly suited to having a different (sub-)class, than it is to having a different row about it in a database.

* This could change if the parser gets more complicated, and needs to be tweaked and configured for each run. At that point, one could argue that saving the configuration and results of a parsing run would at least make for a decent audit table. Even then, though, it might make more sense to have a "parser_details" table, than to pretend that the parser is some sort of fundamental data object that relates to any other data object in the app.


Edit: We also have a process that runs overnight to validate that none of the data files that our system tracks have gone missing or become corrupted. I made this a class method of our data file AR class, but it's pretty large, and may grow to check things besides data files. I probably should have made it its own, non-AR class. There aren't different instances of the validator that have different attributes that we need or want to record in a database, it's just a program that runs periodically, and sends an email and writes a note to a log if it finds any problems.

Peristalsis fucked around with this message at 21:44 on Jan 30, 2015

wins32767
Mar 16, 2007

I'm working my way through a rails tutorial and it's using this syntax in test_helper.rb
code:
 def is_logged_in?
    !session[:user_id].nil?
 end
What's the leading ! on session mean?

MasterSlowPoke
Oct 9, 2005

Our courage will pull us through
!(session[:user_id].nil?)

if session is nil,
is_logged_in? => !(true) => false

if session is set
is_logged_in? => !(false) => true

I'd probably have session[:user_id].present?.

Adbot
ADBOT LOVES YOU

wins32767
Mar 16, 2007

Thanks, I was looking for some deeper meaning. Should have just gone with the obvious option.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply