0

I have a Boilerplate model which has two descending models:

  • BoilerplateOriginal
  • BoilerplateCopy

While BoilerplateOriginals is sort of a template that admins create, BoilerplateCopys are copies of the originals that are free to edit by everyone, and they also have some more associated objects (e.g. BoilerplateCopy belongs_to: BoilerplateOriginal, BoilerplateCopy belongs_to: Project or BoilerplateCopy has_many: Findings, all of which BoilerplateOriginal doesn't).

So in my opinion, it makes sense to maintain two different model classes that share the same basic functionalities.

Because they also look quite the same, I want to use the same views for them. But under the hood, they are treated a bit different, so I also have two different controllers inheriting from a base controller.

Everything seems to work fine, except that form_for boilerplate, as: resource_instance_name raises an exception undefined methodboilerplates_path', but only when called asnewaction, not when called asedit` action.

Here's what I have done so far to make it work (and everything else seems to work fine):

# config/routes.rb
resources :boilerplate_originals

# app/models/boilerplate.rb
class Boilerplate < ActiveRecord::Base
  def to_partial_path
    'boilerplates/boilerplate'
  end
end

# app/models/boilerplate_original.rb
class BoilerplateOriginal < Boilerplate
end

# app/controllers/boilerplates_controller.rb
class BoilerplatesController < InheritedResources::Base
  load_and_authorize_resource

  private

  def boilerplate_params
    params.require(:boilerplate).permit(:title)
  end
end

# app/controllers/boilerplate_originals_controller.rb
class BoilerplateOriginalsController < BoilerplatesController
  defaults instance_name: 'boilerplate'
end

# app/views/boilerplates/_form.html.slim
= form_for boilerplate, as: resource_instance_name
  # ...

As pointed out, new/create works flawlessly, but edit doesn't. And I'm using InheritedResources, by the way.

Joshua Muheim
  • 12,617
  • 9
  • 76
  • 152
  • show your `routes.rb` file. – Arup Rakshit Aug 14 '15 at 11:41
  • what is `resource_instance_name` ? – Arup Rakshit Aug 14 '15 at 11:48
  • `resource_instance_name` is from `InheritedResources`, it's the name of the current resource (e.g. `boilerplate`). By passing it as the `:as` option, it builds a form with input names like `boilerplate_title` (and not `boilerplate_original_title`), and thus can be used for any boilerplate model. – Joshua Muheim Aug 14 '15 at 12:15
  • In fact, as I want the form to be built always as `boilerplate`, I can skip `resource_instance_name ` and simply do `as: :boilerplate`. ;-) – Joshua Muheim Aug 14 '15 at 12:16

2 Answers2

0

Rails is dong it correctly, you're slightly doing it wrong

the problem is:

resources :boilerplate_originals

that will just generate routes for especially boilerplate_originals. when using form_helpers of rails rails will lookup for a route based on the models class which is in this case "boilerplate_copy". that means its looking for a edit_boilerplate_copy_path (which isnt generated by rails)

You said that BoilerplateCopy and BoilerplateOriginal are pretty much looking the same (i guess you just copy a model, there are gems out for doing that for you...)

If you go correct STI it "should" be

class Boilerplate; end
class BoilerplateCopy < Boilerplate; end
class BoilerplateOriginal < Boilerplate; end

for that you need only a route for Boilerplate

resources :boilerplate

and of course a boilerplate_controller

everything is handled as a boilerplate and the form_helper will look up for a new_boilerplate_path, which exists, no matter if its a copy or a original.

Tim Kretschmer
  • 2,272
  • 1
  • 22
  • 35
  • just keep track on how your models changed. https://github.com/airblade/paper_trail or maybe that one https://github.com/amoeba-rb/amoeba – Tim Kretschmer Aug 14 '15 at 11:59
  • so, this is not what I need; I added some more introductory info to the thread. – Joshua Muheim Aug 14 '15 at 12:06
  • Thanks for your reply, huan so. I definitely don't want a single `resources :boilerplates`, as I want two different controllers act upon them (e.g. different allowed strong parameters). I just don't understand why the `new` action tries to create a path acting upon `Boilerplate` and not upon `BoilerplateOriginal`. – Joshua Muheim Aug 14 '15 at 12:08
  • if you need 2 different controller, then you need 2 different routes, for the edit/update action. resources :boilerplate_copy, only[:new, :create, :update] – Tim Kretschmer Aug 14 '15 at 12:15
  • Yes I will need them, this is true. Thanks for helping me. It's working now as expected, see my answer above. – Joshua Muheim Aug 14 '15 at 12:17
  • yes you can do it with the override of the defaultinstanceclass but thats not really quality code. i recommend to share 1route/1controller or if you really need the control by 2 controllers let them use different routes (you still can share the views) – Tim Kretschmer Aug 14 '15 at 12:20
0

I found the problem.

# app/controllers/boilerplate_originals_controller.rb
class BoilerplateOriginalsController < BoilerplatesController
  defaults instance_name: 'boilerplate' # This is wrong!
  defaults instance_name: 'boilerplate', resource_class: BoilerplateOriginal # This is right!
end

Now a BoilerplateOriginal object is passed to the new action, and not a Boilerplate object.

Joshua Muheim
  • 12,617
  • 9
  • 76
  • 152