2

Summary

I want to build a rails monolith with Domain Objects that are easily portable as the app grows. I also want to start in the app/ directory instead of starting with a Rails Engine because engines seem to be a lot of overhead for a feature that may not endure, or the feature might get moved to an HTTP endpoint soon.

Progress

1. Scaffold

Running rails g scaffold post generates the following structure in app/ (in addition to other files)

app/
  controllers/
    posts_controller.rb
  models/
    post.rb
  views
    posts/

2. Swap Directory order

Is it possible to config load paths in order to invert directories so that the following would work;

app/
  post/
    controllers/
      post_controller.rb
     models/
      post.rb
    views/
      index, show, etc...

(I want to have all my MVC for Post in the post/ directory in preparation to move post/:

  • down to lib/
  • to a gem
  • into an engine
  • to my micro service
  • or maybe even to the trash because it's a terrible feature

3. Uninitialized constant PostsController

Currently, simply inverting files provides;

uninitialized constant PostsController even with tinkering with variations of config.autoload_paths += %W( #{config.root}/app/post/* ) in application.rb. @bbozo's suggestion below worked by explicitly including files rather than use '*' like so;

class Application < Rails::Application
  config.autoload_paths += %W( #{config.root}/app/ )
  config.autoload_paths += %W( #{config.root}/app/post/controllers )
  config.autoload_paths += %W( #{config.root}/app/post/models )

4. ActionView::MissingTemplate

The next issue I'm having is ActionView::MissingTemplate

ActionView::MissingTemplate (Missing template posts/index, application/index with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:erb, :builder, :raw, :ruby, :coffee, :jbuilder]}. Searched in:
  * "/app/views"
):
  .bundle/gems/actionview-4.2.0/lib/action_view/path_set.rb:46:in `find'
  .bundle/gems/actionview-4.2.0/lib/action_view/lookup_context.rb:121:in `find'
  .bundle/gems/actionview-4.2.0/lib/action_view/renderer/abstract_renderer.rb:18:in `find_template'
  .bundle/gems/actionview-4.2.0/lib/action_view/renderer/template_renderer.rb:40:in `determine_template'
  .bundle/gems/actionview-4.2.0/lib/action_view/renderer/template_renderer.rb:8:in `render'
  .bundle/gems/actionview-4.2.0/lib/action_view/renderer/renderer.rb:42:in `render_template'
  .bundle/gems/actionview-4.2.0/lib/action_view/renderer/renderer.rb:23:in `render'
  .bundle/gems/actionview-4.2.0/lib/action_view/rendering.rb:100:in `_render_template'
  .bundle/gems/actionpack-4.2.0/lib/action_controller/metal/streaming.rb:217:in `_render_temp
  [cut out the rest]

From the rails guides I read about the file: flag for absolutes so I got to the Index when I added the following:

  def index
    @posts = Post.all
    render file: 'app/post/views/posts/index'
  end

The Index rendered successfully but there was lots of re-configuration I had to do for this single endpoint. I'm trying to determine if I can manage this in the guts and it just works for Post#show, Post#edit, Post#create and other Domain Objects such as Likes

5. Current status

Will I need to reconfigure application.rb and render file: for every endpoint?

Community
  • 1
  • 1
DaveWoodall.com
  • 727
  • 6
  • 22

1 Answers1

1

Solving Rails load paths

This should work afaik:

config.autoload_paths += %W( #{config.root}/app/post/models )
config.autoload_paths += %W( #{config.root}/app/post/controllers )
config.autoload_paths += %W( #{config.root}/app/post/views )

But consider that maybe you're trying to reimplement a Rails engine, so I'd suggest investigating in that direction,

https://blog.pivotal.io/labs/labs/migrating-from-a-single-rails-app-to-a-suite-of-rails-engines

after some experience with breaking up rails apps into component engines, it seems to me from what you're trying to do that that's the direction I'd take

Solving view lookup paths

See How can I add a view path to Rails's partial rendering lookup?,

you could probably do something like this

class BasePostsController < ApplicationController
  # I assume you have a base controller for Post from which other
  # post controllers inherit from, this would be a good fit

  prepend_view_path 'app/post/views'
end

Knowing when to go for plan B :P

Plan B would be to give up on folder structure with app/post/controller and go for folder structure 'app/controller/post' :)

No load paths updates necessary, in the models:

  1. create folder app/models/post
  2. create empty module in app/models/post.rb saying just module Post; end
  3. every model you now put in 'app/models/post' folder can now be namespaced to Post::MyModel and loaded correctly by Rails

For controllers add a namespaced route, see this answer for a possible solution Rails Controller Namespace

bbozo
  • 7,075
  • 3
  • 30
  • 56
  • Thank you @bbozo! That got me past the controller issue and I updated my question. But do you think it's possible to work this way in _vanilla Rails_ with just the 'app/' directory while my feature is still in `MVP` stage? If it works then I could see an Engine as the next step but that feels like too much overhead for the current season of this feature that in all likelihood it's going to get yanked. To put it more succinctly, Can I think in _Domain Objects_ without an Engine? – DaveWoodall.com Jan 03 '16 at 14:31
  • 1
    Of course you can :) My approach is to simply namespace everything and then if I want to bust or extract that feature there's not many pieces to catch around. As for the view error, try adding `posts/views` to rails views lookup path: https://stackoverflow.com/questions/6081603/how-can-i-add-a-view-path-to-railss-partial-rendering-lookup – bbozo Jan 03 '16 at 15:44
  • 1
    This is a great answer. You can make it even more general by adding a dynamic directory lookup - something like: config.autoload_paths += Dir["#{Rails.root}/app/post/*"] – Noah Gibbs Jan 03 '16 at 16:20
  • Hmm I'm not able to get `preppend_view_path` to work. Notice that I commented out *line 6* in PostController. If I uncomment that, it work tho. Here's a screenshot https://www.dropbox.com/s/aned7jke7t05p0z/Screenshot%202016-01-03%2012.54.12.png?dl=0 – DaveWoodall.com Jan 03 '16 at 19:54
  • 1
    @wwwoodall, try playing with the exact path, maybe try using the code from my answer that uses the class method instead of the `before_filter` approach from the SO question I linked you. Also, it's good to recognize when your simple and low cost solution is becoming more expensive then the thing you want to avoid :D But there's a "cheaper" plan B, see my updated answer soon-ish – bbozo Jan 04 '16 at 11:37
  • I found another approach but it's not 100% yet. Please take a look at this screenshot to see that, while it works - Rails required me to nest TWO times `/post/post/`. I'm wondering how can I just move it up a directory? Also, with this approach, I did not need to configure load paths at all. https://www.dropbox.com/s/zdtuk81oprzl5mw/flow.png?dl=0 – DaveWoodall.com Jan 25 '16 at 22:22
  • Here comes Zen moment, it's not 2 posts, it's 1 domain namespace and 1 rest resource named post :D – bbozo Jan 26 '16 at 09:02