Skip to content
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

Split out AV from AP, part 2 (WIP) #11396

Merged
merged 73 commits into from Aug 26, 2013
Merged

Conversation

lukaszx0
Copy link
Member

Hey,

This PR is a continuation of #11032.

I've moved all the rendering-related code to ActionView and added to its railties code that hooks all necessary things to ActionPack if the AV is loaded.

As straight forward as it sounds, it turned out it's not that easy. In the AP, all its components needs to be loaded in order. This introduced huge complication with loading AV stuff in railties as it was included at the end and needed to be included somewhere in the middle.

After discussing this issue with @drogus and @josevalim we agreed on simple solution - https://github.com/strzalek/rails/blob/fc89313fe3f9e508dbe84616fa9a91db123337b0/actionpack/lib/action_controller/base.rb#L164-168

With this code, we're now able to load AV::Rendering between AbstractController::Rendering and ActionPack::Rendering simply by:

ActionPack::Base.superclass.send(:include, ...)

This PR is subject to discussion and WIP, not final solution.

What are your thoughts? Maybe you have other ideas how to abstract ActionView ?

TODOS:

  • --skip_action_view
  • changelog
  • deprecations (if any)
  • Create BasicRendering implementing only render text:
  • Create public API method rendering_format in controller
  • Create ActionPack integration tests for ActionView

/cc @drogus @josevalim @jeremy @wycats @spastorino @tenderlove

@@ -10,12 +10,11 @@ module AbstractController
autoload :Base
autoload :Callbacks
autoload :Collector
autoload :DoubleRenderError, "abstract_controller/rendering.rb"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for .rb extension.

@lukaszx0
Copy link
Member Author

Thanks @josevalim for the review!

I've fixed the small things. I was aware of moving both view_assigns and protected_vars stuff from AV and I will address them in coming days.

The main purpose of posting this was get opinion on the solution I finally stick to (https://github.com/strzalek/rails/blob/fc89313fe3f9e508dbe84616fa9a91db123337b0/actionpack/lib/action_controller/base.rb#L164-168) from others so please that into account that there's a lot rough edges to polish.

@@ -160,7 +161,11 @@ module ActionController
# render action: "overthere" # won't be called if monkeys is nil
# end
#
class Base < Metal
metal = Class.new(Metal) do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this have to be an anonymous class? Would it be easier to debug/extend if it was more explicit?

class MetalWithRendering < Metal
  include AbstractController::Rendering
end

class Base < MetalWithRendering

(MetalWithRendering may be a poor name, better ideas welcome)

I would also put some of the explanation in your PR as for the purpose of this class and the way its defined, as code comments describing this class, so people know why this "hack" is needed and why you don't just include Rendering into Base.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't make it a named class for now but we can always change it later. Also, you need to add code comments on why we need this class and you need to move the comments already existing to the top of class Base < metal otherwise the documentation will disappear.

@lukaszx0
Copy link
Member Author

@robin850 your comments have been addressed in the latest commit. It's true that it's WIP but it's also very helpful to have those kind of suggestions early in the process. Thank you!

@robin850
Copy link
Member

@strzalek : Thanks! I'm always interested in this kind of PR. Glad to help! 😃

initializer "action_view.setup_action_pack" do |app|
ActiveSupport.on_load(:action_controller) do
ActionController::Base.send(:include, ActionView::Layouts)
end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're supposedly already in AC::Base scope under the on_load block, so calling include directly should work I imagine (plus it'd work with rails-api that way).

@lukaszx0
Copy link
Member Author

Few weeks later and it looks like the whole thing is finished! 💥

Tests passes and sample application which doesn't use AV at all can be crated without any problem. You can find it here: https://github.com/strzalek/rails-sandbox

Could you guys give it a look and review all this stuff? It would be very appreciated!

/cc @drogus @josevalim @jeremy

@drogus
Copy link
Member

drogus commented Aug 12, 2013

I will review the entire PR in the evening, but I just wanted to confirm that I've been able to build simple rails app, which just rendered JSON without AV. The simplest way to do that is to create app with --skip-action-view option. Then you should be able to use render :text in controller in order to render a response.

@lukaszx0
Copy link
Member Author

@guilleiguaran
Copy link
Member

@strzalek I'm curious, what's the current status of AV? Is possible to use it without depend on AP? 😃

@@ -12,17 +45,17 @@ def process_action(*) #:nodoc:

# Check for double render errors and set the content_type after rendering.
def render(*args) #:nodoc:
raise ::AbstractController::DoubleRenderError if response_body
raise ::AbstractController::DoubleRenderError if self.response_body
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the need to prefix with self here?

@lukaszx0
Copy link
Member Author

Hah, yes! Using this branch - YES.

The rails-sandbox (https://github.com/strzalek/rails-sandbox) project is rails app without ActionView. It uses rails from my fork and this branch.

You can clone it and give it a try 🎢 !

@egilburg
Copy link
Contributor

AV's gemspec does not depend on AP in production, so the short answer is yes.

However, it still has development dependency on AP, which I'm not sure is a good thing - it makes it harder to verify with tests that AV is actually truly independent of AP.

@lukaszx0
Copy link
Member Author

@egilburg it might be possible to remove dev dep from AP on AV but well, I don't think it makes any sense. At the end of the day, AP and AV are still closely tight together and 95% of the project or maybe even more will be always using rails with AV.

However, almost all of the tests concerning ActionView were moved away from AP to AV, and you can find them as integration ones in actionview/test/actionpack (https://github.com/strzalek/rails/tree/extract_renderers/actionview/test/actionpack). Notice that AV has also dev dep on AP as it needs stuff from there.

Besides that, in railties you can find basic_rendering_test.rb (https://github.com/strzalek/rails/blob/extract_renderers/railties/test/application/basic_rendering_test.rb) which is end-to-end test, checking if rails application without AV works as supposed. (it generates full rails app, makes few changes to files and setup, runs it and checks what was rendered).

That's how it is now. I think that AV should stay as dev dep on AP for now. We have quite good coverage to be sure if stuff with/without AV works properly. Maybe it'll change some time in the future, during the process of refactoring tests, because I must admit, in some places you can find huge mess in there.

Hope that explains everything, @egilburg. Let me know if you still have any questions.

@guilleiguaran
Copy link
Member

@strzalek I was talking about the other use case, using ActionView without Rails, something like this:

https://gist.github.com/drogus/2472931

I'll try it to check if everything works as expected 😄

@drogus
Copy link
Member

drogus commented Aug 12, 2013

@guilleiguaran this is also possible, see the previous PR #11032 :) @strzalek also confirmed that it works with my gists when you change the Gemfile to include actionview instead of entire actionpack.

@guilleiguaran
Copy link
Member

@drogus @strzalek nice!!! great work on this guys 😄

@lukaszx0
Copy link
Member Author

Yeah, just like @drogus said, it was possible after previous PR :)

@lukaszx0
Copy link
Member Author

I give a thought about AP and AV as dev dependency and actually it can - and maybe even should - be dropped. I've just pushed 2 commits, they're moving tests from abstract_controller to AV.

At this state, only few small things are keeping AV as dep. I'll try to remove them tomorrow.

@lukaszx0
Copy link
Member Author

Nice tool. I've updated gist with results: https://gist.github.com/strzalek/6293709

I'm again surprised in a good way 😍 I've run tests few times, results are slightly different every time but my branch seems to get either the almost the same numbers as master or lower (which is better).

What do you think 😉 ?

@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
s.requirements << 'none'

s.add_dependency 'actionpack', version
s.add_development_dependency 'actionview', version
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you checked if action mailer actually works without action view? I guess I kind of ignored it when I was doing testing - if it's really as easy to use it without AV as actionpack it's ok to leave this line, otherwise we should either fix it or use add_dependency

@drogus
Copy link
Member

drogus commented Aug 25, 2013

I gave it another look and apart from one comment about action mailer it looks good to merge. I will be 🚢ing it today in the evening if there is no further concerns from others.

@lukaszx0
Copy link
Member Author

Great catch. That's true, it won't work without ActionView yet. I've pushed commit to make it first class dependency and I think we can merge it.

However I think it'll be easy possible to make ActionMailer work without ActionView to make able sending simple emails, which don't require template rendering (http://guides.rubyonrails.org/action_mailer_basics.html#sending-emails-without-template-rendering), without the need of AV.

I'll work on this later and send another PR not to hold this one.

drogus added a commit that referenced this pull request Aug 26, 2013
Remove dependency on Action View from Action Pack

This set of changes removes the need of using Action View with Action Pack.
Now you may use controllers without Action View, by rendering `:text` response
or alternatively you can plug in your own rendering logic. This is especially
handy when you're just dealing with APIs and don't need to include entire
Action View just to render simple JSON responses.
@drogus drogus merged commit c199580 into rails:master Aug 26, 2013
@lukaszx0
Copy link
Member Author

😍 🎊

@vipulnsward
Copy link
Member

👍

@lukaszx0
Copy link
Member Author

For the record. This was quite big change, moving mostly core parts framework which means some issues may arise later on. Feel free to @mention me on github/twitter or mail me directly if there will be any problem caused by this change.

@pftg
Copy link
Contributor

pftg commented Aug 26, 2013

👍

@spastorino
Copy link
Contributor

@strzalek great work ❤️

@egilburg
Copy link
Contributor

Awesome ❤️

I do think that it would be great if development dependencies could be removed between AP and AV. Otherwise, it's easy for someone in a later patch to re-introduce coupling between the two, and the build won't notice.

@rafaelfranca
Copy link
Member

💚💛❤️💜💙

@lukaszx0
Copy link
Member Author

@egilburg like I've said in previous comments. The goal is get rid of dev-dependencies in the future and I will work on that later. Regarding re-introducing coupling, I think that test added in railties is quite good protection from that.

end

# Overwrite render_to_string because body can now be set to a rack body.
def render_to_string(*)
if self.response_body = super
string = ""
response_body.each { |r| string << r }
self.response_body.each { |r| string << r }
string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method shouldn't set response_body, I saw it is "reverting" to nil in an ensure block but same could be accomplished with https://gist.github.com/spastorino/19079770e255adc97027.
Even more, shouldn't super already return a string instead of something that returns an object which responds to each?. In that case all the code in this method is useless.

/cc @josevalim @strzalek

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, well to be honest I have no idea what this code does. I've found this commit by @josevalim which introduced it: lukaszx0@4f04452

For me, it also looks like it could've be removed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously, response_body= had a bunch of complicated rules to set the body. So in order to reuse the logic, we assigned the value to response_body which normalized everything to an array. That's probably not the case today and I agree it can be refactored.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. I'll review this then

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet