Git Product home page Git Product logo

rfcs's Introduction

Revel Framework

Build Status License Go Report Card

A high productivity, full-stack web framework for the Go language.

Current Version: 1.1.0 (2022-04-11)

Supports go.mod package management

Quick Start

Install Revel:

go install github.com/revel/cmd/revel@latest

Create & Run your app:

revel new -a my-app -r

Open http://localhost:9000 in your browser and you should see "It works!"

Community

Learn More

Contributing

Contributors

rfcs's People

Contributors

brendensoares avatar

Watchers

 avatar  avatar  avatar

rfcs's Issues

New repository

Not sure where to ask this... so why not here, @brendensoares Can you create a new repository called revel-manifest ?

The idea behind this is that you can use a tool like https://code.google.com/p/git-repo/ or https://github.com/notzippy/repo-tool to clone multiple repositories at once at a specific version. The manifest file contains a tag or branch, value matching the same as the one the manifest is on. This allows the repo tool to pull all repositories based on the specific tag

Several different usage scenarios would exist for this.

  1. A revel developer who wants to work on a specific branch (remember we were going to try multiple feature branches) - one command would check all these projects into a src folder that you can point your gopath at
  2. A web developer wanting to retrieve and stick to a consistent version of revel.
  3. A revel documenter, wanting to work on documentation for specific branches

Typical usage commands would be

repo init -u [email protected]:notzippy/revel-manifest.git -b master -g all

Fetch projects labeled with the "all" group on branch master

or

repo-tool init -u [email protected]:notzippy/revel-manifest.git -b develop -g core,docs

Fetch projects labeled with the "core" or "docs" group on branch develop

The advantage of this compared to using subprojects or submodules is that you can place the checked out project on a specific path, also you can lean down the checkout by specifying a depth attribute for the clone (makes clone process faster). I started a default manifest for revel here
https://github.com/notzippy/revel-manifest/blob/master/default.xml

Proposal: Implement Revel as a set of loosely coupled tools.

This is a discussion of #2 and #4.

Current Architecture

Earlier I illustrated the architecture of Revel Framework along with my
misunderstanding of what "convention" means
. To summarize what was said:

  • Revel Framework consists of two parts:
    • revel/cmd package implements a command line tool for:
      • bootstrapping your applications;
      • recompilation and hot reloading them;
      • generation of main.go file;
      • generation of reverse routes;
      • starting integration tests;
      • building applications;
      • packaging them.
    • revel package is responsible for everything else:
      • parsing of configuration files;
      • parsing of routes (on every start of the app);
      • execution of templates;
      • allocation and start of http.Server;
      • allocation and initialization of all kinds of parameters for:
        • Session
        • Cache
        • Flash
        • Validation
      • value binding;

Drawbacks

  • User app is tightly coupled with the revel package and if they don't like something
    about its implementation aspects they have to ask about making changes to the core code base.
    • With this approach it is not possible to:
      1. Stop embedding revel.Controller;
      2. Do not use revel.Result;
      3. Use custom configuration format of choice;
      4. Use alternative implementation of router;
      5. Replace template package;
  • Slow releases: large code base that cannot be broken without releasing the next version, the next version cannot be released till enough changes are made;
  • A lot of unnecessary for a user dependencies;
  • The framework is difficult to maintain: monolithic codebase, expected behaviour of its components is not defined:
    • when user embeds revel.Controller instead of *revel.Controller, it works but shows Error 500, we have to guess whether it is by design or a bug.

Mechanisms of Extendability

  • Startup hooks - implemented as a global revel.OnAppStart variable of type []func() (now a bit more complex than that to support ordering);
  • Interceptors - similar to Startup Hooks but for special Actions that can be started Before, ..., After regular Actions.
  • Filters - similar to Startup Hooks but work on a level of HTTP Handler function.
Drawbacks
  • Too many ways to define middleware: interceptor functions vs. interceptor methods vs. filters;
  • No way for a middleware to share something with controllers, for Session, Cache, Flash, Validation this is
    solved by manually adding respective fields
    to the revel.Controller{} struct.
    • I.e. for some new middleware CSRF that means that in order to share CSRFToken with actions of controllers
      there are a few options:
      1. Add one more field to the revel.Controller{} struct;
      2. Pass the value using some field that already exists:
        • c.Controller.Session, c.Controller.Flash (not always possible, e.g. I may want to pass int type or some struct; and requires an allocation of a map even if I don't need a session but just a single variable of scalar type).

Default Layout

  • app/
    • controllers/ - imports revel package;
    • routes/ - imports revel package. Automatically generated by revel/cmd and cannot be commited due to .gitignore;
    • tmp/ - entry point of the application,
      imports: revel, revel/testing, revel/modules/*, controllers, tests.
      Automatically generated by revel/cmd and cannot be commited due to .gitignore;
    • views/
  • conf/
    • app.conf - INI configuration file;
    • routes - a list of routes in a custom format inspired by Play Framework;
  • messages/
  • public/
  • tests/ - integration tests that can be run by revel/cmd.
Drawbacks
  • app/ directory is a rudiment of Play Framework 1 that was written in Java where it is a regular
    practice to have a lot of nested directories. Go projects may have a more flat structure;
  • app/tmp/ and app/routes/ are not part of user app (they are in .gitignore), thus builds are not reproducable and revel/cmd is required to build/start an app;
  • conf/routes is not type safe, validated at start time, changes at the stage when the project has already been compiled
    lead to unexpected result (Solutions TBD);
  • tests/ cannot be run without revel/cmd.

Sample Code

  • app/controllers/init.go
package controllers

import (
	"github.com/revel/revel"
)

func init() {
	revel.Filters = []revel.Filter{
		...
		MyCustomFilter(*revel.Controller, fc []revel.Filter) {
			// Do something.
			...
			fc[0](c, fc[1:])
		}
	}
}
  • app/controllers/app.go
package controllers

import (
	"github.com/revel/revel"
)

type App struct {
	*revel.Controller
}

func (c *App) Before() revel.Result {
	return nil
}

func (c *App) Index() revel.Result {
	return c.Render()
}

func (c *App) After() revel.Result {
	return nil
}

func init() {
	revel.InterceptMethod((&App{}).Before, revel.BEFORE)
	revel.InterceptMethod((&App{}).After, revel.AFTER)

	revel.OnAppStart(func() {
		// Initialize the app at start-up time.
	})
}

Proposal

Tools

Implement everything that Revel provides as a set of independent tools.
Every single one should be usable on its own:

  • harness/ - hot reloads user applications;
    • main.go - entry point of the tool so it can be used independently;
    • runner/ - package that implements some Handler interface;
  • bootstrap/ - generates a new application from a specified skeleton;
    • main.go - entry point of the tool so it can be used independently;
    • creator/ - package that implements some Handler interface;
  • handler/ - generates standard handler functions from Revel-like controllers;
    • ...
  • routes/ - generates type safe reverse routes;
    • ...
  • TBD

Turn revel package into a simple command that imports the tool packages and starts their Handler functions.

Generated Code

Generated code should not depend on revel or any other projects if it's not possible to replace
the dependency easily. The standard library should be relied on as much as possible.

New Definition of Action

Any method that returns a type implementing standard http.Handler interface
as a first argument is an Action.

func (c *App) Index(...) http.Handler {
	return c.Render()
}
New Definition of Controller

Any struct type that has actions is a controller.

// App is a controller.
type App struct {
}

func (c *App) Index(...) http.Handler {
	return c.Render()
}

// Smth isn't.
type Smth struct {
}
Extendability
Special Actions
  • Before is a special action that will be executed before every regular action.
  • After is an equivalent of Before but with a finally semantics meaning that it will be guaranteed
    to be started after every regular action.
Binding

Actions can bind not only built-in types (int, uint, string, float32, etc.) but also http.Request,
types implementing http.ResponseWriter interface, and any other types that have a special
Bind(url.Values) method (TBD).

That would allow us to use the Special Actions as Filters (in Revel's terminology):

func (c *App) Before(w http.ResponseWriter, r *http.Request) http.Handler {
	http.SetCookie(w, c.cookieByRequest(r))
	return nil
}
Embedding of Controllers
  • Proposed controllers/init.go
type Controller struct {
	... // Third party controllers are here.
	*MyCustomController
}

type MyCustomController struct {
}

func (c *MyCustomController) Before(page int) http.Handler {
	// Do something.
}
  • Proposed controllers/app.go
type App struct {
	*Controller
}

func (c *App) Before() http.Handler {
	...
}

func (c *App) Index() http.Handler {
	return c.Render()
}

func (c *App) After() http.Handler {
	...
}
Drawbacks
  • Use of anonymous embedding requires every Controller to have a unique name. Solutions TBD.
  • Controller auto allocation rules TBD.
Startup Hooks

Functions for running some controller's code after init() but before the app is started
are still needed. One of the options is to have a special Init function reserved for that:

func Init() {
}

Or alternatively, auto start any function that is:

  • exported;
  • returns an error.

Details TBD.

func SomeFunctionInControllersPkg() error {
	return nil
}
New Layout
  • assets/ - autogenerated assets;
    • handlers/ - handler functions generated from controllers;
    • routes/ - reverse routes;
  • config/
  • controllers/ - controllers are splitted into independent packages (TBD);
    • account/
    • profile/
    • smthelse/
  • routes/ - imports assets/handlers (TBD);
  • static/
  • views/
  • main.go - entry point of the application, imports net/http, routes, and starts web-server;
  • revel.ini - configuration of the project: how to build it, run, and package. If not presented,
    default settings (by convention) will be used.
Motivation
  • go run is everything that is needed for start of the project;
  • User does not depend on revel package, only on the standard library;
  • Every single aspect of the app is customizable;

Other ideas

More value for the end users
  • Bring support of Meteor.js style rpc-publish-subscribe and data synchronization out of the box;
  • WebAssembly is coming. Think over how Revel can be used for isomorphic
    development in Go (i.e. the same language for both client and server side).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.