-
-
Notifications
You must be signed in to change notification settings - Fork 542
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Autogeneration of MyApp::Controller and MyApp::View is magic #75
Comments
@janko-m TL;DR Lotus offers tools to avoid monolithic applications and to test components in isolation. SlicesIt allows to run multiple applications in the same Ruby process, to encourage isolation of macro functionalities. Imagine an user facing application, a JSON API and an administration backend. Framework duplicationBecause of this specific need, you want to keep your configuration separated. For instance, the default response type of your JSON api is To avoid conflicts between configurations, Lotus has a technique called "framework duplication". It allows to (thread) safely duplicate the components and the configurations of each framework, so they can coexist in the same Ruby process without friction. That's why if you have Testing in isolationFor convenience sake, we configure the behavior of the application with # Tedious
router = Lotus::Router.new do
# ...
end
Lotus::Controller.configure do
default_format :json
end
Lotus::View.configure do
root __root__ + '/app/templates'
end
# This is new ;)
Lotus::Assets.configure do
prefix '/myapp'
end vs # Convenient
module Bookshelf
class Application < Lotus::Application
configure do
default_format :json
templates 'app/templates'
routes do
# ...
end
end
end
end Now, forget for a second the framework duplication. Imagine that you still include the old Lotus helps you with this. When you do In your testing suite you can do: # spec/spec_helper.rb
RSpec.configure do |config|
# You load the application environment once
config.before :suite do
Bookshelf::Application.load!
end
end # spec/controllers/home/index_spec.rb
require 'spec/spec_helper'
require 'path/to/home/index'
describe Bookshelf::Controllers::Home::Index do
# ...
end If you run that single spec with ConclusionI hope all the explanations above are exhaustive on why things are designed like that in Lotus. Closing this. Feel free to ask other questions. /cc @jeremyf |
Thank you very much for the detailed explanation. I really appreciate that you care so much that everybody who wants to get involved understands the main concepts. I will try to tag on to your main points. Framework duplication
Yes, this is what Testing in isolation
That's a bummer, because this was for me one of main beauties of Lotus, that I configure each framework separately, that I really liked the explicitness, and it isn't tedious at all. I have the following structure for controllers (generalize it to views and others): # my_app.rb
require "lotus"
module MyApp
class Application < Lotus::Application
load_paths << ["my_app/controllers"]
routes do
# ...
end
end
end # my_app/controller.rb
require "lotus/controller"
module MyApp
# this line would be if `Lotus::Application` wouldn't create it
Controller = Lotus::Controller.duplicate(self) do
# configuration
end
end # my_app/controllers/home.rb
require "my_app/controller"
module MyApp
module Controllers
module Home
include MyApp::Controller
end
end
end
You see, I configured the controller outside of
Yes, I agree, at the end it is testing in isolation. But it still strongly doesn't feel right that you need to require |
@janko-m You're welcome. Without framework duplication there will be race conditions. # json_api.rb
Lotus::Controller.configure do
default_format :json
end
# web.rb
Lotus::Controller.configure do
default_format :html
end
# At this point Lotus::Controller.configuration.default_format is :html
# When you load the code for both the applications, and then inject the configuration, they will both see `:html`.
module JsonApi
class MyAction
include Lotus::Action
end
end
module Web
class MyAction
include Lotus::Action
end
end
JsonApi::MyAction.configuration.default_format # => :html WRONG
Web::MyAction.configuration.default_format # => :html OK Speaking of convenience, you may not find tedious to configure all the frameworks separately, but they are a lot of people that are finding the current usage of Lotus a bit verbose. So why not streamline what can be easily achieved? In conclusion, if you find those conventions not adhering to your point of view, Lotus is also a DIY framework. You can always pick the separate components and make them to work together. Don't get me wrong, I don't want to be rude here, just communicating the power of having reusable gems. 😉 |
Yes, that is a race condition, but it's an obvious one, because you're configuring a About convenience, doesn't it feel wrong (tedious) for you to have to add each configuration from each framework to I just wanted to show you the advantages of my point of view about this problem, because I'm so sure it's the right way, but I can stop now :). Thank you for listening me out; you're right, maybe I can just use the frameworks separately. |
How to solve this problem then? 😉
Good catch, it's because the configuration API for the single frameworks is still under high development. So I'm slowly porting them. I have another idea in mind to avoid the porting at all: module Bookshelf
class Application < ::Lotus::Application
configure do
controller.default_format :json
end
end
end That syntax will talk to |
From the outside looking in (on this conversation) @jodosha is extending an olive branch to those requesting a less verbose means of configuring Lotus. Its a higher level of abstraction/obfuscation; but is something Rails "hostages" may be needing. And @janko-m you are advocating for a more explicit and verbose process for configuration. Perhaps you might be willing to craft a guide/application that uses your preferred process. This is something that would fit great in Lotus::Docs. I would love to see guides/examples of both. |
I'm not sure I understand what you mean, but isn't this the solution? module JsonApi
Controller = Lotus::Controller.duplicate(self) do
default_format :json
end
end module Web
Controller = Lotus::Controller.duplicate(self) do
default_format :html
end
end
This solution looks much better 👍 @jeremyf Good advice, I will definitely submit an example application, because I came to this by working on a real application. |
@janko-m But that creates the |
No, I do like it :), I just don't think that |
This thread is gold and should be linked somewhere :) |
I would like that autogeneration of
MyApp::Controller
is removed. This feature suprised me when I found out about it. What happened in my development was that I wanted to make aMyApp::Controller
, and then I got warnings of already initialized constant, and some really weird errors (because I tried to makeMyApp::Controller
a class). There isBut it didn't work for me, because I didn't
require "my_app/controller"
before the app loading (I did it in individual controllers).Another downside of this approach is that now controllers (and views) which "inherit" from
MyApp::Controller
(MyApp::View
) can't be tested in isolation, because they requireMyApp::Application
to be loaded (so that it generates the "application" controller). I really liked Lotus' idea of isolated unit-testing, that I can just require the components I need.I know there is the problem of "inheriting" some configuration from
Lotus::Application#configuration
, like#handle_exceptions
,#format
etc. But in my opinion these should have stayed only inLotus::Controller
, for me it's such a perfect idea to configure individual components (I original thought Lotus encouraged that), and not the globalApplication
class itself.What do you think about this?
The text was updated successfully, but these errors were encountered: