0

I've got STI like this:

class Post
end

class Post::Confirmed < Post
end

class Post::Draft < Post
  def confirm!
    becomes Post::Confirmed
  end
end

...# somewhere in controller
# POST /posts/1/confirm
# POST /posts/1/confirm.json
def confirm
  @post = Post::Draft.first
  @post = @post.confirm!    # this is the only way I can reload @post with Post::Confrmed 
end

Is it somehow possible to make:

@post.confirm! # I want this @post(Post::Draft) to become Post::Confirmed without reassigning

Or is it just nor RoR way?

Thanks in advance!

IlyaDoroshin
  • 4,659
  • 4
  • 22
  • 26
  • 2
    Are you sure that STI is appropriate in this case? Wouldn't a simple status flag and some scopes make more sense? – mu is too short Oct 02 '14 at 21:21
  • I've though about that, but the scope is a little bigger then in this example: all subclasses works as namespaces for given classes, so drafts have their own methods and confirmed posts — their own. I guess, I can be wrong. But still, STI looks perfect for that. – IlyaDoroshin Oct 02 '14 at 21:21
  • @muistooshort imagine, that you've got shopping cart. It's structurally the same as the Order: it's got items, total price etc. But the methods for each of class are different. – IlyaDoroshin Oct 02 '14 at 21:25
  • 2
    Switching an object's type is probably a really bad idea and ActiveRecord makes it difficult on purpose. Just flip a flag. Your shopping cart example makes more sense, certain kinds of line-items would have different properties than others, but transmogrifying one into another is really bizarre. – tadman Oct 02 '14 at 21:35
  • @tadman I've just wanted to know if there's any better way to deal with that instead of reassigning the variable each time. But if it's not possible — whatever, I an deal with that, i guess. – IlyaDoroshin Oct 02 '14 at 21:37

1 Answers1

0

The pattern I've found that works best here is having a datetime type field that records when the record was flagged.

For example:

def confirm!
  self.confirmed_at = DateTime.now
  self.save!
end

Then you can tell when something was confirmed. This comes in especially handy for when you have a situation where something will be flagged but isn't yet, such as setting a publishing date in the future.

Although it might seem a little annoying to not have your STI bag of tricks available, STI is not always the appropriate tool. Generally STI is to differentiate between similar but different models that have a lot of commonality or are used in a common context. It's not supposed to be used to handle different states of a singular model.

What you want in that case is a state-machine type pattern.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • actually, true, but state-machine gems are awful: statesman is too complicated to use, and state_machine is not maintained for a year.. all I wanted to use "bang" method, that will change the instance (as all bang-methods do). But if it's not possible, I'll stick with the reassigning the variable. For me this way is more clear and robust. Thanks for your answer though) – IlyaDoroshin Oct 02 '14 at 22:18
  • I've just found another gem, that looks promising actually: aasm – IlyaDoroshin Oct 02 '14 at 22:24
  • 1
    The reason state-machine type gems are not always well maintained is because implementing that behaviour yourself is trivial. Give it a shot. – tadman Oct 05 '14 at 19:25