Now that people are beginning to create more and more plugins, it would perhaps be good to step in here and propose a few conventions to make our lives simpler and to make plugins easier to maintain. What follows are a few ideas I''ve had--feel free to propose more or to talk about better ones: 1. Let''s not reopen the Rails namespaces for our plugin components. I suggest creating your own namespace for your own plugins. If it were me, I might do: module JamisBuck class ActsAsChunkyBacon ... end end This reduces the risk of namespace conflicts between two or more plugins, if two people implement the same functionality independently. 2. Let''s DO follow the Rails extension conventions. Looking at Rails, you''ll see this idiom very frequently: module SomeThing def self.included(base) base.extend(ClassMethods) class <<base alias_method :override_without_something, :override alias_method :override, :override_with_something end end module ClassMethods def some_class_level_method end end def some_instance_level_method end def override_with_something ... override_without_something ... end end This allows you to then extend any class at all with this functionality, allowing you to more easily stub out a parent class and unit test your extension. Once you have it set up this way, you can easily include it into whatever Rails component you desire, in the plugin''s init.rb: ActiveRecord::Base.send(:include, Something) 3. Let''s DO include unit tests. I''ve only done this on some of the plugins I''ve developed, and I need to do better, but by including unit tests you greatly increase people''s confidence in your contribution. It also just looks really good. :) Currently, there aren''t any helper methods to aid in unit testing plugins, though, so be on the lookout for commmon needs in plugin tests. That might be an area that Rails could help out with, down the road. 4. Let''s DO include documentation. Even a simple README that includes configuration instructions (if applicable) and a few examples demonstrating how your extension can be used. Any other ideas? Any thoughts on the above? - Jamis
Jamis Buck wrote:> Any other ideas? Any thoughts on the above?Yeah, what about a generator? Sascha Ebach
On Oct 26, 2005, at 8:59 AM, Sascha Ebach wrote:> Jamis Buck wrote: > >> Any other ideas? Any thoughts on the above? >> > > Yeah, what about a generator?Hmmm, I''m not sure I''m following what you are suggesting. Are you asking about what conventions generators ought to follow? - Jamis
I understood it to mean, should having a generator be part of the conventions for plugins? On 26 Oct 2005, at 16:03, Jamis Buck wrote:> On Oct 26, 2005, at 8:59 AM, Sascha Ebach wrote: > > >> Jamis Buck wrote: >> >> >>> Any other ideas? Any thoughts on the above? >>> >>> >> >> Yeah, what about a generator? >> > > Hmmm, I''m not sure I''m following what you are suggesting. Are you > asking about what conventions generators ought to follow? > > - Jamis > > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >
On Oct 26, 2005, at 8:03 AM, Jamis Buck wrote:> Hmmm, I''m not sure I''m following what you are suggesting. Are you > asking about what conventions generators ought to follow?Or just a generator to generate a plugin boilerplate. --Steve
> Or just a generator to generate a plugin boilerplate.bingo. Create a skeleton with a README and everything, maybe even all the suggestions in a file called CONVENTIONS. Sascha
On Oct 26, 2005, at 10:15 AM, Sascha Ebach wrote:>> Or just a generator to generate a plugin boilerplate. >> > > bingo. > > Create a skeleton with a README and everything, maybe even all the > suggestions in a file called CONVENTIONS.That''s a great idea, Sascha. Thanks for the suggestion. - Jamis
Hi Jamis, Well, this is what I get for posting to my blog before opening my inbox... http://tinyurl.com/8rant (and I love that I have a tinyurl with ''rant'' in it) Anyhow, the only thing that I can add is that using your namespace in your /lib directory to avoid ''require'' conflicts seems ''right'' to me. Regards, Trevor On 26-Oct-05, at 7:38 AM, Jamis Buck wrote:> > This reduces the risk of namespace conflicts between two or more > plugins, if two people implement the same functionality independently. >-- Trevor Squires http://somethinglearned.com
On Oct 26, 2005, at 10:46 AM, Trevor Squires wrote:> Hi Jamis, > > Well, this is what I get for posting to my blog before opening my > inbox... > > http://tinyurl.com/8rant > > (and I love that I have a tinyurl with ''rant'' in it) > > Anyhow, the only thing that I can add is that using your namespace > in your /lib directory to avoid ''require'' conflicts seems ''right'' > to me.Well said, Trevor (nice article). I completely agree. Note, however, that in order for Rails to be able to easily find your namespaced classes and modules, you''ll need to add some intermediate files at each level of your namespace hierarchy, like so: lib/ lib/jamis.rb lib/jamis/ lib/jamis/buck.rb lib/jamis/buck/ lib/jamis/buck/my_module.rb jamis.rb looks like this: require ''jamis/buck'' buck.rb looks like this: require ''jamis/buck/my_module'' and my_module looks like this: module Jamis module Buck module MyModule ... end end end Then, you can simply do Jamis::Buck::MyModule in your code and not have to do any explicit requires anywhere. (Without those intermediate files, you''d have to do ''require "jamis/buck/my_module"'' somewhere in your code before you could access the module.) - Jamis> > Regards, > Trevor > > > On 26-Oct-05, at 7:38 AM, Jamis Buck wrote: > > >> >> This reduces the risk of namespace conflicts between two or more >> plugins, if two people implement the same functionality >> independently. >> >> > -- > Trevor Squires > http://somethinglearned.com > > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >
Hi Jamis, In my best pirate voice I say: "yarrr, there be dragons!" At first blush I think the *exact* opposite should be the advice. You should not set up intermediate files,expecting rails to "easily find your namespaced classes and modules". Otherwise, using the example you''ve provided below, under certain circumstances (i.e. when the require is deferred) users can only have one plugin installed from the vendor "Jamis". Consider: plugins/jamis_my_module/lib/jamis.rb plugins/jamis_my_module/lib/jamis/* plugins/jamis_my_other_module/lib/jamis.rb plugins/jamis_my_other_module/lib/jamis/* Jamis::MyModule::Foo Jamis::MyOtherModule::Bar As I''ve said, this isn''t a big deal if all of your plugins require *everything* during init.rb because $LOAD_PATH is correctly set at that time. However, intermediate files suggests that deferred-implicit requires are okay and I''m pretty sure they aren''t. Then again, I could be totally missing something... Regards, Trevor On 26-Oct-05, at 10:06 AM, Jamis Buck wrote:> Note, however, that in order for Rails to be able to easily find your > namespaced classes and modules, you''ll need to add some intermediate > files at each level of your namespace hierarchy, like so: > > lib/ > lib/jamis.rb > lib/jamis/ > lib/jamis/buck.rb > lib/jamis/buck/ > lib/jamis/buck/my_module.rb > > jamis.rb looks like this: > > require ''jamis/buck'' > > buck.rb looks like this: > > require ''jamis/buck/my_module'' > > and my_module looks like this: > > module Jamis > module Buck > module MyModule > ... > end > end > end > > Then, you can simply do Jamis::Buck::MyModule in your code and not > have to do any explicit requires anywhere. (Without those intermediate > files, you''d have to do ''require "jamis/buck/my_module"'' somewhere in > your code before you could access the module.) >
On Oct 26, 2005, at 1:08 PM, Trevor Squires wrote:> Hi Jamis, > > In my best pirate voice I say: "yarrr, there be dragons!" > > At first blush I think the *exact* opposite should be the advice. > You should not set up intermediate files,expecting rails to "easily > find your namespaced classes and modules". > > Otherwise, using the example you''ve provided below, under certain > circumstances (i.e. when the require is deferred) users can only > have one plugin installed from the vendor "Jamis". Consider: > > plugins/jamis_my_module/lib/jamis.rb > plugins/jamis_my_module/lib/jamis/* > plugins/jamis_my_other_module/lib/jamis.rb > plugins/jamis_my_other_module/lib/jamis/* > > Jamis::MyModule::Foo > Jamis::MyOtherModule::BarYou''re right of course, Trevor. However, I''m reluctant to say that, as a best practice, consumers of certain plugins must explicitly require something--it kind of defeats the whole "drop in and use" idea of plugins. Still, I''m not seeing my way to a clean approach to it. You either accept conflicts (as you described), or you add some unique-ifying text to the file and module names (yuck). Anyway, let''s see what evolves. - Jamis> > As I''ve said, this isn''t a big deal if all of your plugins require > *everything* during init.rb because $LOAD_PATH is correctly set at > that time. > > However, intermediate files suggests that deferred-implicit > requires are okay and I''m pretty sure they aren''t. > > Then again, I could be totally missing something... > > Regards, > Trevor > > On 26-Oct-05, at 10:06 AM, Jamis Buck wrote: > > >> Note, however, that in order for Rails to be able to easily find >> your namespaced classes and modules, you''ll need to add some >> intermediate files at each level of your namespace hierarchy, like >> so: >> >> lib/ >> lib/jamis.rb >> lib/jamis/ >> lib/jamis/buck.rb >> lib/jamis/buck/ >> lib/jamis/buck/my_module.rb >> >> jamis.rb looks like this: >> >> require ''jamis/buck'' >> >> buck.rb looks like this: >> >> require ''jamis/buck/my_module'' >> >> and my_module looks like this: >> >> module Jamis >> module Buck >> module MyModule >> ... >> end >> end >> end >> >> Then, you can simply do Jamis::Buck::MyModule in your code and not >> have to do any explicit requires anywhere. (Without those >> intermediate files, you''d have to do ''require "jamis/buck/ >> my_module"'' somewhere in your code before you could access the >> module.) >> >> > > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >
Hey again, comment below: On 27-Oct-05, at 8:20 AM, Jamis Buck wrote:> On Oct 26, 2005, at 1:08 PM, Trevor Squires wrote: >> >> plugins/jamis_my_module/lib/jamis.rb >> plugins/jamis_my_module/lib/jamis/* >> plugins/jamis_my_other_module/lib/jamis.rb >> plugins/jamis_my_other_module/lib/jamis/* >> >> Jamis::MyModule::Foo >> Jamis::MyOtherModule::Bar > > You''re right of course, Trevor. However, I''m reluctant to say that, as > a best practice, consumers of certain plugins must explicitly require > something--it kind of defeats the whole "drop in and use" idea of > plugins. Still, I''m not seeing my way to a clean approach to it. You > either accept conflicts (as you described), or you add some > unique-ifying text to the file and module names (yuck). > > Anyway, let''s see what evolves. > > - Jamis >Woke up, got out of bed, dragged a comb across my --- "oh hey, I think I can make that work!" (with apologies to the Beatles) I''m 99.9% certain I''ve got a clean solution for this. More to come. Regards, Trevor
On 26-okt-2005, at 18:21, Jamis Buck wrote:> On Oct 26, 2005, at 10:15 AM, Sascha Ebach wrote: > > >>> Or just a generator to generate a plugin boilerplate. >>> >>> >> >> bingo. >> >> Create a skeleton with a README and everything, maybe even all the >> suggestions in a file called CONVENTIONS. >> > > That''s a great idea, Sascha. Thanks for the suggestion.And please a test suite with flunk() in the middle. -- Julian "Julik" Tarkhanov
In article <f27c9b21a79f97e356af9650a2c51045-k8q5a0yEZAgS+FvcfC7Uqw@public.gmane.org>, trevor- k8q5a0yEZAgS+FvcfC7Uqw-XMD5yJDbdMReXY1tMh2IBg@public.gmane.org says...> > I''m 99.9% certain I''ve got a clean solution for this. > > More to come. >Too small to fit in the margin of this page? -- Jay Levitt | Wellesley, MA | I feel calm. I feel ready. I can only Faster: jay at jay dot fm | conclude that''s because I don''t have a http://www.jay.fm | full grasp of the situation. - Mark Adler
On 10/26/05, Jamis Buck <jamis-uHoyYlH2B+GakBO8gow8eQ@public.gmane.org> wrote:> lib/ > lib/jamis.rb > lib/jamis/ > lib/jamis/buck.rb > lib/jamis/buck/ > lib/jamis/buck/my_module.rb[...]> Then, you can simply do Jamis::Buck::MyModule in your code and not > have to do any explicit requires anywhere. (Without those > intermediate files, you''d have to do ''require "jamis/buck/my_module"'' > somewhere in your code before you could access the module.)How about putting the "require ''jamis/buck/my_module''" in the module''s "init.rb"? This way, the user does not have to add anything to use the plugin and you as the plugin-developer can keep it simple. Sebastian
What is the convention and mechanism for distributing static plugin content such as stylesheets, javascripts and images? -Sean
You might want to look at some of the stuff in Rails Engines, a plugin which I will (hopefully) be releasing at the start of next week - it has mechanisms for making static content available to the webserver, plus a bunch of other stuff. I''m in the midst of making a ruby-forge project for it: http://rails-engines.rubyforge.org/ Essentially it''s a plugin to help enable the use of "uber-plugins" - we''ve taken the concept of plugins to the "next level" and let you include whole chunks of rails-app functionality without impacting on your own application files or directory tree. Plus all the code in any Engine (uber-plugin) is totally overridable at a fairly granular level. I''m hoping that Rails Engines might possibly be used as an alternative to the current generator mechanism, which can be be problematic in that updating generator-created parts of your application becomes a merge-intensive drain when you want to take advantage of new features in, say, a Login System. Speaking of which, as a (useful) proof of concept, the currently-popular salted-login-generator has been repackaged as an Engine, and also greatly enhanced with role & permission features; I''ll be releasing this as the first Engine in tandem with the Engine plugin itself so folks can get an idea of what I''m talking about. Hopefully this will be something the community can use, and it may even foster more collaboration on these commonly-needed bits of functionality (authentication, reporting, model security....). Anyway, I''ve probably already said too much.... keep your eyes peeled, and your ear to the ground.... - lazy On 10/28/05, Sean Treadway <seant-4hAOBN7YpypC/EKHXe1xOQ@public.gmane.org> wrote:> > What is the convention and mechanism for distributing static plugin > content such as stylesheets, javascripts and images? > > -Sean > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >_______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails
On Friday 28 October 2005 15:49, James Adam wrote:> You might want to look at some of the stuff in Rails Engines, a > plugin which I will (hopefully) be releasing at the start of next > week - it has mechanisms for making static content available to the > webserver, plus a bunch of other stuff. I''m in the midst of making a > ruby-forge project for it: > > http://rails-engines.rubyforge.org/Now that''s a mouthful. If it works I might even use it. I''m currently pondering how to package BoilerPlate[*]. Right now, I keep the core functionality in a subdirectory of vendor/ and use a generator to copy a bunch of assets to public/. At least the latter part I''d like to get rid of. Can you describe in a few sentences how Rails Engines work internally? Michael [*] http://www.schuerig.de/michael/boilerplate/ The available releases are seriously outdated. -- Michael Schuerig This is not a false alarm mailto:michael-q5aiKMLteq4b1SvskN2V4Q@public.gmane.org This is not a test http://www.schuerig.de/michael/ --Rush, Red Tide
> http://rails-engines.rubyforge.org/Ack... less ''top secret project speak'' and more code! I''m definitely interested in this. I''m working on two plugins that may be a bit large to be rails plugins. I''m still not quite sure how to package it. I too am eagerly awaiting your release.... -- rick http://techno-weenie.net
On Oct 28, 2005, at 7:23 AM, Sean Treadway wrote:> What is the convention and mechanism for distributing static plugin > content such as stylesheets, javascripts and images?Nothing, right now. If anyone has a good suggestion for these things, I''m all ears. - Jamis
Apologies for the ''top secret project speak'' - would you believe that I''m actually away from the codebase until Monday! It''s frustrating for me too, but I *promise* to make good come next week. Ok, here''s an example, to make things a bit more concrete: your_rails_app/ |- app/ | |- controllers/ | |- models/ | |- helpers/ | |- views/ |- lib/ | - <etc> |- vendor/ |- plugins/ | |- engines/ <-- the engines plugin itself |- engines/ |- user_engine <-- the ''user'' engine | |- app/ | | |- controllers/ | | |- model/ | | |- helpers/ | | |- views/ | |- lib/ | |- db/ | |- public/ | |- init.rb |- an_other_engine |- <etc> What you''ve got is a structure mirroring a rails app itself, with all the controllers, models, helpers, public files (javascripts, images, stylesheets), libraries and the database schema in a new subdirectory of /vendor called ''engines'' (I wanted to put them in /vendor/plugins, but until we get plugin dependencies that''s a no-go, since you can''t load an engine before the ''engines'' plugin itself gets loaded). For the user_engine example, which is basically a superset of the salted login generator everyone knows it includes the views and controllers to manage user logins, signups, editing and so forth, plus a bunch more stuff, but that''s not so important right now. You''ll still need to edit /app/controllers/application.rb to include the UserEngine and set up the before_filters and so on. The difference is that the actual login system code is isolated from the rest of your appliation, and can therefore be updated without kaboshing any of your stuff. Ack, this is a getting to be a long post. I''ll update the rubyforge page with an example soon (serves me right for starting talking about this when I''m on holdiay!) In your environment.rb file, you need to add the following line at the bottom: start_engine ''user_engine'' # or whatever your engine is called This takes care of making sure that the engine code is available to your application. It also mirrors whatever you''ve got in <engine>/public under a directory of /public/engine_files. So, say for instance we have a stylesheet for the user engine - you might end up with something like this in /public: public/ |- javascripts/ <-- your javascripts |- stylesheets/ <--- your stylesheets |- robots.txt |- <etc, all the standard stuff> |- engine_files/ |- user_engine |- stylesheets | |- user_engine.css |- javascripts | |- user_engine.js |- images |- padlock.jpg |- < etc > The public engine files get copied over every time you restart your server (actually, any time your environment.rb gets read and the start_enginemethod is called). You''ve got some helper functions automagically available for your layout too: engine_stylesheet "user_engine" ==> <link href="/engine_files/user_engine/stylesheets/user_engine.css" media="screen" rel="Stylesheet" type="text/css" /> Ditto for javascript files. Internally there''s actually not too much magic happening. The key is loading controllers from the engines first, and then loading any controllers of the same name from the user''s app directory over the top - this lets you override the methods you want changed. For views/partials, if it finds them in your app directory, they''ll get used, otherwise it will use those from the engine. The Engines plugin itself isn''t big, it just tweaks the Rails core enough to make that happen... but I''m sure there''s plenty more than can be added. For teams working with Rails, the huge plus is being able to include engines as svn:externals - bugs get fixed just by running svn update on your project, and all your engines get updated without interfering with your application-specific code. I hope this explains things a bit better - as I said, I *promise* that code will appear when I get back to work after the weekend!! - lazy On 10/28/05, Rick Olson <technoweenie-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> > > http://rails-engines.rubyforge.org/ > > Ack... less ''top secret project speak'' and more code! I''m definitely > interested in this. I''m working on two plugins that may be a bit > large to be rails plugins. I''m still not quite sure how to package > it. > > I too am eagerly awaiting your release.... > > -- > rick > http://techno-weenie.net > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >_______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails