-
Notifications
You must be signed in to change notification settings - Fork 194
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
Fix mixin/inheritance chain #4
Comments
So Shoes::Animation is loaded, then by framework-loader, Shoes::Swt::Animation is loaded. Should the GUI interface use before/after hooks to do its work? Do you have a vision for this? |
I kind of like the idea of before/after hooks, because it allows you to hook into the behavior rather than overriding it. It's potentially a significant amount of overhead, though. class Shoes::Button
def click
@gui.before_click
# all the code
@gui.after_click
end
end (or some metaprogrammatical version of the same). It may be desirable to override the behavior of a Shoes object sometimes. For that, what would we do? Something like this? class Shoes::Button
def initialize
self.extend Shoes::Swt::Button::Overrides
end
end
class Shoes::Swt::Button
module Overrides
def click
# Different click behavior
end
end
end Note that this is exactly the same thing we are doing now, except that it fixes the inheritance order. |
The before after turns them into middlewares, like Rack. I'm gonna give all of this a big think today... |
I will try to take time to dive into the latest code and refresh my memory |
TL;DR: Rack works like this: class ZomgMiddleware
def initialize(app)
@app = app
end
def call(env)
puts "Before, yo!"
@app.call(env)
puts "after, playa"
end
end
class LolMiddleware
def initialize(app)
@app = app
end
def call(env)
puts "lol!"
@app.call(env)
puts "wtfffff!"
end
end
class MyRackApp
def call(env)
puts "So useful!"
end
end
whatevs = Rack.builder do
use ZomgMiddleware
use LolMiddleware
end
whateves.mount MyRackApp
whatevs.run ... not 100% sure about the mounting. I'm sure you can see what this does. |
I've not had time yet... need to get my elbows into this. |
Middlewares are great for building up systems. What other layers might work into a middleware stack? This middleware idea also inverts the composition idea, so that the implementation becomes a wrapper around the DSL layer. If we wanted to stay with a 1:1 relationship like we have now, you could also make a case that |
Oh, and Vagrant uses this as an architecture: http://www.youtube.com/watch?v=fcNaiP5tea0 |
So I was toying around with this this evening, and I noticed one thing: the fact that Shoes is an instance_eval-y DSL makes some things hard. First, a sample: https://gist.github.com/2785728 Basically none of the logic is in class Shoes, it's all in the adapter. So I tried doing this: class Shoes
Adapter = Tk
def self.app(&blk)
Adapter::App.new.instance_eval(&blk)
end
end But... then the |
This is true. There has to be a certain open-endedness about the structure. If we are trying to make this DSL open to pluggable backends, then it seems like we want to keep as much logic as possible in the Shoes DSL proper (i.e. not repeat it for every implementation). So putting all the logic in the adapter might not be what we want anyway... |
I liked the talk about Vagrant and middleware. The wins for Vagrant were clear: composition through middleware enables simplification and reuse. In Shoes, I can see the simplification of objects (and after looking through the specs tonight, we desperately need this), but I can't yet see the reuse. Do we get that benefit? |
I'm not sure we do. I actually think that the pattern has a lot of issues, but just trying to get a number of options on the table... |
Yeah, I think that's a good idea. I just keep feeling like there's something there that I'm missing. So help me think about this differently. Take our current module Shoes
class Shape
include Shoes::CommonMethods
include Shoes::Common::Paint
include Shoes::Common::Style and also gets the SwtShoes::Button class mixed in: module Shoes
class Button
include SwtShoes::Button
end
end This is starting to look like a multilayered system. What if we wrote it like this: button = Shoes.builder do
use Shoes::Common
use Shoes::Common::Paint
use Shoes::Common::Style
use Shoes::Button
use SwtShoes::Button
end ? |
Umm,... Am I missing something? class Shoes::Swt::Button
def hello
# bla bla bla
end
end
Shoes.app do
b.button 'my own button'
b.hello
end |
I hope not :) But this is a good point to consider. I hadn't thought about where a user would extend Keep in mind this is all to make it easy to reuse the Shoes DSL with different implementations. |
I see. But, can't you choose Swt or Swing before loading their own Shoes::Button? |
HI Folks, Clarity here. Shoes and Shoes:: should be invisible to the "User". Remember As far as construction, I believe we're going to run into significant |
Yes. As it is right now, Shoes mixes in the Swt modules automatically. The user doesn't see them. See shoes/brown_shoes#14 for the beginning of the discussion. |
Well, Shoes can't be entirely invisible to the user. b = button "squish" Now I'm not sure whether it's important that users can open that class to add/modify behavior, but it's a good question to ask right now. |
Oh, I mis-worded that sentence. |
I read shoes/brown_shoes#14 and understood the background of this discussion. There are two directories, |
We could do it with either namespace. I prefer require 'shoes'
require 'shoes/swt' vs require 'shoes'
require 'swt/shoes' |
Umm, ... but OK for now. Can you close this issue? |
The issue remains open until we've done the refactoring work. An additional question is outstanding for #4, not sure if it's answerable You'll see in the code that I have "do_gui_stuff" type hooks, since the Thoughts? Peter Fitzgibbons On Fri, May 25, 2012 at 9:11 PM, ashbb <
|
In Shoes::App#initialize, there are two "do_gui_stuff" calls. Here's how I propose we solve this issue:
I still don't know about the whole middleware idea, but I think that this refactoring puts us in a better place for future refactorings. Less coupling. Thoughts? |
This is not direct answer, sorry. But I don't know yet why implementation namespace, i.e. Swt, is necessary. :( I changed C:\tmp\shoes4\lib\swt_shoes>diff app.rb app_new.rb 5c5 < module SwtShoes --- > module Shoes 10c10 < module App --- > class App 42,47d41 < end < end < < module Shoes < class App < include SwtShoes::App and did The samples/test1.rb is If possible, I'd like to use just |
HI Ash, What you're proposing is a code smell. I can't recall a documentation Thoughts? |
Umm,... No. My point is different. Shoes.configuration.framework = 'swt_shoes' and line 9-11 in lib/shoes/configuration.rb def framework=(value)
@framework = value
require value
end You've already choose the framework (require the file) in the above. So, it's okay to use the same module name 'Shoes' both in lib/swt_shoes/app.rb and lib/swing_shoes/app.rb even if they are completely different. Am I missing something? |
@ashbb let me see if I understand you correctly. I think you are saying that because we choose the framework once, and don't change it, there is no functional difference between mixing in a module like Assuming I do understand :) Peter's point is still true for me—it is easier (for me) to reason about the code when it has a different name. However, your idea does solve the problem that I first noticed. It makes the code in But I am suggesting that we turn those modules into classes. So we would have |
HI Eric, Not sure why these need to be classes? As mixins the framework can still Could you tell me about other motivations to make the framework be classes |
Oh, I didn't think you were suggesting Draper :) I'm enjoying stretching the mind a little... Your example instantiation code is pretty much what I was thinking, except that we would want to instantiate a Shoes.backend = Shoes::Swt # this would be the default, but could be overriden like this.
module Shoes
class Button
def initialize
@gui = Shoes.backend.const_get("Button").new
end
end
end |
Does this video have visual for y'all? #my-new-os-install-is-broken? |
Shoes.backend::Button should also work? (How do I prove this in IRB?) |
@wasnotrice Ahhh yeah, that's what happens when it's from memory. ;) @pjfitzgibbons yes, it does: $ irb
1.9.3-p194 :001 > module Shoes
1.9.3-p194 :002?> attr_accessor :backend
1.9.3-p194 :003?> class Swt
1.9.3-p194 :004?> end
1.9.3-p194 :005?> class Button
1.9.3-p194 :006?> def initialize
1.9.3-p194 :007?> @gui = Shoes.backend::Button.new
1.9.3-p194 :008?> end
1.9.3-p194 :009?> end
1.9.3-p194 :010?> end
=> nil
1.9.3-p194 :011 > Shoes::Button.new
NoMethodError: undefined method `backend' for Shoes:Module
from (irb):7:in `initialize'
from (irb):11:in `new'
from (irb):11
1.9.3-p194 :012 > module Shoes
1.9.3-p194 :013?> class << self
1.9.3-p194 :014?> attr_accessor :backend
1.9.3-p194 :015?> end
1.9.3-p194 :016?> end
=> nil
1.9.3-p194 :017 > Shoes::Button.new
TypeError: is not a class/module
from (irb):7:in `initialize'
from (irb):17:in `new'
from (irb):17
1.9.3-p194 :018 > Shoes.backend = Shoes::Swt
=> Shoes::Swt
1.9.3-p194 :019 > Shoes::Button.new
NameError: uninitialized constant Shoes::Swt::Button
from (irb):7:in `initialize'
from (irb):19:in `new'
from (irb):19
1.9.3-p194 :020 > I forgot to actually make a Shoes::Swt::Button, hence the last error. And yes, it has video. It uses the HTML5 player by default iirc... |
Video didn't work for me either |
Oh! Well, I posted the wrong one, anyways: https://vimeo.com/42622511 was what I meant. Sigh... |
I'm really liking the direction of this. Feels like an actual plugin. Does the following outline our usage correctly? I assigned this issue to me... I'd really like to do this one as soon as we I got this, as a known-good example : irb(main):001:0> module Shoes |
👍 |
1 similar comment
+1 |
HI Folks, ./lib/shoes contains all the base classes, Shoes::Button, Shoes::Window, Filepath for Shoes::Swt ends up being ./lib/shoes/swt ... this is I'm thinking of placing the backends in ./lib/backends/swt/shoes/swt Then this will work ok : Thougths? |
Maybe it's the Rails in me, but I really think that if you |
We now have two problems :
So, Swt::Shoes is buried in the forest of Swt gem. I believe that my choice to name the backend "SwtShoes" was an attempt to Thoughts? Peter Fitzgibbons On Sun, May 27, 2012 at 9:21 PM, Steve Klabnik <
|
I definitely think Shoes::Swt should be in lib/shoes/swt. This is POLS. It shouldn't produce that much confusion. To me, it says, "Hey, this is part of Shoes...the Swt part." That makes more sense than "Hey, this is part of Swt...the Shoes part." And also less confusion than manipulating the load path to include files from other directories. Although admittedly, adding the spec directory to the load path was part of getting Shoes to build on Travis :) If we are worried that Shoes::Swt will be lost among other Shoes business, we can always extract it into its own gem at the point where it causes problems. |
Through all the above discussion, it would appear to me that the following is the code structure you want. Right? module Shoes
class << self
attr_accessor :backend
end
def self.app &blk
App.new &blk
end
class App
def initialize &blk
instance_eval &blk
end
def button
Shoes::Button.new
end
end
class Button
def initialize
@gui = Shoes.backend::Button.new
end
attr_accessor :gui
end
end
module Swt # instead of Shoes::Swt
class Button; end
end
Shoes.backend = Swt # instead of Shoes::Swt
Shoes.app do
p self #=> #<Shoes::App:0xf6b148>
b = button
p b.class #=> Shoes::Button
p b.gui #=> #<Swt::Button:0xf6b0b8>
end |
If we don't namespace our Swt stuff, won't we run afoul of the official Swt stuff? |
HI Ash, You're exactly right... with one exception, which I'll make sure to cover : Shoes.app do Thanks for the confirmation. |
Well, yes we will run into the Swt namespace in the gem. Other than "hoping" that we don't clash the namespace, I'm open to Thoughts? |
I don't have any kind of problem with |
Here's a test inside the new namespace, with some runtime results (pry module Shoes I think we're clear. I'm going for it. Shoes On! On Mon, May 28, 2012 at 9:21 AM, Steve Klabnik <
|
Check me : The global #window, currently defined in ./lib/swt_shoes.rb really belongs on ./lib/shoes... no? def window(_a, &b) |
Yes. |
I think this all looks good except that we should keep our Swt stuff in the Shoes namespace. That's what a namespace is for :) If we dont like saying Shoes.backend = Shoes::Swt we can certainly code Shoes.backend = Swt # => Shoes::Swt |
This issue is HUGE. Would it be possible/helpful to separate into 2 issues, so that we can parallelize the tasks?
|
Okay. Move to each separated threads.
|
Ok, I see what happened here. Peter Fitzgibbons |
Fixes to stop needing bin/rspec and bin/guard
I had to code lots of classes before I ran into a problem with this structure, so I think it was a completely reasonable idea. We adapt.
2 Answers:
A: Yes, currently,
SwtShoes::Button
is a mixin toShoes::Button
, and likewise for otherShoes
classes andSwtShoes
modules. In theSwtShoes::Button
file, there is a reopening ofShoes::Button
to mixin theSwtShoes
module. It is expected in the sense that everything is currently coded that way.A: Hmmm. I think we could go either way on this.
Shoes::Button::Swt
makes sense because the framework buttons kind of belong to the Shoes class. But when I visualize it:I think it makes more sense to namespace like
Shoes::Swt::Button
. Since the gui framework classes are going to be bundled together, it makes sense that they are namespaced together. Compare:The text was updated successfully, but these errors were encountered: