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

[Feature Request] My proposal of plugins system #2083

Closed
wants to merge 83 commits into from

Conversation

Kikobeats
Copy link

Hello everyone,

This weeks I'm working trying to design a plugin system for Sails. At the beginning part of the Yomguithereal's concepts but early I detected other things that plugin system needed to support. (In my opinion, of course)

For this reason, I created a parallel proyect called Sailor which is my playground scenario to try out the plugin system where I try to improve it.

My idea is not to create a parallel proyect. The idea is that sails implements a plugin system and uses sailor for this.

I want to explain how the plugin system works and which modifications in the current sails version are necessary for this.

Hipster mode on, get ready for reading and GO!

The idea

  • We have an application and we can divide it in two parts: a unique core (called base), and one or more modules.
  • The modules have logic domain that the core loads and they are a little piece of domain code.
  • The responsability of each module is the module.
  • The modules can be relationated between them.
  • The modules can be combinated for create a custom backend.
  • The modules follow FIRST principles.

How it works

Scaffolding support

Sails has a default configuration when you create a sails application. The sails application have a scaffolding that overwrite the start sails core settings.

The concept of the plugin system is the same, but with another depth level: sails has a configuration that lets you overwrite your base application, and you can overwrite or add more logical code with each module as well.

It could exist conflicts between base and module configuration. In this case, the base configuration has priority.

For this, I modified the core (moduleloader hook). I declared the modules in a new file called config/plugins.js that is an array of plugin namen that checks if it exists in node_modules as dependency. If it exist, I load it:

Multilanguage support

I created a module called sailor-translate that is based in the angular-translate solution. Basically it creates a JSON for each language and it is very easy to create and use (much more than locales files)

Why? Because for me is very important to create a multilang API from the beginning. You should work hard in your API development in order to make your frontend much easier to use.

You can check that this pseudo-file compiles to this one . I use sailor-translate for register the translation files and make it easier to access.

Another thing about this is that I had the core for creating route binding for each language (blueprint hook). I thought that it could be improved because in my solution I read the local files to know the languages and later I bind the routes for each language. I try binding a route like /:lang/ but it doesn't work. Also, I created a little middleware for fix language in the route because some routes don't have a /lang/ param.

Tasking support

Tasks for each module are necessary because the modules are independent. For this reason I modified the core to load the module's tasks in a different thread compared to the base tasks thread. (grunt hook).

The only problem in this aspect is that you want to install a module as a dependency, before that you lift the server you need to install the local module grunt dependencies or the tasks that don't work (because the tasks are local in the module). In my launcher it doesn't work but is easy to fix.

Scaffolding

The Scaffold of each module is similar to the base, but more extended:

--sails-module-example/
----api/
------api/adapters/
------api/blueprints/
------api/controllers/
------api/hooks/
------api/models/
------api/policies/
------api/responses/
------api/services/
----views/
----config/
------config/translation.js
----translation/
-----Gruntfile.js

In this aspect, I think that to overwrite blueprint is SO USEFUL to avoid rewriting code. Another think like adapters or hooks are not important for module design.

One more thing

Another thing that I did not commented and that is a problem for my workflow is the waterline errors schema. It is ugly, and of course, it is not multilanguage. I propose a more simple schema based in express-validator:

It is simple and clean. And the most important: easy to setup (and easy to make multilanguage). This is ready for frontend. Doesn't exist a waterline errors validation interface for validate fields out of the ORM.

What To Improve

  • Fixed grunt dependencies modules installation before lift server.
  • Better way to binding multilang routes.
  • Native mutlilang route support in the core and not use a middleware for this.
  • Improve waterline errors and make multilang

Demo

You can experiment with this features NOW.

  1. Install sailor globally. Basically is a little CLI interface to run the sails core modification.

  2. Check sailor-module-user that is a version of sails-generate-auth rewrite like a module. (I have a little problem with login methods. Check the tests, try to fix and PR pls :P)

  3. sailor-app-playground is a little app that use this module. Experiment with this!

Your turn: Feedback!

Please, now is your moment. Feedback this plugin system. For make a better Sails core.

…ilor

* 'master' of https://github.com/balderdashy/sails:
  Remove reference to req.csrfToken in CORS hook
  Fixed CORS+CSRF tests
  Don't add req.headers.origin to socket requests in CORS hook This is now being handled in a more global manner in the sockets hook Also ignore /csrfToken requests in CORS hook when CSRF hook is enabled
  Add req.headers.origin for all x-domain socket requests
  Move CSRF route binding into initialize function This way it takes place *after* the views hook does its bindings, including adding res.view closes balderdashy#1983
…ilor

* 'master' of https://github.com/balderdashy/sails:
  0.10.0-rc10
  Don't smash lodash defaults
  Removed unused _.defaults var.
  Updated CORS/CSRF tests
  Simplified CORS+CSRF handling In CSRF hook, added /csrfToken route directly to sails.config.routes so that CORS hook will process it like every other route.  This also allowed simplification of the route handler--no need for more CORS checks since the CORS hook will handle it. In CORS hook, removed all references to CSRF entirely; it's just another route now.  Also removed code related to sending 403 responses for socket requests on bad origins, since we're doing that for ALL requests now regardless of transport.
  Skip same-origin requests in CORS catchall, but catch everything else even if no access control header was set
  Only set headers if they'll have values Prevents sending a header in the response with a value of "undefined"
  Added "Access-Control-Allow-Credentials" to x-domain /csrfToken response
  Fixed clearHeaders definition
  Updated CSRF tests skipping a couple of OPTIONS tests that don't work right now
  Updated CSRF protection CSRF check is always made now it protectionEnabled is on, but the token is only set if its from an approved origin (or is not cross-domain)
  Added a CORS catchall route handler that denies requests from disallowed domains Added a CORS preflight route handler that basically says yes to everything
  Updated the way the router binding log works Use "_middlewareFn" key of the bound function if available, so we only have to use the options hash as a last resort Added _middlewareFn key and/or function names wherever possible in hooks
  Added "log" option to controller action and policy functions Now with sails lift --silly, you can see what is being bound to each route--no more mystery double log messages.
  Added ability to add custom log message to router "bind" method, to make it easier to tell what's being bound.
…ilor

* 'master' of https://github.com/balderdashy/sails:
  Test inline policy in routes config.
  Don't allow explicit routing to controller/action unless it's a custom action Routes like "UserController.find" will no longer work unless you have a custom "find" action in your User controller.  To use the "find" blueprint, route to {"blueprint":"find", "model":"user"} or, if the path starts with /user/, just {"blueprint": "find"} will do.
  Reverse course on CORS in favor of a more traditional implementation Don't return 403 unless "sails.config.cors.securityLevel" is set to high or above Handle OPTIONS requests on a route-by-route basis Changed order of _middlewareType log in bind.js
  Expose original function being bound to route in router:bind event
* 'master' of https://github.com/balderdashy/sails:
  Allow default middleware order to be used when custom middleware is specified.
  0.10.1
  Updated dependencies
  Added test environment files for mongo, mysql
* 'master' of https://github.com/balderdashy/sails:
  Stop silencing Express multipart warning, since we don't use Express multipart anymore. Three cheers for Skipper! closes balderdashy#2055
  Fix typo in getting custom middleware order. closes balderdashy#2051
  Fixes balderdashy#2052
  Update README.md
  Update README.md
  Update README.md
@Kikobeats Kikobeats changed the title # My proposal of plugins system My proposal of plugins system Aug 8, 2014
@Kikobeats Kikobeats changed the title My proposal of plugins system [Feature Request] My proposal of plugins system Aug 9, 2014
@sskyy
Copy link

sskyy commented Aug 9, 2014

wow, amazing work!

* sails:
  Update EVENTS.md
  Expanded roadmap.
  Update ROADMAP.md
  Update ROADMAP.md
  Update ROADMAP.md
  Added information about new Feature Request procedure.
  Better explanation of setting sails.config.models.migrate.
  0.10.3
  0.10.2
  Depend on skipper 0.5.3 re: balderdashy#2070
  0.10.2
  Added tests for router changes in balderdashy@42a0aaf
  Changed "prompt" dependency to use ~
  Fixed typo
  Add "migrate:alter" to test fixture so that it doesn't prompt.
  Don't use _ global in core hook since it can be turned off (Or do `var _ = require('lodash');`)
  Update index.js
  Proposal to fix sails www --prod
* 'master' of https://github.com/balderdashy/sails:
  Better warnings for layout configuration
  Update warnings for layout since we support handlebars
  Update CHANGELOG.md with latest commits
  blueprint create returns 201 status code. fixes balderdashy#2299
  add "created" response method
@macedd
Copy link

macedd commented Dec 2, 2014

sounds good +1

@thomasfr
Copy link

👍
Sails absolutely needs this. My use case was way simpler. I just wanted to share my Models across two projects. One is the content Management and administration Part. The Other one is the frontend project which has other developers. The Models to Access the Database Are the Same. So i want to Share them. Preferable with a Node Module. I think your module System you Propose can do this right?
Any updates in this PR from Sails Core team? Where can we help?
Thanks so far.

@Kikobeats
Copy link
Author

@thomasfr Yes. the main idea of this project is uncouple code and make easy share between other projects.

But unfortunately I'm feeling a little sad because exists a disconnection between the community and sails team. There aren't a public information (like a trello, only a little roadmap) with the enough information about what is sails team is working, the backlog and other important things to account for sails as serious tool.

I have a lot of information about this, examples, and definetly code. So, what's happens?

@isery
Copy link

isery commented Dec 15, 2014

👍

@eversteini
Copy link

You can do it !!!! 👍

@pagrit
Copy link

pagrit commented Dec 15, 2014

sounds good 👍

@gmickel
Copy link

gmickel commented Dec 15, 2014

+1 for this 👍

@albertosouza
Copy link

Current sails.js version already allow plugin features and @sgress454 is improving sails hooks.

See the https://github.com/wejs/we-plugin where where I created a hook to plug features on sails without changing the core system ( sails )

@Kikobeats
Copy link
Author

@albertosouza honestly, IMHO, custom hooks are far away of being a system of plugins

@Josebaseba
Copy link
Contributor

Hooks are great for extend/create some functionalities, they are really useful,for example, to redirect from www to non-www -> https://github.com/Josebaseba/sails-hook-non-www.

But this plugin system is the solution to avoid writing all the time the same as login/signup or whatever. It's a really clean solution to share the controllers/models/routes. I work with this forked sails+plugin system and is amazing the way it works.

But, I think this kind of pull requests are really hard to merge because they have a lot of code to check, I'm sure that sooner or later sails core will add this great work or at least something similar. I totally understand the delay of checking/commenting this pull request, Balderdash is a startup working WITH SailsJS not a startup working FOR SailsJS. So we have to be patient! 👍

@thomasfr
Copy link

In the meantime i used git submodules as a "workaround" because at the moment we just need to share the models.

@mgenev
Copy link

mgenev commented Dec 15, 2014

I absolutely love this. It's still pending a merge??

@KeKs0r
Copy link

KeKs0r commented Dec 16, 2014

Coming from other frameworks and not having much experience with sails. I can only share my theoretical thoughts and show my excitement about this.

I think this is definately going into the right direction. Since it allows not only to organize our own code better, but also to share common functionality. My main use case is user management and authentication, since I am slightly confused with how sails-auth & sails-auth-generate work.

I think it would be way cleaner if authentication is in its own module and only needs to be configured. The module itself can have some kind of default configuration, but the app itself should be able to overwrite it. This also means that modules can provide routes, etc.

I have used Symfony2 (PHP-World) and what they are doing is, that you basically register your modules in an "AppKernel" which is basically the bootstrap file of sails. I think, this could be the right approach. Somehow "registering" our modules, allowing them to add bootstrap logic on their site.

The user module I used in symfony also added some hooks, in order to allow extending the logic, while e.g. a user registers.

I think these components can make modules very loosely coupled and therefore increase reusability within the community.

Another trend I have seen (again comparing with the S2 world, but I think there are concepts that are very sophisticated) is that normal packages, in this case NPM packages, could be wrapped into an sails module. E.g. a module wrapping kue and adding an admin interface and additional integration with the sails ORM.

Also for me those modules are not only for sharing and reusing code, but also organizing. In bigger applications, we might want to coordinate our own code into functional blocks, if we can use these modules within our own app, without adding them as dependency in node_modules, this would also help organizing our own functionality.

Bottomline I am really excited about this, unfortunately there is no deep sails know how, in order to actually help on this.

@rsmoorthy
Copy link

+1 for this!

@hellowin
Copy link
Contributor

I wonder is this concept will be available on Sails Core? If yes, I will change my own modules, hopefully will be open sourced next year, since my modules still not big and complicated.

@loicsaintroch
Copy link
Contributor

@mikermcneil @sgress454 Can you give us an update about this feature since you are both currently working on a subapp hook and a new way to load hooks?

@dan-omniscience
Copy link

+1

@macedd
Copy link

macedd commented Jan 5, 2015

Adding ideas:

I think a plugin should be able to create the same behavior of an app.
Idealy, it would have an api folder which triggers sails behavior
just like an app.

In this scenario not only extensions would be possible, but really
reusable projects and components.

Also would be trivial to refactor code into a plugin/component, thus
being easy to read and maintain by any sails app developer.

2015-01-04 19:46 GMT-02:00, dan-omniscience notifications@github.com:

+1


Reply to this email directly or view it on GitHub:
#2083 (comment)

Thiago Fernandes
Analista de Processos
34 9176- 4055 | thiago@internetbudi.com.br

@NathanRSmith
Copy link

Really nice pull request @Kikobeats. It would be great if this could be integrated soon. I'm needing exactly this functionality to continue using Sails.

@loicsaintroch
Copy link
Contributor

@Kikobeats Can you tell us the differences, the pros and the cons of your pull request instead of using the subapps hook? @sgress454 wrote this hook and it works perfectly.

@albertosouza
Copy link

@loicsaintroch some time ago I created a hook for sails.js ( 0.10.x ) who has this feature and now I'm upgrading to sails.js 0.11.x, link: https://github.com/wejs/we-plugin

the idea is to work as plugins like Drupal, Joomla, Wordpress or Plone.

we-plugin is only a sails module loader with some "fix" in current version to work with sails 0.10.x how will be upgrated to sails 0.11.x and i have one small project working with it in https://novo.atencaobasica.org.br/

I guess you could consider the we-plugin as an option to work with plugins in the sails.js ....

@Kikobeats
Copy link
Author

@loicsaintroch I feel that are very different and for different purpose.

I don't remember now how works @sgress454 hook system, but the idea behind this plugin system is, for each plugin, load a sails tree directory when your lift your application.

Because when I'm working with sails I feel that I have to rewrite the some code in some projects. Wrong. Instead of I want to encapsulate the code, or common parts of code and re-use in different projects, and make easy adapt the code for each specific situation.

On the other hand, I added some features that support they system:

  • A minimal core API for know plugins loaded, blind plugins that depend of other plugins.
  • Because we are defining things in executing time we can do things very powerful as database schema conditional.

I collected all features in a little book that is in spanish only at this moment. If exist people in the fork I can translate it quickly :-) (I should!)

And things that are not directly have relation with the plugin system, but I feel that I need in my workflow:

  • When I put my code in production, my assets are ready, don't need to compile again in the cloud. This is very costly for the production machine and easy to resolve locally.
  • Relative to the previous point, I can convert grunt dependencies into dev-dependencies and make the core build more lightweight (and you can use another workflow for assets, for example with gulp).
  • A better (in my opinion) multilanguage system based in javascript object instead of JSON. Is the same than angular translate.
  • An ecosystem testing system for plugins. The CLI generate all that you need for the testing. You don't need to install the core but you can referenced it.
  • Again, IMHO, if sails is a API-oriented framework is a must support tokens for authentication instead of cookies. I have a little playground for this ideas

and more, more things. I recommend check all sailor repositories.

I don't think that sails team go to integrate this, at this moment is a PR with too many changes, but if exist the enough people interested, we can define a roadmap and prepare sailor for production.

What do you think?

@sgress454
Copy link
Member

@Kikobeats @loicsaintroch @albertosouza I know I'm weighing in on this a little late. The fact is that there is no perfect solution for plugins in Sails, or even a perfect consensus on what "plugins" are. The subapps hook is intended more than anything as a demonstration of how powerful hooks can be in extending Sails without modifying the core.

In coming releases, you'll see more and more core hooks separated into their own modules (the sockets hook is already a separate module in v0.11). Most of the plugin ideas we hear for Sails could (and should) be implemented in v0.11.x as hooks without needing to load whole "slave" Sails apps as both Sailor and subapps do. It's just rare to find a module idea that a) requires bringing the full force of Sails to bear and b) can be made generic enough to act as a plugin to other apps. Building a generic admin panel for Sails apps comes to mind, or a CMS like what @loicsaintroch is working on.

The Sails core team hasn't given Sailor the attention it probably deserved, because it was focused on modifying the core at a time when we were actively trying to make the core leaner and more modular. It also seemed to rely on using the Sails module loader to load the plugin controllers, models, etc. directly into the host Sails app, which in my experience makes writing the plugins difficult because you have to avoid name collisions between the plugin and host (this may not still be how Sailor works, it's just what I recall from the first pass). Now that Sailor is using a forked Sails, it's hard for us to actively support it, as much as we appreciate @Kikobeats and his contributions to Sails. Ideally the best ideas from Sailor could be merged back into Sails or (better) converted to hooks, making the framework stronger. We're all after the the same thing, after all: making life easier and more fun for Node developers. 👬👭👫

@albertosouza
Copy link

@sgress454 thanks for this comment

And I am working to create a blogging and site generator with we.js and an admin like Wordpress but with SPA structure ... I think it may be related to the @loicsaintroch project. I will try to talk to him

@mikermcneil
Copy link
Member

Thanks for the great discussion everyone.

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

Successfully merging this pull request may close these issues.

None yet