Showing preview only (617K chars total). Download the full file or copy to clipboard to get everything.
Repository: revel/revel
Branch: master
Commit: b05317527954
Files: 132
Total size: 583.7 KB
Directory structure:
gitextract_z1a8uhvh/
├── .codebeatsettings
├── .gitignore
├── .travis.yml
├── AUTHORS
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── before_after_filter.go
├── before_after_filter_test.go
├── binder.go
├── binder_test.go
├── cache/
│ ├── cache.go
│ ├── cache_test.go
│ ├── init.go
│ ├── inmemory.go
│ ├── inmemory_test.go
│ ├── memcached.go
│ ├── memcached_test.go
│ ├── redis.go
│ ├── redis_test.go
│ ├── serialization.go
│ └── serialization_test.go
├── compress.go
├── compress_test.go
├── conf/
│ └── mime-types.conf
├── controller.go
├── controller_type.go
├── errors.go
├── event.go
├── event_test.go
├── fakeapp_test.go
├── field.go
├── filter.go
├── filterconfig.go
├── filterconfig_test.go
├── flash.go
├── go.mod
├── go.sum
├── http.go
├── i18n.go
├── i18n_test.go
├── intercept.go
├── intercept_test.go
├── invoker.go
├── invoker_test.go
├── libs.go
├── libs_test.go
├── logger/
│ ├── composite_multihandler.go
│ ├── doc.go
│ ├── handlers.go
│ ├── init.go
│ ├── init_test.go
│ ├── log_function_map.go
│ ├── logger.go
│ ├── revel_logger.go
│ ├── terminal_format.go
│ ├── utils.go
│ └── wrap_handlers.go
├── logger.go
├── model/
│ ├── revel_container.go
│ ├── revel_controller.go
│ ├── revel_paths.go
│ └── revel_unit.go
├── module.go
├── namespace.go
├── panic.go
├── params.go
├── params_test.go
├── results.go
├── results_test.go
├── revel.go
├── revel_hooks.go
├── revel_test.go
├── router.go
├── router_test.go
├── server-engine.go
├── server.go
├── server_adapter_go.go
├── server_test.go
├── session/
│ ├── init.go
│ ├── session.go
│ ├── session_cookie_test.go
│ └── session_test.go
├── session_adapter_cookie.go
├── session_engine.go
├── session_filter.go
├── template.go
├── template_adapter_go.go
├── template_engine.go
├── template_functions.go
├── templates/
│ └── errors/
│ ├── 403.html
│ ├── 403.json
│ ├── 403.txt
│ ├── 403.xml
│ ├── 404-dev.html
│ ├── 404.html
│ ├── 404.json
│ ├── 404.txt
│ ├── 404.xml
│ ├── 405.html
│ ├── 405.json
│ ├── 405.txt
│ ├── 405.xml
│ ├── 500-dev.html
│ ├── 500.html
│ ├── 500.json
│ ├── 500.txt
│ └── 500.xml
├── testdata/
│ ├── app/
│ │ └── views/
│ │ ├── footer.html
│ │ ├── header.html
│ │ └── hotels/
│ │ └── show.html
│ ├── conf/
│ │ ├── app.conf
│ │ └── routes
│ ├── i18n/
│ │ ├── config/
│ │ │ └── test_app.conf
│ │ └── messages/
│ │ ├── dutch_messages.nl
│ │ ├── english_messages.en
│ │ ├── english_messages2.en
│ │ └── invalid_message_file_name.txt
│ └── public/
│ └── js/
│ └── sessvars.js
├── testing/
│ ├── testsuite.go
│ └── testsuite_test.go
├── util.go
├── util_test.go
├── utils/
│ ├── simplestack.go
│ └── simplestack_test.go
├── validation.go
├── validation_test.go
├── validators.go
├── validators_test.go
├── version.go
└── watcher.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .codebeatsettings
================================================
{
"GOLANG": {
"ABC":[15, 25, 50, 70],
"BLOCK_NESTING":[5, 6, 7, 8],
"CYCLO":[20, 30, 45, 60],
"TOO_MANY_IVARS": [15, 18, 20, 25],
"TOO_MANY_FUNCTIONS": [20, 30, 40, 50],
"TOTAL_COMPLEXITY": [150, 250, 400, 500],
"LOC": [50, 75, 90, 120]
}
}
================================================
FILE: .gitignore
================================================
tmp/
routes/
test-results/
revel/revel
# editor
*.swp
.idea/
*.iml
================================================
FILE: .travis.yml
================================================
language: go
go:
- "1.13.x"
- "1.14.x"
- "1.15.x"
- "tip"
os:
- linux
- osx
- windows
sudo: false
branches:
only:
- master
- develop
services:
# github.com/revel/revel/cache
- memcache
- redis-server
before_install:
# TRAVIS_OS_NAME - linux and osx
- echo $TRAVIS_OS_NAME
- echo $PATH
- |
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
brew update && brew install memcached redis && brew services start redis && brew services start memcached
fi
- |
if [[ "$TRAVIS_OS_NAME" != "windows" ]]; then
redis-server --daemonize yes
redis-cli info
else
# redis-server.exe
# redis-cli.exe info
echo $PATH
fi
install:
# Setting environments variables
- export PATH=$PATH:$HOME/gopath/bin
- export REVEL_BRANCH="develop"
- 'if [[ "$TRAVIS_BRANCH" == "master" ]]; then export REVEL_BRANCH="master"; fi'
- 'echo "Travis branch: $TRAVIS_BRANCH, Revel dependency branch: $REVEL_BRANCH"'
- git clone -b $REVEL_BRANCH git://github.com/revel/modules ../modules/
- git clone -b $REVEL_BRANCH git://github.com/revel/cmd ../cmd/
- git clone -b $REVEL_BRANCH git://github.com/revel/config ../config/
- git clone -b $REVEL_BRANCH git://github.com/revel/cron ../cron/
- git clone -b $REVEL_BRANCH git://github.com/revel/examples ../examples/
- go get -t -v github.com/revel/revel/...
script:
- |
if [[ "$TRAVIS_OS_NAME" != "windows" ]]; then
go test -v github.com/revel/revel/...
else
go test -v github.com/revel/revel/.
fi
matrix:
allow_failures:
- go: tip
- os: windows
================================================
FILE: AUTHORS
================================================
# TODO Revel Framework Authors Information
================================================
FILE: CHANGELOG.md
================================================
# CHANGELOG
## v1.1.0
[[revel/revel](https://github.com/revel/revel)]
* bc0e27f Merge pull request #1552 from revel/1542_recursive_call
* d202b93 Merge pull request #1448 from golddranks/master
* 859e5b4 Merge pull request #1511 from jiro4989/patch-1
* 90489b1 Merge pull request #1523 from KoichiWada/handle-sigterm
* 347610c Merge branch 'develop' into handle-sigterm
* 942bd2e Merge pull request #1525 from mikyk10/feature/adding-mime-compress
* 2cb950f Merge pull request #1543 from dhiemaz/fixing-typos
* aed0d1e Merge pull request #1550 from revel/bugfix/readme-install
* bc89379 Fixed log recursive call There was a recusive loop in the logger, this fixes it closes #1542
* aa8a94d Merge pull request #1546 from braineet/master
* 741d2c8 Corrects install command for new version of go
* 65db3c0 Merge pull request #1549 from revel/bugfix/session-uuid
* 624f341 Merge branch 'develop' into bugfix/session-uuid
* 5e99db8 Refactors session uuid to use google's package
* 30f3424 Correction redis import
* f934412 fixing typos
* f261091 Added a MIME type for compressableMimes
* 9907376 Handle SIGTERM for graceful shutdown.
* 413dda3 Merge pull request #1518 from ptman/lint
* 45a4413 More lint fixes and dead code removal
* 3799c55 Merge pull request #1514 from ptman/lint
* 655927a Removed go 1.12 added go 1.15
* 060e640 Fix misspellings and some lint errors
* 921e1b4 Fix URL (http -> https)
* e12cd0e develop v1.1.0-dev
* a29f37c Fix a bug when binding to pointer
[[revel/cmd](https://github.com/revel/cmd)]
* 86b4670 Update README.md
* bc376fb Merge pull request #211 from lujiacn/develop
* 5c8ac53 Merge branch 'develop' into develop
* c674084 Merge pull request #201 from ptman/lint
* cfe1d97 Merge branch 'develop' into lint
* 7f9f658 Merge pull request #209 from glaslos/patch-1
* 126d20c Merge pull request #210 from revel/build_process_update
* 111264c updated go.mod
* 4087c49 updated golang.org/x/tools, to avoid internal error: package xxx without types was imported from ...
* 3602eb4 Merge branch 'develop' into patch-1
* 192fc66 Merge pull request #200 from julidau/develop
* 5689f86 Merge pull request #204 from shinypb/master
* 6dba0c3 Fix bad error syntax An wrapped error message in the cmd module was referencing the wrong parameter value to be built closes revel/revel#1532
* bb926f3 Added additional pattern to test against Another different missing pacakge error thrown that can be detected and added This error occurs because a package may have been stripped down when originally loaded
* 3cd5ebb Updated launch scripts
* 25dc05b Updated Launch code Added output to error stack, so terminal errors are displayed Ficed c.Verbose, it was changed to an array which causes issues launching Removed . notation from doing anything special. This was already replaced with the -p CLI option Added documentaiton on adding the package name Started watcher with force refresh.
* 0a40a20 Merge pull request #208 from notzippy/build_process_update
* fcc1319 Fixing type
* ea5acb7 Updated shared build environments Updated check for errors. Updated go.mod Added .vscode launch
* 25d6352 Get rid of redundant space in the output of `revel new -a`
* ddec572 More linting
* 7a91d0c interrupt process on windows as well
* b562bd2 Merge pull request #199 from ptman/lint
* bf17a71 Merge branch 'develop' into lint
* 3d924a0 Lint fixes
* d64c7f1 develop v1.1.0-dev
[[revel/config](https://github.com/revel/config)]
* no changes
[[revel/modules](https://github.com/revel/modules)]
* 852ea71 Merge branch 'master' into develop
* 789324e Merge pull request #105 from ptman/lint
* 0a9a7f4 Update .travis.yml
* 8dbd171 Lint fixes
* 464e072 develop v1.1.0-dev
[[revel/cron](https://github.com/revel/cron)]
* 031e64e Merge pull request #5 from ptman/lint
* 7cfc261 Lint fixes
[[revel/examples](https://github.com/revel/examples)]
* e51ed5a Merge pull request #54 from realbucksavage/master
* c555714 Merge pull request #58 from teitei-tk/update_gorp_link
* 24ed869 Merge pull request #62 from ptman/lint
* 5ec13f7 More lint fixes
* b14432a Merge branch 'develop' into lint
* a33af07 Merge pull request #59 from obsti8383/master
* 46ae3a6 Removed go 1.12, added 1.15
* ced52ae Lint and revel 1.0.0 fixes
* b245628 revert to old go.sum
* b6ea1ec upgrade references to revel framework to 1.0.0 in go.mod; fixes compile errors
* 8353712 update gorp link
* be755f5 Indented the code.
[[revel/revel.github.io](https://github.com/revel/revel.github.io)]
* 32d1cd9 Merge branch 'master' into develop
* e72f206 Merge pull request #197 from revel/update_go_version
* 1ae75e8 Merge branch 'develop' into update_go_version
* 75172ea develop v1.1.0-dev
[[revel/heroku-buildpack-go-revel](https://github.com/revel/heroku-buildpack-go-revel)]
* no changes
## v1.0.0
[[revel/revel](https://github.com/revel/revel)]
* 3d1b0c3 Merge pull request #1497 from lujiacn/master
* ff2da7e Merge pull request #1498 from aacapella/feature/same-site-cookies
* c6c4c35 SameSite cookie support
* bfad570 Update server_adapter_go.go
* ff43c73 Merge pull request #1491 from notzippy/go-mod
* dbe9fee Update .travis.yml
* 38b0687 Fixed paths for test cases
* 39523bf Enhanced logging
* 59b8375 Changes to Revel for go.mod support Modified module lookup to handle lookups using the app.conf (before relied on source file) Added extra logging for routes error handling
* 1053f49 Merge pull request #1443 from lujiacn/develop
* dcafb9e Merge pull request #1488 from notzippy/go-mod
* e30c8da Merge pull request #1483 from goevexx/feature/fix-issue-1482
* 50e70f9 Updated revel to receive paths passed in Updated watcher to use master branch
* d581f71 change import to fix issue 1482
* fdc724a Merge pull request #1462 from torden/feature/fix_puretextstrict
* fe4861c Fix (#1458) the undetected self-closing tags in isPureTextStrict Fix (#1458) the always uses STRICT mode in PureText.IsSatisFied
* ae3895a added wasm mime-type
* 45ec814 Merge pull request #1439 from mukeshjeengar/hotfix/log-rotation-fixed
* d3a76ed log rotation fixed
* 2eb9067 Merge pull request #1413 from nevkontakte/patch-1
* ccf085e Merge pull request #1434 from dmjones/fix-1433
* 34e886a Don't invoke action when Before returns value
* 5b70626 Merge pull request #1427 from SYM01/hotfix/avoid-dos
* d160ecb fix issue #1424
* db7db5b remove unneccsary code assignment to nil
* 8bff5bb Update controller.go
* 16f5fef Remove a stray println call.
* 60c3d7a develop v1.0.0-dev
[[revel/cmd](https://github.com/revel/cmd)]
* d8117a3 Merge pull request #186 from notzippy/go-mod
* 6371373 Removed version update Version control is maintained through go.mod file Modified harness to only kill the application if not responded after 60 seconds in windows
* 28ac65f Merge pull request #185 from notzippy/go-mod
* 5070fb8 Fixed issue with new and run flag Updated tests to run final test in non gopath, with new name
* 904cfa2 Added some informational messages while download
* 223bd3b Added manual scan on packages in app folder This allows for source code generation. Packages in <application>/app folder are scanned manually as opposed to the `packages.Load` scan which will fast fail on compile error, and leave you with go files with no syntax.
* 4987ee8 Added verbose logging to building / testing a no-vendor app Removed section which raises an error when examining packages, we dont need to check for errors on foreign packages since we are importing only a slice of the data
* 4bab440 Updated Revel command Added a check to see if harness had already started, saves a recompile on load Added check to source info for local import renames Removed the go/build check for path and just check existence of the path Formatting updates
* 741f492 Updated scanner Removed scanning all the import statements, this is not needed Added recursive scan for revel import path to pick up testunits
* 60b88a4 Merge pull request #180 from notzippy/go-mod
* 49eef29 Build and Historic build updates Modified GOPATH to not modify build with go.mod Updated go.mod to version 1.12 Updated harness to setup listener before killing process Updated notvendored flag to --no-vendor Updated command_config to ensure no-vendor can be build Added additional checks in source path lookup
* 9d3a554 Updates Updated NotVendored flag Updated travis matrix Updated build log
* 36bd6b9 Corrected flags
* 1d9df25 Moved test cases to run last
* ad694c0 Debug travis
* fb4b565 Debug travis Added verbose flag so we can see what is occurring, Removed checkout for revel, not needed anymore
* 20d5766 Added gomod-flags Added a gomod-flags parameter which allows you to run go mod commands on the go.mod file before the build is performed. This allows for development environments.
* 0920905 Updated to build go 1.12 and up Modified to use fsnotify directlyUpdated travis to not use go deps
* 31cb64e Check-in of command_test, remaps the go mod command to use the develop branch.
* 33abc47 Fixed remaining test
* 86736d6 Updated formating Ran through testing individually for vendored Revel applications
* 07d6784 Restructured command config Removed go/build reference in clean
* c1aee24 Corrected version detection, so that equal versions match
* f2b54f5 Updated sourceinfo Added packagepathmap to the SourceInfo, this in turn allows the RevelCLI app command to pass the source paths directly to Revel directly Added default to build to be "target" of the current folder Renamed source processor
* 3f54665 Added processor to read the functions in the imported files, and populate the SourceInfo object the same as before
* 548cbc1 Upatede Error type to SourceError Added processor object to code Verified compile errors appearing Signed-off-by: notzippy@gmail.com
* 9a9511d Updated so revel new works and revel run starts parsing the source.
* acb8fb6 Initial commit to go mod
* d201463 Merge pull request #176 from xXLokerXx/fix_windows_path
* 773f688 Merge branch 'develop' into fix_windows_path
* ca4cfa5 Merge pull request #165 from kumakichi/fixed_import_C
* 4368690 Merge pull request #179 from Laur1nMartins/Laur1nMartins/fix-linkerFlags
* cf2e617 Merge branch 'develop' into Laur1nMartins/fix-linkerFlags
* 424474a Fix linker flags inclusion in build comamnd
* 6d8fcd9 Fix sintax error
* aa459c1 Fix sintaxis error
* 0b23b3e Fix complexity
* 3f65e1e acept slash and inverted slash in src path validation
* 7dce3d8 fixed import "C"
* 5c8d5bc develop v1.0.0-dev
[[revel/config](https://github.com/revel/config)]
* no changes
[[revel/modules](https://github.com/revel/modules)]
* e1fdc01 Merge pull request #103 from revel/master
* 80d53e2 Merge pull request #102 from notzippy/go-mod
* 2048fce Updated build processor
* 19728d3 Added gomod removed vendor specific imports
* 515369e develop v1.0.0-dev
[[revel/cron](https://github.com/revel/cron)]
* no changes
[[revel/examples](https://github.com/revel/examples)]
* 2d2968c Merge pull request #57 from notzippy/go-mod
* dc75997 Updated examples Updated booking app to go.mod Updated chat, facebook, others app to add in go file in the root Updated travis to run tests in windows Updated travis to exclude testing fasthttp on windows
* 5b25a51 Removed persona from project, this function no longer exists in browsers
[[revel/revel.github.io](https://github.com/revel/revel.github.io)]
* 6cd3647 Merge pull request #196 from aacapella/feature/same-site-cookies
* 9f8f537 Merge pull request #191 from dmjones/session-value-not-found-returns-error
* d79c912 Merge pull request #194 from DGKSK8LIFE/patch-1
* 3911471 Merge pull request #195 from notzippy/develop
* 67b088f Same site cookie setting
* f5c5cb0 Corrected issues
* bba502d Update for gomod docs
* 9765ef0 Merge remote-tracking branch 'revel/master' into develop
* eedc235 fixed spelling error
* 9b9270a Explain return value when session value not found
* 24abe9a Merge pull request #184 from manfordbenjamin/master
* 4969200 Change logo and apply blue theme style to all pages
* 844fe5d Revamp homepage
* ef54af7 develop v1.0.0-dev
[[revel/heroku-buildpack-go-revel](https://github.com/revel/heroku-buildpack-go-revel)]
* no changes
## v0.21.0
### New items
* **Session Engine support** You can now define your own session engine. By default the cookie engine is the one used for the session, but with this change you can implement your own. Also controller.Session is now a `map[string]interface{}` which means you can place any object that can be serialized to a string.
* **Added http mux support** you can now integrate Revel with packages that have their own HTTP muxers. This allows you to integrate with Hugo, Swagger or any suite of software that is out there.
* `revel.controller.reuse` app.conf option to turn on / off reuse of the controllers. Defaults to true
### Breaking changes
controller.Session is now a map[string]interface{} (previously was map[string]string) this means existing code needs to cast any values they pull from the session
```
if username, ok := c.Session["user"]; ok { // username is a string type
return c.getUser(username)
}
```
changes to
```
if username, ok := c.Session["user"]; ok { // username is an interface{} type
return c.getUser(username.(string))
}
```
Deprecated log points removed
revel.ERROR, revel.TRACE, revel.DEBUG, revel.WARN have been removed
Function name change `revel.OnAppStop` Replaced revel.OnAppShutdown
revel.SimpleStack was moved to github.com/revel/revel/utils.SimpleStack
### New packages required
#### Revel Framework
* github.com/twinj/uuid (revel/revel session ID generation)
#### Revel Cmd
* github.com/kr/pty (revel/cmd capture output of dep tool)
### Package changes
#### Revel Framework
* Added stack to errors Added stack information to router when forumlating error
* Fix spelling errors from go report
* Removed deprecated loggers
* Updated travis , made windows success optional
* Exposed StopServer function to public Changed session filter to use empty call
* 577ae8b Enhancement pack for next release Added session engine support, and the session cookie engine breaking change revel.Session was map[string]string now is map[string]interface{}
* Updated shutdown to support windows environment
* Patched shutdown support to make it work through the event engine
* Added ENGINE_SHUTDOWN_REQUEST to events, raising this event will cause the server to shutdown
* Assigned Server engines to receive revel.Events Added revel.OnAppStop hooks -
* Normalized startup / shutdown hooks into their own package
#### Revel Cmd
* Modified run command to translate symlinks to absolute paths
* Tool updates Updated tool to give more meaningful errors
* Added file system as an option to fetch the skeleton from
* Allow windows to fail on travis
* run Command will choose CWD if no additional arguments are supplied
* Added Revel version check, compatible lists are in model/version
#### Revel Modules
* Updated CSRF test cases
* Added travis test for modified test engine
* Updated server-engine modules to support OnAppStop functionality.
* Reorganization, readme updates Moved auth example into its own folder
* Updated root readme
* Updated CSRF
#### Revel Examples
* Fixed issue with error checking closes websocket in chat
* Updated booking module to work with changed session
* Updated to remove any references to old revel.log variables
## v0.20.0
### New items
* **Updated minimum Go requirements to Go 1.8**
#### Revel Cmd changes
See [manual](http://revel.github.io/manual/tool.html) for more information on the flags and new features
* Rewrote revel/cmd package so it has no dependencies on revel/revel - future releases for revel/cmd will not be on the same schedule as revel/revel
* Added flag support to revel/cmd ,
* Added automatic vendor creation flag, when enabled a vendor folder will be added and used to the project
* Added [DEFAULT] section to message skeleton so multiple lines work
* If port specified is 0 then proxy can will run on a random free port
* Split main file into two files so it may be invoked using other methods
* Added ability to only monitor and update build files (without running a proxy)
* Auto download revel/revel, revel/cmd - if you point your GOPATH to an empty directory the `revel` tool will still be able to create a new project
* Made application path smarter. Now supports absolute paths and relatives paths.
* modified `revel run` to pass in json code to the `revel.Init` function in place of the `mode` field. This allows for dynamic information to be passed to the Revel Server. This can be disabled by using this flag ` --historic-run-mode`
* modified `revel new` added `-V` to auto create the vendor folder inside the application along with the Gopkg.toml file.
* modified `revel new` added `-r` to run the application after creating it.
### revel/module
* Enhanced gorp module to make database schema available
* Added server-engine server-engine/gohttptest and a testsuite file that implements all the methods from revel/testsuite. This test engine as an alpha implementation to be able to run `go test` from the command line. Meaning your tests can now live beside the controller. An example testsuite is [here](https://github.com/revel/examples/blob/develop/booking/app/controllers/app_test.go)
* Updated static module, moved a structure into a model package
### revel/revel
* Added startup fail REVEL_FAILURE event
* Added HTTP_REQUEST_CONTEXT fetch
* Added websockets Message.Send / Message.Receive functions.
* Enhanced required validator
* Graceful shutdown added. By doing a `revel.RaiseEvent()`
#### Breaking Changes
* `Request.Context() map[string]interface()` renamed to
`Request.Args() map[string]interface()`
new function added which returns the request.Context for the server-engine that supports it
`Request.Context() context.Context`
`revel.EventHandler` signature was `func(typeOf int, value interface{}) (responseOf int)`
now it is func(typeOf Event, value interface{}) (responseOf EventResponse)
## v0.19.1
Fix import to point to the fsnotify/fsnotify.v1
## v0.19.0
# Maintenance Release
This release is focused on improving the security and resolving some issues.
**There are no breaking changes from version 0.18**
[[revel/cmd](https://github.com/revel/cmd)]
* Improved vendor folder detection revel/cmd#117
* Added ordering of controllers so order remains consistent in main.go revel/cmd#112
* Generate same value of `AppVersion` regardless of where Revel is run revel/cmd#108
* Added referrer policy security header revel/cmd#114
[[revel/modules](https://github.com/revel/modules)]
* Added directory representation to static module revel/modules#46
* Gorp enhancements (added abstraction layer for transactions and database connection so both can be used), Added security fix for CSRF module revel/modules#68
* Added authorization configuration options to job page revel/modules#44
[[revel/examples](https://github.com/revel/examples)]
* General improvements and examples added revel/examples#39 revel/examples#40
## v0.18
## Upgrade path
The main breaking change is the removal of `http.Request` from the `revel.Request` object.
Everything else should just work....
## New items
* Server Engine revel/revel#998
The server engine implementation is described in the [docs](http://revel.github.io/manual/server-engine.html)
* Allow binding to a structured map. revel/revel#998
Have a structure inside a map object which will be realized properly from params
* Gorm module revel/modules/#51
Added transaction controller
* Gorp module revel/modules/#52
* Autorun on startup in develop mode revel/cmd#95
Start the application without doing a request first using revel run ....
* Logger update revel/revel#1213
Configurable logger and added context logging on controller via controller.Log
* Before after finally panic controller method detection revel/revel#1211
Controller methods will be automatically detected and called - similar to interceptors but without the extra code
* Float validation revel/revel#1209
Added validation for floats
* Timeago template function revel/revel#1207
Added timeago function to Revel template functions
* Authorization to jobs module revel/module#44
Added ability to specify authorization to access the jobs module routes
* Add MessageKey, ErrorKey methods to ValidationResult object revel/revel#1215
This allows the message translator to translate the keys added. So model objects can send out validation codes
* Vendor friendlier - Revel recognizes and uses `deps` (to checkout go libraries) if a vendor folder exists in the project root.
* Updated examples to use Gorp modules and new loggers
### Breaking Changes
* `http.Request` is no longer contained in `revel.Request` revel.Request remains functionally the same but
you cannot extract the `http.Request` from it. You can get the `http.Request` from `revel.Controller.Request.In.GetRaw().(*http.Request)`
* `http.Response.Out` Is not the http.Response and is deprecated, you can get the output writer by doing `http.Response.GetWriter()`. You can get the `http.Response` from revel.Controller.Response.Out.Server.GetRaw().(*http.Response)`
* `Websocket` changes. `revel.ServerWebsocket` is the new type of object you need to declare for controllers
which should need to attach to websockets. Implementation of these objects have been simplified
Old
```
func (c WebSocket) RoomSocket(user string, ws *websocket.Conn) revel.Result {
// Join the room.
subscription := chatroom.Subscribe()
defer subscription.Cancel()
chatroom.Join(user)
defer chatroom.Leave(user)
// Send down the archive.
for _, event := range subscription.Archive {
if websocket.JSON.Send(ws, &event) != nil {
// They disconnected
return nil
}
}
// In order to select between websocket messages and subscription events, we
// need to stuff websocket events into a channel.
newMessages := make(chan string)
go func() {
var msg string
for {
err := websocket.Message.Receive(ws, &msg)
if err != nil {
close(newMessages)
return
}
newMessages <- msg
}
}()
```
New
```
func (c WebSocket) RoomSocket(user string, ws revel.ServerWebSocket) revel.Result {
// Join the room.
subscription := chatroom.Subscribe()
defer subscription.Cancel()
chatroom.Join(user)
defer chatroom.Leave(user)
// Send down the archive.
for _, event := range subscription.Archive {
if ws.MessageSendJSON(&event) != nil {
// They disconnected
return nil
}
}
// In order to select between websocket messages and subscription events, we
// need to stuff websocket events into a channel.
newMessages := make(chan string)
go func() {
var msg string
for {
err := ws.MessageReceiveJSON(&msg)
if err != nil {
close(newMessages)
return
}
newMessages <- msg
}
}()
```
* GORM module has been refactored into modules/orm/gorm
### Deprecated methods
* `revel.Request.FormValue()` Is deprecated, you should use methods in the controller.Params to access this data
* `revel.Request.PostFormValue()` Is deprecated, you should use methods in the controller.Params.Form to access this data
* `revel.Request.ParseForm()` Is deprecated - not needed
* `revel.Request.ParseMultipartForm()` Is deprecated - not needed
* `revel.Request.Form` Is deprecated, you should use the controller.Params.Form to access this data
* `revel.Request.MultipartForm` Is deprecated, you should use the controller.Params.Form to access this data
* `revel.TRACE`, `revel.INFO` `revel.WARN` `revel.ERROR` are deprecated. Use new application logger `revel.AppLog` and the controller logger `controller.Log`. See [logging](http://revel.github.io/manual/logging.html) for more details.
### Features
* Pluggable server engine support. You can now implement **your own server engine**. This means if you need to listen to more then 1 IP address or port you can implement a custom server engine to do this. By default Revel uses GO http server, but also available is fasthttp server in the revel/modules repository. See the docs for more information on how to implement your own engine.
### Enhancements
* Controller instances are cached for reuse. This speeds up the request response time and prevents unnecessary garbage collection cycles.
### Bug fixes
## v0.17
[[revel/revel](https://github.com/revel/revel)]
* add-validation
* i18-lang-by-param
* Added namespace to routes, controllers
* Added go 1.6 to testing
* Adds the ability to set the language by a url parameter. The route file will need to specify the parameter so that it will be picked up
* Changed url validation logic to regex
* Added new validation mehtods (IPAddr,MacAddr,Domain,URL,PureText)
[[revel/cmd](https://github.com/revel/cmd)]
* no changes
[[revel/config](https://github.com/revel/config)]
* no changes
[[revel/modules](https://github.com/revel/modules)]
* Added Gorm module
[[revel/cron](https://github.com/revel/cron)]
* Updated cron task manager
* Added ability to run a specific job, reschedules job if cron is running.
[[revel/examples](https://github.com/revel/examples)]
* Gorm module (Example)
# v0.16.0
Deprecating support for golang versions prior to 1.6
### Breaking Changes
* `CurrentLocaleRenderArg` to `CurrentLocaleViewArg` for consistency
* JSON requests are now parsed by Revel, if the content type is `text/json` or `application/json`. The raw data is available in `Revel.Controller.Params.JSON`. But you can also use the automatic controller operation to load the data like you would any structure or map. See [here](http://revel.github.io/manual/parameters.html) for more details
### Features
* Modular Template Engine #1170
* Pongo2 engine driver added revel/modules#39
* Ace engine driver added revel/modules#40
* Added i18n template support #746
### Enhancements
* JSON request binding #1161
* revel.SetSecretKey function added #1127
* ResolveFormat now looks at the extension as well (this sets the content type) #936
* Updated command to run tests using the configuration revel/cmd#61
### Bug fixes
* Updated documentation typos revel/modules#37
* Updated order of parameter map assignment #1155
* Updated cookie lifetime for firefox #1174
* Added test path for modules, so modules will run tests as well #1162
* Fixed go profiler module revel/modules#20
# v0.15.0
@shawncatz released this on 2017-05-11
Deprecating support for golang versions prior to 1.7
### Breaking Changes
* None
### Features
* None
### Enhancements
* Update and improve docs revel/examples#17 revel/cmd#85
### Bug fixes
* Prevent XSS revel/revel#1153
* Improve error checking for go version detection revel/cmd#86
# v0.14.0
@notzippy released this on 2017-03-24
## Changes since v0.13.0
#### Breaking Changes
- `revel/revel`:
- change RenderArgs to ViewArgs PR #1135
- change RenderJson to RenderJSON PR #1057
- change RenderHtml to RenderHTML PR #1057
- change RenderXml to RenderXML PR #1057
#### Features
- `revel/revel`:
#### Enhancements
- `revel/revel`:
#### Bug Fixes
- `revel/revel`:
# v0.13.1
@jeevatkm released this on 2016-06-07
**Bug fix:**
- Windows path fix #1064
# v0.13.0
@jeevatkm released this on 2016-06-06
## Changes since v0.12.0
#### Breaking Changes
- `revel/revel`:
- Application Config name changed from `watcher.*` to `watch.*` PR #992, PR #991
#### Features
- `revel/revel`:
- Request access log PR #1059, PR #913, #1055
- Messages loaded from modules too PR #828
- `revel/cmd`:
- Added `revel version` command emits the revel version and go version revel/cmd#19
#### Enhancements
- `revel/revel`:
- Creates log directory if missing PR #1039
- Added `application/javascript` to accepted headers PR #1022
- You can change `Server.Addr` value via hook function PR #999
- Improved deflate/gzip compressor PR #995
- Consistent config name `watch.*` PR #992, PR #991
- Defaults to HttpOnly and always secure cookies for non-dev mode #942, PR #943
- Configurable server Read and Write Timeout via app config #936, PR #940
- `OnAppStart` hook now supports order param too PR #935
- Added `PutForm` and `PutFormCustom` helper method in `testing.TestSuite` #898
- Validator supports UTF-8 string too PR #891, #841
- Added `InitServer` method that returns `http.HandlerFunc` PR #879
- Symlink aware processing Views, Messages and Watch mode PR #867, #673
- Added i18n settings support unknown format PR #852
- i18n: Make Message Translation pluggable PR #768
- jQuery `min-2.2.4` & Bootstrap `min-3.3.6` version updated in `skeleton/public` #1063
- `revel/cmd`:
- Revel identifies current `GOPATH` and performs `new` command; relative to directory revel/revel#1004
- Installs package dependencies during a build PR revel/cmd#43
- Non-200 response of test case request will correctly result into error PR revel/cmd#38
- Websockets SSL support in `dev` mode PR revel/cmd#32
- Won't yell about non-existent directory while cleaning PR revel/cmd#31, #908
- [x] non-fatal errors when building #908
- Improved warnings about route generation PR revel/cmd#25
- Command is Symlink aware PR revel/cmd#20
- `revel package` & `revel build` now supports environment mode PR revel/cmd#14
- `revel clean` now cleans generated routes too PR revel/cmd#6
- `revel/config`:
- Upstream `robfig/config` refresh and import path updated from `github.com/revel/revel/config` to `github.com/revel/config`, PR #868
- Config loading order and external configuration to override application configuration revel/config#4 [commit](https://github.com/revel/revel/commit/f3a422c228994978ae0a5dd837afa97248b26b41)
- Application config error will produce insight on error PR revel/config#3 [commit](https://github.com/revel/config/commit/85a123061070899a82f59c5ef6187e8fb4457f64)
- `revel/modules`:
- Testrunner enhancements
- Minor improvement on testrunner module PR #820, #895
- Add Test Runner panels per test group PR revel/modules#12
- `revel/revel.github.io`:
- Update `index.md` and homepage (change how samples repo is installed) PR [#85](https://github.com/revel/revel.github.io/pull/85)
- Couple of UI improvements PR [#93](https://github.com/revel/revel.github.io/pull/93)
- Updated techempower benchmarks Round 11 [URL](http://www.techempower.com/benchmarks/#section=data-r11)
- Docs updated for v0.13 release
- Cross-Platform Support
- Slashes should be normalized in paths #260, PR #1028, PR #928
#### Bug Fixes
- `revel/revel`:
- Binder: Multipart `io.Reader` parameters needs to be closed #756
- Default Date & Time Format correct in skeleton PR #1062, #878
- Addressed with alternative for `json: unsupported type: <-chan struct {}` on Go 1.6 revel/revel#1037
- Addressed one edge case, invalid Accept-Encoding header causes panic revel/revel#914
# v0.11.3
@brendensoares released this on 2015-01-04
This is a minor release to address a critical bug (#824) in v0.11.2.
Everybody is strongly encouraged to rebuild their projects with the latest version of Revel. To do it, execute the commands:
``` sh
$ go get -u github.com/revel/cmd/revel
$ revel build github.com/myusername/myproject /path/to/destination/folder
```
# v0.11.2
on 2014-11-23
This is a minor release to address a critical bug in v0.11.0.
Everybody is strongly encouraged to rebuild their projects with the latest version of Revel. To do it, execute the commands:
``` sh
$ go get -u github.com/revel/cmd/revel
$ revel build github.com/myusername/myproject /path/to/destination/folder
```
# v0.11.1
@pushrax released this on 2014-10-27
This is a minor release to address a compilation error in v0.11.0.
# v0.12.0
@brendensoares released this on 2015-03-25
Changes since v0.11.3:
## Breaking Changes
1. Add import path to new `testing` sub-package for all Revel tests. For example:
``` go
package tests
import "github.com/revel/revel/testing"
type AppTest struct {
testing.TestSuite
}
```
1. We've relocated modules to a dedicated repo. Make sure you update your `conf/app.conf`. For example, change:
``` ini
module.static=github.com/revel/revel/modules/static
module.testrunner = github.com/revel/revel/modules/testrunner
```
to the new paths:
``` ini
module.static=github.com/revel/modules/static
module.testrunner = github.com/revel/modules/testrunner
```
## [ROADMAP] Focus: Improve Internal Organization
The majority of our effort here is increasing the modularity of the code within Revel so that further development can be done more productively while keeping documentation up to date.
- `revel/revel.github.io`
- [x] Improve docs #[43](https://github.com/revel/revel.github.io/pull/43)
- `revel/revel`:
- [x] Move the `revel/revel/harness` to the `revel/cmd` repo since it's only used during build time. #[714](https://github.com/revel/revel/issues/714)
- [x] Move `revel/revel/modules` to the `revel/modules` repo #[785](https://github.com/revel/revel/issues/785)
- [x] Move `revel/revel/samples` to the `revel/samples` repo #[784](https://github.com/revel/revel/issues/784)
- [x] `testing` TestSuite #[737](https://github.com/revel/revel/issues/737) #[810](https://github.com/revel/revel/issues/810)
- [x] Feature/sane http timeout defaults #[837](https://github.com/revel/revel/issues/837) PR#[843](https://github.com/revel/revel/issues/843) Bug Fix PR#[860](https://github.com/revel/revel/issues/860)
- [x] Eagerly load templates in dev mode #[353](https://github.com/revel/revel/issues/353) PR#[844](https://github.com/revel/revel/pull/844)
- [x] Add an option to trim whitespace from rendered HTML #[800](https://github.com/revel/revel/issues/800)
- [x] Remove built-in mailer in favor of 3rd party package #[783](https://github.com/revel/revel/issues/783)
- [x] Allow local reverse proxy access to jobs module status page for IPv4/6 #[481](https://github.com/revel/revel/issues/481) PR#[6](https://github.com/revel/modules/pull/6) PR#[7](https://github.com/revel/modules/pull/7)
- [x] Add default http.Status code for render methods. #[728](https://github.com/revel/revel/issues/728)
- [x] add domain for cookie #[770](https://github.com/revel/revel/issues/770) PR#[882](https://github.com/revel/revel/pull/882)
- [x] production mode panic bug #[831](https://github.com/revel/revel/issues/831) PR#[881](https://github.com/revel/revel/pull/881)
- [x] Fixes template loading order whether watcher is enabled or not #[844](https://github.com/revel/revel/issues/844)
- [x] Fixes reverse routing wildcard bug PR#[886](https://github.com/revel/revel/pull/886) #[869](https://github.com/revel/revel/issues/869)
- [x] Fixes router app start bug without routes. PR #[855](https://github.com/revel/revel/pull/855)
- [x] Friendly URL template errors; Fixes template `url` func "index out of range" when param is `undefined` #[811](https://github.com/revel/revel/issues/811) PR#[880](https://github.com/revel/revel/pull/880)
- [x] Make result compression conditional PR#[888](https://github.com/revel/revel/pull/888)
- [x] ensure routes are loaded before returning from OnAppStart callback PR#[884](https://github.com/revel/revel/pull/884)
- [x] Use "302 Found" HTTP code for redirect PR#[900](https://github.com/revel/revel/pull/900)
- [x] Fix broken fake app tests PR#[899](https://github.com/revel/revel/pull/899)
- [x] Optimize search of template names PR#[885](https://github.com/revel/revel/pull/885)
- `revel/cmd`:
- [x] track current Revel version #[418](https://github.com/revel/revel/issues/418) PR#[858](https://github.com/revel/revel/pull/858)
- [x] log path error After revel build? #[763](https://github.com/revel/revel/issues/763)
- [x] Use a separate directory for revel project binaries #[17](https://github.com/revel/cmd/pull/17) #[819](https://github.com/revel/revel/issues/819)
- [x] Overwrite generated app files instead of deleting directory #[551](https://github.com/revel/revel/issues/551) PR#[23](https://github.com/revel/cmd/pull/23)
- `revel/modules`:
- [x] Adds runtime pprof/trace support #[9](https://github.com/revel/modules/pull/9)
- Community Goals:
- [x] Issue labels #[545](https://github.com/revel/revel/issues/545)
- [x] Sync up labels/milestones in other repos #[721](https://github.com/revel/revel/issues/721)
- [x] Update the Revel Manual to reflect current features
- [x] [revel/revel.github.io/32](https://github.com/revel/revel.github.io/issues/32)
- [x] [revel/revel.github.io/39](https://github.com/revel/revel.github.io/issues/39)
- [x] Docs are obsolete, inaccessible TestRequest.testSuite #[791](https://github.com/revel/revel/issues/791)
- [x] Some questions about revel & go docs #[793](https://github.com/revel/revel/issues/793)
- [x] RFCs to organize features #[827](https://github.com/revel/revel/issues/827)
[Full list of commits](https://github.com/revel/revel/compare/v0.11.3...v0.12.0)
# v0.11.0
@brendensoares released this on 2014-10-26
Note, Revel 0.11 requires Go 1.3 or higher.
Changes since v0.10:
[BUG] #729 Adding define inside the template results in an error (Changes how template file name case insensitivity is handled)
[ENH] #769 Add swap files to gitignore
[ENH] #766 Added passing in build flags to the go build command
[ENH] #761 Fixing cross-compiling issue #456 setting windows path from linux
[ENH] #759 Include upload sample's tests in travis
[ENH] #755 Changes c.Action to be the action method name's letter casing per #635
[ENH] #754 Adds call stack display to runtime panic in browser to match console
[ENH] #740 Redis Cache: Add timeouts.
[ENH] #734 watcher: treat fsnotify Op as a bitmask
[ENH] #731 Second struct in type revel fails to find the controller
[ENH] #725 Testrunner: show response info
[ENH] #723 Improved compilation errors and open file from error page
[ENH] #720 Get testrunner path from config file
[ENH] #707 Add log.colorize option to enable/disable colorize
[ENH] #696 Revel file upload testing
[ENH] #694 Install dependencies at build time
[ENH] #693 Prefer extension over Accept header
[ENH] #692 Update fsnotify to v1 API
[ENH] #690 Support zero downtime restarts
[ENH] #687 Tests: request override
[ENH] #685 Persona sample tests and bugfix
[ENH] #598 Added README file to Revel skeleton
[ENH] #591 Realtime rebuild
[ENH] #573 Add AppRoot to allow changing the root path of an application
[FTR] #606 CSRF Support
[Full list of commits](https://github.com/revel/revel/compare/v0.10.0...v0.11.0)
# v0.10.0
@brendensoares released this on 2014-08-10
Changes since v0.9.1:
- [FTR] #641 - Add "X-HTTP-Method-Override" to router
- [FTR] #583 - Added HttpMethodOverride filter to routes
- [FTR] #540 - watcher flag for refresh on app start
- [BUG] #681 - Case insensitive comparison for websocket upgrades (Fixes IE Websockets ...
- [BUG] #668 - Compression: Properly close gzip/deflate
- [BUG] #667 - Fix redis GetMulti and improve test coverage
- [BUG] #664 - Is compression working correct?
- [BUG] #657 - Redis Cache: panic when testing Ge
- [BUG] #637 - RedisCache: fix Get/GetMulti error return
- [BUG] #621 - Bugfix/router csv error
- [BUG] #618 - Router throws exception when parsing line with multiple default string arguments
- [BUG] #604 - Compression: Properly close gzip/deflate.
- [BUG] #567 - Fixed regex pattern to properly require message files to have a dot in filename
- [BUG] #566 - Compression fails ("unexpected EOF" in tests)
- [BUG] #287 - Don't remove the parent folders containing generated code.
- [BUG] #556 - fix for #534, also added url path to not found message
- [BUG] #534 - Websocket route not found
- [BUG] #343 - validation.Required(funtionCall).Key(...) - reflect.go:715: Failed to generate name for field.
- [ENH] #643 - Documentation Fix in Skeleton for OnAppStart
- [ENH] #674 - Removes custom `eq` template function
- [ENH] #669 - Develop compress closenotifier
- [ENH] #663 - fix for static content type not being set and defaulting to OS
- [ENH] #658 - Minor: fix niggle with import statement
- [ENH] #652 - Update the contributing guidelines
- [ENH] #651 - Use upstream gomemcache again
- [ENH] #650 - Go back to upstream memcached library
- [ENH] #612 - Fix CI package error
- [ENH] #611 - Fix "go vet" problems
- [ENH] #610 - Added MakeMultipartRequest() to the TestSuite
- [ENH] #608 - Develop compress closenotifier
- [ENH] #596 - Expose redis cache options to config
- [ENH] #581 - Make the option template tag type agnostic.
- [ENH] #576 - Defer session instantiation to first set
- [ENH] #565 - Fix #563 -- Some custom template funcs cannot be used in JavaScript cont...
- [ENH] #563 - TemplateFuncs cannot be used in JavaScript context
- [ENH] #561 - Fix missing extension from message file causing panic
- [ENH] #560 - enhancement / templateFunc `firstof`
- [ENH] #555 - adding symlink handling to the template loader and watcher processes
- [ENH] #531 - Update app.conf.template
- [ENH] #520 - Respect controller's Response.Status when action returns nil
- [ENH] #519 - Link to issues
- [ENH] #486 - Support for json compress
- [ENH] #480 - Eq implementation in template.go still necessary ?
- [ENH] #461 - Cron jobs not started until I pull a page
- [ENH] #323 - disable session/set-cookie for `Static.Serve()`
[Full list of commits](https://github.com/revel/revel/compare/v0.9.1...v0.10.0)
# v0.9.1
@pushrax released this on 2014-03-02
Minor patch release to address a couple bugs.
Changes since v0.9.0:
- [BUG] #529 - Wrong path was used to determine existence of `.git`
- [BUG] #532 - Fix typo for new type `ValidEmail`
The full list of commits can be found [here](https://github.com/revel/revel/compare/v0.9.0...v0.9.1).
# v0.9.0
@pushrax released this on 2014-02-26
## Revel GitHub Organization
We've moved development of the framework to the @revel GitHub organization, to help manage the project as Revel grows. The old import path is still valid, but will not be updated in the future.
You'll need to manually update your apps to work with the new import path. This can be done by replacing all instances of `github.com/robfig/revel` with `github.com/revel/revel` in your app, and running:
```
$ cd your_app_folder
$ go get -u github.com/howeyc/fsnotify # needs updating
$ go get github.com/revel/revel
$ go get github.com/revel/cmd/revel # command line tools have moved
```
**Note:** if you have references to `github.com/robfig/revel/revel` in any files, you need to replace them with `github.com/revel/cmd/revel` _before_ replacing `github.com/robfig/revel`! (note the prefix collision)
If you have any trouble upgrading or notice something we missed, feel free to hop in the IRC channel (#revel on Freenode) or send the mailing list a message.
Also note, the documentation is now at [revel.github.io](http://revel.github.io)!
Changes since v0.8:
- [BUG] #522 - `revel new` bug
- [BUG] - Booking sample error
- [BUG] #504 - File access via URL security issue
- [BUG] #489 - Email validator bug
- [BUG] #475 - File watcher infinite loop
- [BUG] #333 - Extensions in routes break parameters
- [FTR] #472 - Support for 3rd part app skeletons
- [ENH] #512 - Per session expiration methods
- [ENH] #496 - Type check renderArgs[CurrentLocalRenderArg]
- [ENH] #490 - App.conf manual typo
- [ENH] #487 - Make files executable on `revel build`
- [ENH] #482 - Retain input values after form valdiation
- [ENH] #473 - OnAppStart documentation
- [ENH] #466 - JSON error template quoting fix
- [ENH] #464 - Remove unneeded trace statement
- [ENH] #457 - Remove unneeded trace
- [ENH] #508 - Support arbitrary network types
- [ENH] #516 - Add Date and Message-Id mail headers
The full list of commits can be found [here](https://github.com/revel/revel/compare/v0.8...v0.9.0).
# v0.8
@pushrax released this on 2014-01-06
Changes since v0.7:
- [BUG] #379 - HTTP 500 error for not found public path files
- [FTR] #424 - HTTP pprof support
- [FTR] #346 - Redis Cache support
- [FTR] #292 - SMTP Mailer
- [ENH] #443 - Validator constructors to improve `v.Check()` usage
- [ENH] #439 - Basic terminal output coloring
- [ENH] #428 - Improve error message for missing `RenderArg`
- [ENH] #422 - Route embedding for modules
- [ENH] #413 - App version variable
- [ENH] #153 - $GOPATH-wide file watching aka hot loading
# v0.6
@robfig released this on 2013-09-16
================================================
FILE: CONTRIBUTING.md
================================================
## Contributing to Revel
This describes how developers may contribute to Revel.
## Mission
Revel's mission is to provide a batteries-included framework for making large
scale web application development as efficient and maintainable as possible.
The design should be configurable and modular so that it can grow with the
developer. However, it should provide a wonderful un-boxing experience and
default configuration that can woo new developers and make simple web apps
straight forward. The framework should have an opinion about how to do all of the
common tasks in web development to reduce unnecessary cognitive load.
Perhaps most important of all, Revel should be a joy to use. We want to reduce
the time spent on tedious boilerplate functionality and increase the time
available for creating polished solutions for your application's target users.
## How to Contribute
### Join the Community
The first step to making Revel better is joining the community! You can find the
community on:
* [Google Groups](https://groups.google.com/forum/#!forum/revel-framework) via [revel-framework@googlegroups.com](mailto:revel-framework@googlegroups.com)
* [GitHub Issues](https://github.com/revel/revel/issues)
* [StackOverflow Questions](http://stackoverflow.com/questions/tagged/revel)
* [IRC](http://webchat.freenode.net/?channels=%23revel&uio=d4) via #revel on Freenode
Once you've joined, there are many ways to contribute to Revel:
* Report bugs (via GitHub)
* Answer questions of other community members (via Google Groups or IRC)
* Give feedback on new feature discussions (via GitHub and Google Groups)
* Propose your own ideas (via Google Groups or GitHub)
### How Revel is Developed
We have begun to formalize the development process by adopting pragmatic
practices such as:
* Developing on the `develop` branch
* Merging `develop` branch to `master` branch in 6 week iterations
* Tagging releases with MAJOR.MINOR syntax (e.g. v0.8)
** We may also tag MAJOR.MINOR.HOTFIX releases as needed (e.g. v0.8.1) to
address urgent bugs. Such releases will not introduce or change functionality
* Managing bugs, enhancements, features and release milestones via GitHub's Issue Tracker
* Using feature branches to create pull requests
* Discussing new features **before** hacking away at it
### How to Correctly Fork
Go uses the repository URL to import packages, so forking and `go get`ing the
forked project **will not work**.
Instead, follow these steps:
1. Install Revel normally
2. Fork Revel on GitHub
3. Add your fork as a git remote
Here's the commands to do so:
```
$ go get github.com/revel/revel # Install Revel
$ cd $GOPATH/src/github.com/revel/revel # Change directory to revel repo
$ git remote add fork git@github.com:$USER/revel.git # Add your fork as a remote, where $USER is your GitHub username
```
### Create a Feature Branch & Code Away!
Now that you've properly installed and forked Revel, you are ready to start coding (assuming
you have a validated your ideas with other community members)!
In order to have your pull requests accepted, we recommend you make your changes to Revel on a
new git branch. For example,
```
$ git checkout -b feature/useful-new-thing origin/develop # Create a new branch based on develop and switch to it
$ ... # Make your changes and commit them
$ git push fork feature/useful-new-thing # After new commits, push to your fork
```
### Format Your Code
Remember to run `go fmt` before committing your changes.
Many Go developers opt to have their editor run `go fmt` automatically when
saving Go files.
Additionally, follow the [core Go style conventions](https://code.google.com/p/go-wiki/wiki/CodeReviewComments)
to have your pull requests accepted.
### Write Tests (and Benchmarks for Bonus Points)
Significant new features require tests. Besides unit tests, it is also possible
to test a feature by exercising it in one of the sample apps and verifying its
operation using that app's test suite. This has the added benefit of providing
example code for developers to refer to.
Benchmarks are helpful but not required.
### Run the Tests
Typically running the main set of unit tests will be sufficient:
```
$ go test github.com/revel/revel
```
Refer to the
[Travis configuration](https://github.com/revel/revel/blob/master/.travis.yml)
for the full set of tests. They take less than a minute to run.
### Document Your Feature
Due to the wide audience and shared nature of Revel, documentation is an essential
addition to your new code. **Pull requests risk not being accepted** until proper
documentation is created to detail how to make use of new functionality.
The [Revel web site](http://revel.github.io/) is hosted on GitHub Pages and
[built with Jekyll](https://help.github.com/articles/using-jekyll-with-pages).
To develop the Jekyll site locally:
# Clone the documentation repository
$ git clone git@github.com:revel/revel.github.io
$ cd revel.github.io
# Install and run Jekyll to generate and serve the site
$ gem install jekyll kramdown
$ jekyll serve --watch
# Now load in your browser
$ open http://localhost:4000/
Any changes you make to the site should be reflected within a few seconds.
### Submit Pull Request
Once you've done all of the above & pushed your changes to your fork, you can create a pull request for review and acceptance.
## Potential Projects
These are outstanding feature requests, roughly ordered by priority.
Additionally, there are frequently smaller feature requests or items in the
[issues](https://github.com/revel/revel/issues?labels=contributor+ready&page=1&state=open).
1. Better ORM support. Provide more samples (or modules) and better documentation for setting up common situations like SQL database, Mongo, LevelDB, etc.
1. Support for other templating languages (e.g. mustache, HAML). Make TemplateLoader pluggable. Use Pongo instead of vanilla Go templates (and update the samples)
1. Test Fixtures
1. Authenticity tokens for CSRF protection
1. Coffeescript pre-processor. Could potentially use [otto](https://github.com/robertkrimen/otto) as a native Go method to compiling.
1. SCSS/LESS pre-processor.
1. GAE support. Some progress made in the 'appengine' branch -- the remaining piece is running the appengine services in development.
1. More Form helpers (template funcs).
1. A Mongo module (perhaps with a sample app)
1. Deployment to OpenShift (support, documentation, etc)
1. Improve the logging situation. The configuration is a little awkward and not very powerful. Integrating something more powerful would be good. (like [seelog](https://github.com/cihub/seelog) or [log4go](https://code.google.com/p/log4go/))
1. ETags, cache controls
1. A module or plugins for adding HTTP Basic Auth
1. Allowing the app to hook into the source code processing step
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (C) 2012-2018 The Revel Framework Authors.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# Revel Framework
[](https://travis-ci.org/revel/revel)
[](LICENSE)
[](https://goreportcard.com/report/github.com/revel/revel)
A high productivity, full-stack web framework for the [Go language](https://www.golang.org).
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
* [Gitter](https://gitter.im/revel/community)
* [StackOverflow](https://stackoverflow.com/questions/tagged/revel)
## Learn More
* [Manual, Samples, Godocs, etc](https://revel.github.io)
* [Apps using Revel](https://github.com/revel/revel/wiki/Apps-in-the-Wild)
* [Articles Featuring Revel](https://github.com/revel/revel/wiki/Articles)
## Contributing
* [Contributing Code Guidelines](https://github.com/revel/revel/blob/master/CONTRIBUTING.md)
* [Revel Contributors](https://github.com/revel/revel/graphs/contributors)
## Contributors
[](https://sourcerer.io/fame/notzippy/revel/revel/links/0)
[](https://sourcerer.io/fame/notzippy/revel/revel/links/1)
[](https://sourcerer.io/fame/notzippy/revel/revel/links/2)
[](https://sourcerer.io/fame/notzippy/revel/revel/links/3)
[](https://sourcerer.io/fame/notzippy/revel/revel/links/4)
[](https://sourcerer.io/fame/notzippy/revel/revel/links/5)
[](https://sourcerer.io/fame/notzippy/revel/revel/links/6)
[](https://sourcerer.io/fame/notzippy/revel/revel/links/7)
================================================
FILE: before_after_filter.go
================================================
package revel
import (
"reflect"
)
// Autocalls any defined before and after methods on the target controller
// If either calls returns a value then the result is returned.
func BeforeAfterFilter(c *Controller, fc []Filter) {
defer func() {
if resultValue := beforeAfterFilterInvoke(FINALLY, c); resultValue != nil && !resultValue.IsNil() {
c.Result = resultValue.Interface().(Result)
}
}()
defer func() {
if err := recover(); err != nil {
if resultValue := beforeAfterFilterInvoke(PANIC, c); resultValue != nil && !resultValue.IsNil() {
c.Result = resultValue.Interface().(Result)
}
panic(err)
}
}()
if resultValue := beforeAfterFilterInvoke(BEFORE, c); resultValue != nil && !resultValue.IsNil() {
c.Result = resultValue.Interface().(Result)
return
}
fc[0](c, fc[1:])
if resultValue := beforeAfterFilterInvoke(AFTER, c); resultValue != nil && !resultValue.IsNil() {
c.Result = resultValue.Interface().(Result)
}
}
func beforeAfterFilterInvoke(method When, c *Controller) (r *reflect.Value) {
if c.Type == nil {
return
}
var index []*ControllerFieldPath
switch method {
case BEFORE:
index = c.Type.ControllerEvents.Before
case AFTER:
index = c.Type.ControllerEvents.After
case FINALLY:
index = c.Type.ControllerEvents.Finally
case PANIC:
index = c.Type.ControllerEvents.Panic
}
if len(index) == 0 {
return
}
for _, function := range index {
result := function.Invoke(reflect.ValueOf(c.AppController), nil)[0]
if !result.IsNil() {
return &result
}
}
return
}
================================================
FILE: before_after_filter_test.go
================================================
// Copyright (c) 2019 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package revel
import (
"testing"
)
type bafTestController struct {
*Controller
}
func (c bafTestController) Before() (Result, bafTestController) {
return c.Redirect("http://www.example.com"), c
}
func (c bafTestController) Index() Result {
// We shouldn't get here
panic("Should not be called")
}
type failingFilter struct {
t *testing.T
}
func (f failingFilter) FailIfCalled(c *Controller, filterChain []Filter) {
f.t.Error("Filter should not have been called")
}
func TestInterceptorsNotCalledIfBeforeReturns(t *testing.T) {
Init("prod", "github.com/revel/revel/testdata", "")
controllers = make(map[string]*ControllerType)
RegisterController((*bafTestController)(nil), []*MethodType{
{
Name: "Before",
},
{
Name: "Index",
},
})
c := NewControllerEmpty()
err := c.SetAction("bafTestController", "Index")
if err != nil {
t.Error(err.Error())
}
BeforeAfterFilter(c, []Filter{failingFilter{t}.FailIfCalled})
}
================================================
FILE: binder.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package revel
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"os"
"reflect"
"strconv"
"strings"
"time"
)
// A Binder translates between string parameters and Go data structures.
type Binder struct {
// Bind takes the name and type of the desired parameter and constructs it
// from one or more values from Params.
//
// Example
//
// Request:
// url?id=123&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=rob
//
// Action:
// Example.Action(id int, ol []int, ul []string, user User)
//
// Calls:
// Bind(params, "id", int): 123
// Bind(params, "ol", []int): {1, 2}
// Bind(params, "ul", []string): {"str", "array"}
// Bind(params, "user", User): User{Name:"rob"}
//
// Note that only exported struct fields may be bound.
Bind func(params *Params, name string, typ reflect.Type) reflect.Value
// Unbind serializes a given value to one or more URL parameters of the given
// name.
Unbind func(output map[string]string, name string, val interface{})
}
var binderLog = RevelLog.New("section", "binder")
// ValueBinder is adapter for easily making one-key-value binders.
func ValueBinder(f func(value string, typ reflect.Type) reflect.Value) func(*Params, string, reflect.Type) reflect.Value {
return func(params *Params, name string, typ reflect.Type) reflect.Value {
vals, ok := params.Values[name]
if !ok || len(vals) == 0 {
return reflect.Zero(typ)
}
return f(vals[0], typ)
}
}
// Revel's default date and time constants.
const (
DefaultDateFormat = "2006-01-02"
DefaultDateTimeFormat = "2006-01-02 15:04"
)
// Binders type and kind definition.
var (
// These are the lookups to find a Binder for any type of data.
// The most specific binder found will be used (Type before Kind).
TypeBinders = make(map[reflect.Type]Binder)
KindBinders = make(map[reflect.Kind]Binder)
// Applications can add custom time formats to this array, and they will be
// automatically attempted when binding a time.Time.
TimeFormats = []string{}
DateFormat string
DateTimeFormat string
TimeZone = time.UTC
IntBinder = Binder{
Bind: ValueBinder(func(val string, typ reflect.Type) reflect.Value {
if len(val) == 0 {
return reflect.Zero(typ)
}
intValue, err := strconv.ParseInt(val, 10, 64)
if err != nil {
binderLog.Warn("IntBinder Conversion Error", "error", err)
return reflect.Zero(typ)
}
pValue := reflect.New(typ)
pValue.Elem().SetInt(intValue)
return pValue.Elem()
}),
Unbind: func(output map[string]string, key string, val interface{}) {
output[key] = fmt.Sprintf("%d", val)
},
}
UintBinder = Binder{
Bind: ValueBinder(func(val string, typ reflect.Type) reflect.Value {
if len(val) == 0 {
return reflect.Zero(typ)
}
uintValue, err := strconv.ParseUint(val, 10, 64)
if err != nil {
binderLog.Warn("UintBinder Conversion Error", "error", err)
return reflect.Zero(typ)
}
pValue := reflect.New(typ)
pValue.Elem().SetUint(uintValue)
return pValue.Elem()
}),
Unbind: func(output map[string]string, key string, val interface{}) {
output[key] = fmt.Sprintf("%d", val)
},
}
FloatBinder = Binder{
Bind: ValueBinder(func(val string, typ reflect.Type) reflect.Value {
if len(val) == 0 {
return reflect.Zero(typ)
}
floatValue, err := strconv.ParseFloat(val, 64)
if err != nil {
binderLog.Warn("FloatBinder Conversion Error", "error", err)
return reflect.Zero(typ)
}
pValue := reflect.New(typ)
pValue.Elem().SetFloat(floatValue)
return pValue.Elem()
}),
Unbind: func(output map[string]string, key string, val interface{}) {
output[key] = fmt.Sprintf("%f", val)
},
}
StringBinder = Binder{
Bind: ValueBinder(func(val string, typ reflect.Type) reflect.Value {
return reflect.ValueOf(val)
}),
Unbind: func(output map[string]string, name string, val interface{}) {
output[name] = val.(string)
},
}
// Booleans support a various value formats,
// refer `revel.Atob` method.
BoolBinder = Binder{
Bind: ValueBinder(func(val string, typ reflect.Type) reflect.Value {
return reflect.ValueOf(Atob(val))
}),
Unbind: func(output map[string]string, name string, val interface{}) {
output[name] = fmt.Sprintf("%t", val)
},
}
PointerBinder = Binder{
Bind: func(params *Params, name string, typ reflect.Type) reflect.Value {
v := Bind(params, name, typ.Elem())
if v.CanAddr() {
return v.Addr()
}
return reflect.Zero(typ)
},
Unbind: func(output map[string]string, name string, val interface{}) {
Unbind(output, name, reflect.ValueOf(val).Elem().Interface())
},
}
TimeBinder = Binder{
Bind: ValueBinder(func(val string, typ reflect.Type) reflect.Value {
for _, f := range TimeFormats {
if r, err := time.ParseInLocation(f, val, TimeZone); err == nil {
return reflect.ValueOf(r)
}
}
return reflect.Zero(typ)
}),
Unbind: func(output map[string]string, name string, val interface{}) {
var (
t = val.(time.Time)
format = DateTimeFormat
h, m, s = t.Clock()
)
if h == 0 && m == 0 && s == 0 {
format = DateFormat
}
output[name] = t.Format(format)
},
}
MapBinder = Binder{
Bind: bindMap,
Unbind: unbindMap,
}
)
// Used to keep track of the index for individual keyvalues.
type sliceValue struct {
index int // Index extracted from brackets. If -1, no index was provided.
value reflect.Value // the bound value for this slice element.
}
// This function creates a slice of the given type, Binds each of the individual
// elements, and then sets them to their appropriate location in the slice.
// If elements are provided without an explicit index, they are added (in
// unspecified order) to the end of the slice.
func bindSlice(params *Params, name string, typ reflect.Type) reflect.Value {
// Collect an array of slice elements with their indexes (and the max index).
maxIndex := -1
numNoIndex := 0
sliceValues := []sliceValue{}
maxIndexBound := Config.IntDefault("params.max_index", 4096)
// Factor out the common slice logic (between form values and files).
processElement := func(key string, vals []string, files []*multipart.FileHeader) {
if !strings.HasPrefix(key, name+"[") {
return
}
// Extract the index, and the index where a sub-key starts. (e.g. field[0].subkey)
index := -1
leftBracket, rightBracket := len(name), strings.Index(key[len(name):], "]")+len(name)
if rightBracket > leftBracket+1 {
index, _ = strconv.Atoi(key[leftBracket+1 : rightBracket])
}
subKeyIndex := rightBracket + 1
// Handle the indexed case.
if index > -1 {
// Just ignore illegal index, fix issue #1424
if index > maxIndexBound {
binderLog.Error("Ignoring parameter for security reason", "index", index, "key", key)
return
}
if index > maxIndex {
maxIndex = index
}
sliceValues = append(sliceValues, sliceValue{
index: index,
value: Bind(params, key[:subKeyIndex], typ.Elem()),
})
return
}
// It's an un-indexed element. (e.g. element[])
numNoIndex += len(vals) + len(files)
for _, val := range vals {
// Unindexed values can only be direct-bound.
sliceValues = append(sliceValues, sliceValue{
index: -1,
value: BindValue(val, typ.Elem()),
})
}
for _, fileHeader := range files {
sliceValues = append(sliceValues, sliceValue{
index: -1,
value: BindFile(fileHeader, typ.Elem()),
})
}
}
for key, vals := range params.Values {
processElement(key, vals, nil)
}
for key, fileHeaders := range params.Files {
processElement(key, nil, fileHeaders)
}
resultArray := reflect.MakeSlice(typ, maxIndex+1, maxIndex+1+numNoIndex)
for _, sv := range sliceValues {
if sv.index != -1 {
resultArray.Index(sv.index).Set(sv.value)
} else {
resultArray = reflect.Append(resultArray, sv.value)
}
}
return resultArray
}
// Break on dots and brackets.
// e.g. bar => "bar", bar.baz => "bar", bar[0] => "bar".
func nextKey(key string) string {
fieldLen := strings.IndexAny(key, ".[")
if fieldLen == -1 {
return key
}
return key[:fieldLen]
}
func unbindSlice(output map[string]string, name string, val interface{}) {
v := reflect.ValueOf(val)
for i := 0; i < v.Len(); i++ {
Unbind(output, fmt.Sprintf("%s[%d]", name, i), v.Index(i).Interface())
}
}
func bindStruct(params *Params, name string, typ reflect.Type) reflect.Value {
resultPointer := reflect.New(typ)
result := resultPointer.Elem()
if params.JSON != nil {
// Try to inject the response as a json into the created result
if err := json.Unmarshal(params.JSON, resultPointer.Interface()); err != nil {
binderLog.Error("bindStruct Unable to unmarshal request", "name", name, "error", err, "data", string(params.JSON))
}
return result
}
fieldValues := make(map[string]reflect.Value)
for key := range params.Values {
if !strings.HasPrefix(key, name+".") {
continue
}
// Get the name of the struct property.
// Strip off the prefix. e.g. foo.bar.baz => bar.baz
suffix := key[len(name)+1:]
fieldName := nextKey(suffix)
fieldLen := len(fieldName)
if _, ok := fieldValues[fieldName]; !ok {
// Time to bind this field. Get it and make sure we can set it.
fieldValue := result.FieldByName(fieldName)
if !fieldValue.IsValid() {
binderLog.Warn("bindStruct Field not found", "name", fieldName)
continue
}
if !fieldValue.CanSet() {
binderLog.Warn("bindStruct Field not settable", "name", fieldName)
continue
}
boundVal := Bind(params, key[:len(name)+1+fieldLen], fieldValue.Type())
fieldValue.Set(boundVal)
fieldValues[fieldName] = boundVal
}
}
return result
}
func unbindStruct(output map[string]string, name string, iface interface{}) {
val := reflect.ValueOf(iface)
typ := val.Type()
for i := 0; i < val.NumField(); i++ {
structField := typ.Field(i)
fieldValue := val.Field(i)
// PkgPath is specified to be empty exactly for exported fields.
if structField.PkgPath == "" {
Unbind(output, fmt.Sprintf("%s.%s", name, structField.Name), fieldValue.Interface())
}
}
}
// Helper that returns an upload of the given name, or nil.
func getMultipartFile(params *Params, name string) multipart.File {
for _, fileHeader := range params.Files[name] {
file, err := fileHeader.Open()
if err == nil {
return file
}
binderLog.Warn("getMultipartFile: Failed to open uploaded file", "name", name, "error", err)
}
return nil
}
func bindFile(params *Params, name string, typ reflect.Type) reflect.Value {
reader := getMultipartFile(params, name)
if reader == nil {
return reflect.Zero(typ)
}
// If it's already stored in a temp file, just return that.
if osFile, ok := reader.(*os.File); ok {
return reflect.ValueOf(osFile)
}
// Otherwise, have to store it.
tmpFile, err := ioutil.TempFile("", "revel-upload")
if err != nil {
binderLog.Warn("bindFile: Failed to create a temp file to store upload", "name", name, "error", err)
return reflect.Zero(typ)
}
// Register it to be deleted after the request is done.
params.tmpFiles = append(params.tmpFiles, tmpFile)
_, err = io.Copy(tmpFile, reader)
if err != nil {
binderLog.Warn("bindFile: Failed to copy upload to temp file", "name", name, "error", err)
return reflect.Zero(typ)
}
_, err = tmpFile.Seek(0, 0)
if err != nil {
binderLog.Warn("bindFile: Failed to seek to beginning of temp file", "name", name, "error", err)
return reflect.Zero(typ)
}
return reflect.ValueOf(tmpFile)
}
func bindByteArray(params *Params, name string, typ reflect.Type) reflect.Value {
if reader := getMultipartFile(params, name); reader != nil {
b, err := ioutil.ReadAll(reader)
if err == nil {
return reflect.ValueOf(b)
}
binderLog.Warn("bindByteArray: Error reading uploaded file contents", "name", name, "error", err)
}
return reflect.Zero(typ)
}
func bindReadSeeker(params *Params, name string, typ reflect.Type) reflect.Value {
if reader := getMultipartFile(params, name); reader != nil {
return reflect.ValueOf(reader.(io.ReadSeeker))
}
return reflect.Zero(typ)
}
// bindMap converts parameters using map syntax into the corresponding map. e.g.:
// params["a[5]"]=foo, name="a", typ=map[int]string => map[int]string{5: "foo"}
func bindMap(params *Params, name string, typ reflect.Type) reflect.Value {
var (
keyType = typ.Key()
valueType = typ.Elem()
resultPtr = reflect.New(reflect.MapOf(keyType, valueType))
result = resultPtr.Elem()
)
result.Set(reflect.MakeMap(typ))
if params.JSON != nil {
// Try to inject the response as a json into the created result
if err := json.Unmarshal(params.JSON, resultPtr.Interface()); err != nil {
binderLog.Warn("bindMap: Unable to unmarshal request", "name", name, "error", err)
}
return result
}
for paramName := range params.Values {
// The paramName string must start with the value in the "name" parameter,
// otherwise there is no way the parameter is part of the map
if !strings.HasPrefix(paramName, name) {
continue
}
suffix := paramName[len(name)+1:]
fieldName := nextKey(suffix)
if fieldName != "" {
fieldName = fieldName[:len(fieldName)-1]
}
if !strings.HasPrefix(paramName, name+"["+fieldName+"]") {
continue
}
result.SetMapIndex(BindValue(fieldName, keyType), Bind(params, name+"["+fieldName+"]", valueType))
}
return result
}
func unbindMap(output map[string]string, name string, iface interface{}) {
mapValue := reflect.ValueOf(iface)
for _, key := range mapValue.MapKeys() {
Unbind(output, name+"["+fmt.Sprintf("%v", key.Interface())+"]",
mapValue.MapIndex(key).Interface())
}
}
// Bind takes the name and type of the desired parameter and constructs it
// from one or more values from Params.
// Returns the zero value of the type upon any sort of failure.
func Bind(params *Params, name string, typ reflect.Type) reflect.Value {
if binder, found := binderForType(typ); found {
return binder.Bind(params, name, typ)
}
return reflect.Zero(typ)
}
func BindValue(val string, typ reflect.Type) reflect.Value {
return Bind(&Params{Values: map[string][]string{"": {val}}}, "", typ)
}
func BindFile(fileHeader *multipart.FileHeader, typ reflect.Type) reflect.Value {
return Bind(&Params{Files: map[string][]*multipart.FileHeader{"": {fileHeader}}}, "", typ)
}
func Unbind(output map[string]string, name string, val interface{}) {
if binder, found := binderForType(reflect.TypeOf(val)); found {
if binder.Unbind != nil {
binder.Unbind(output, name, val)
} else {
binderLog.Error("Unbind: Unable to unmarshal request", "name", name, "value", val)
}
}
}
func binderForType(typ reflect.Type) (Binder, bool) {
binder, ok := TypeBinders[typ]
if !ok {
binder, ok = KindBinders[typ.Kind()]
if !ok {
binderLog.Error("binderForType: no binder for type", "type", typ)
return Binder{}, false
}
}
return binder, true
}
// Sadly, the binder lookups can not be declared initialized -- that results in
// an "initialization loop" compile error.
func init() {
KindBinders[reflect.Int] = IntBinder
KindBinders[reflect.Int8] = IntBinder
KindBinders[reflect.Int16] = IntBinder
KindBinders[reflect.Int32] = IntBinder
KindBinders[reflect.Int64] = IntBinder
KindBinders[reflect.Uint] = UintBinder
KindBinders[reflect.Uint8] = UintBinder
KindBinders[reflect.Uint16] = UintBinder
KindBinders[reflect.Uint32] = UintBinder
KindBinders[reflect.Uint64] = UintBinder
KindBinders[reflect.Float32] = FloatBinder
KindBinders[reflect.Float64] = FloatBinder
KindBinders[reflect.String] = StringBinder
KindBinders[reflect.Bool] = BoolBinder
KindBinders[reflect.Slice] = Binder{bindSlice, unbindSlice}
KindBinders[reflect.Struct] = Binder{bindStruct, unbindStruct}
KindBinders[reflect.Ptr] = PointerBinder
KindBinders[reflect.Map] = MapBinder
TypeBinders[reflect.TypeOf(time.Time{})] = TimeBinder
// Uploads
TypeBinders[reflect.TypeOf(&os.File{})] = Binder{bindFile, nil}
TypeBinders[reflect.TypeOf([]byte{})] = Binder{bindByteArray, nil}
TypeBinders[reflect.TypeOf((*io.Reader)(nil)).Elem()] = Binder{bindReadSeeker, nil}
TypeBinders[reflect.TypeOf((*io.ReadSeeker)(nil)).Elem()] = Binder{bindReadSeeker, nil}
OnAppStart(func() {
DateTimeFormat = Config.StringDefault("format.datetime", DefaultDateTimeFormat)
DateFormat = Config.StringDefault("format.date", DefaultDateFormat)
TimeFormats = append(TimeFormats, DateTimeFormat, DateFormat)
})
}
================================================
FILE: binder_test.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package revel
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"reflect"
"sort"
"strings"
"testing"
"time"
"github.com/revel/config"
)
type A struct {
ID int
Name string
B B
//nolint:unused
private int
}
type B struct {
Extra string
}
var (
ParamTestValues = map[string][]string{
"int": {"1"},
"int8": {"1"},
"int16": {"1"},
"int32": {"1"},
"int64": {"1"},
"uint": {"1"},
"uint8": {"1"},
"uint16": {"1"},
"uint32": {"1"},
"uint64": {"1"},
"float32": {"1.000000"},
"float64": {"1.000000"},
"str": {"hello"},
"bool-true": {"true"},
"bool-1": {"1"},
"bool-on": {"on"},
"bool-false": {"false"},
"bool-0": {"0"},
"bool-0.0": {"0.0"},
"bool-off": {"off"},
"bool-f": {"f"},
"date": {"1982-07-09"},
"datetime": {"1982-07-09 21:30"},
"customDate": {"07/09/1982"},
"arr[0]": {"1"},
"arr[1]": {"2"},
"arr[3]": {"3"},
"uarr[]": {"1", "2"},
"arruarr[0][]": {"1", "2"},
"arruarr[1][]": {"3", "4"},
"2darr[0][0]": {"0"},
"2darr[0][1]": {"1"},
"2darr[1][0]": {"10"},
"2darr[1][1]": {"11"},
"A.ID": {"123"},
"A.Name": {"rob"},
"B.ID": {"123"},
"B.Name": {"rob"},
"B.B.Extra": {"hello"},
"pB.ID": {"123"},
"pB.Name": {"rob"},
"pB.B.Extra": {"hello"},
"priv.private": {"123"},
"arrC[0].ID": {"5"},
"arrC[0].Name": {"rob"},
"arrC[0].B.Extra": {"foo"},
"arrC[1].ID": {"8"},
"arrC[1].Name": {"bill"},
"m[a]": {"foo"},
"m[b]": {"bar"},
"m2[1]": {"foo"},
"m2[2]": {"bar"},
"m3[a]": {"1"},
"m3[b]": {"2"},
"m4[a].ID": {"1"},
"m4[a].Name": {"foo"},
"m4[b].ID": {"2"},
"m4[b].Name": {"bar"},
"mapWithAMuchLongerName[a].ID": {"1"},
"mapWithAMuchLongerName[a].Name": {"foo"},
"mapWithAMuchLongerName[b].ID": {"2"},
"mapWithAMuchLongerName[b].Name": {"bar"},
"invalidInt": {"xyz"},
"invalidInt2": {""},
"invalidBool": {"xyz"},
"invalidArr": {"xyz"},
"int8-overflow": {"1024"},
"uint8-overflow": {"1024"},
"arrDoS[2]": {"2"},
"arrDoS[65535]": {"65535"},
}
testDate = time.Date(1982, time.July, 9, 0, 0, 0, 0, time.UTC)
testDatetime = time.Date(1982, time.July, 9, 21, 30, 0, 0, time.UTC)
)
var binderTestCases = map[string]interface{}{
"int": 1,
"int8": int8(1),
"int16": int16(1),
"int32": int32(1),
"int64": int64(1),
"uint": 1,
"uint8": uint8(1),
"uint16": uint16(1),
"uint32": uint32(1),
"uint64": uint64(1),
"float32": float32(1.0),
"float64": float64(1.0),
"str": "hello",
"bool-true": true,
"bool-1": true,
"bool-on": true,
"bool-false": false,
"bool-0": false,
"bool-0.0": false,
"bool-off": false,
"bool-f": false,
"date": testDate,
"datetime": testDatetime,
"customDate": testDate,
"arr": []int{1, 2, 0, 3},
"uarr": []int{1, 2},
"arruarr": [][]int{{1, 2}, {3, 4}},
"2darr": [][]int{{0, 1}, {10, 11}},
"A": A{ID: 123, Name: "rob"},
"B": A{ID: 123, Name: "rob", B: B{Extra: "hello"}},
"pB": &A{ID: 123, Name: "rob", B: B{Extra: "hello"}},
"arrC": []A{
{
ID: 5,
Name: "rob",
B: B{"foo"},
},
{
ID: 8,
Name: "bill",
},
},
"m": map[string]string{"a": "foo", "b": "bar"},
"m2": map[int]string{1: "foo", 2: "bar"},
"m3": map[string]int{"a": 1, "b": 2},
"m4": map[string]A{"a": {ID: 1, Name: "foo"}, "b": {ID: 2, Name: "bar"}},
// NOTE: We also include a map with a longer name than the others since this has caused problems
// described in github issue #1285, resolved in pull request #1344. This test case should
// prevent regression.
"mapWithAMuchLongerName": map[string]A{"a": {ID: 1, Name: "foo"}, "b": {ID: 2, Name: "bar"}},
// TODO: Tests that use TypeBinders
// Invalid value tests (the result should always be the zero value for that type)
// The point of these is to ensure that invalid user input does not cause panics.
"invalidInt": 0,
"invalidInt2": 0,
"invalidBool": true,
"invalidArr": []int{},
"priv": A{},
"int8-overflow": int8(0),
"uint8-overflow": uint8(0),
"arrDoS": []int{0, 0, 2},
}
// Types that files may be bound to, and a func that can read the content from
// that type.
// TODO: Is there any way to create a slice, given only the element Type?
var fileBindings = []struct{ val, arrval, f interface{} }{
{(**os.File)(nil), []*os.File{}, ioutil.ReadAll},
{(*[]byte)(nil), [][]byte{}, func(b []byte) []byte { return b }},
{(*io.Reader)(nil), []io.Reader{}, ioutil.ReadAll},
{(*io.ReadSeeker)(nil), []io.ReadSeeker{}, ioutil.ReadAll},
}
func TestJsonBinder(t *testing.T) {
// create a structure to be populated
{
d, _ := json.Marshal(map[string]int{"a": 1})
params := &Params{JSON: d}
foo := struct{ A int }{}
c := NewTestController(nil, getMultipartRequest())
ParseParams(params, NewRequest(c.Request.In))
actual := Bind(params, "test", reflect.TypeOf(foo))
valEq(t, "TestJsonBinder", reflect.ValueOf(actual.Interface().(struct{ A int }).A), reflect.ValueOf(1))
}
{
d, _ := json.Marshal(map[string]interface{}{"a": map[string]int{"b": 45}})
params := &Params{JSON: d}
testMap := map[string]interface{}{}
actual := Bind(params, "test", reflect.TypeOf(testMap)).Interface().(map[string]interface{})
if actual["a"].(map[string]interface{})["b"].(float64) != 45 {
t.Errorf("Failed to fetch map value %#v", actual["a"])
}
// Check to see if a named map works
actualb := Bind(params, "test", reflect.TypeOf(map[string]map[string]float64{})).Interface().(map[string]map[string]float64)
if actualb["a"]["b"] != 45 {
t.Errorf("Failed to fetch map value %#v", actual["a"])
}
}
}
func TestBinder(t *testing.T) {
// Reuse the mvc_test.go multipart request to test the binder.
params := &Params{}
c := NewTestController(nil, getMultipartRequest())
if Config == nil {
Config = config.NewContext()
defer func() {
Config = nil
}()
}
ParseParams(params, NewRequest(c.Request.In))
params.Values = ParamTestValues
// Values
for k, v := range binderTestCases {
actual := Bind(params, k, reflect.TypeOf(v))
expected := reflect.ValueOf(v)
valEq(t, k, actual, expected)
}
// Files
// Get the keys in sorted order to make the expectation right.
keys := []string{}
for k := range expectedFiles {
keys = append(keys, k)
}
sort.Strings(keys)
expectedBoundFiles := make(map[string][]fh)
for _, k := range keys {
fhs := expectedFiles[k]
k := nextKey(k)
expectedBoundFiles[k] = append(expectedBoundFiles[k], fhs...)
}
for k, fhs := range expectedBoundFiles {
if len(fhs) == 1 {
// Test binding single files to: *os.File, []byte, io.Reader, io.ReadSeeker
for _, binding := range fileBindings {
typ := reflect.TypeOf(binding.val).Elem()
actual := Bind(params, k, typ)
if !actual.IsValid() || (actual.Kind() == reflect.Interface && actual.IsNil()) {
t.Errorf("%s (%s) - Returned nil.", k, typ)
continue
}
returns := reflect.ValueOf(binding.f).Call([]reflect.Value{actual})
valEq(t, k, returns[0], reflect.ValueOf(fhs[0].content))
}
} else {
// Test binding multi to:
// []*os.File, [][]byte, []io.Reader, []io.ReadSeeker
for _, binding := range fileBindings {
typ := reflect.TypeOf(binding.arrval)
actual := Bind(params, k, typ)
if actual.Len() != len(fhs) {
t.Fatalf("%s (%s) - Number of files: (expected) %d != %d (actual)",
k, typ, len(fhs), actual.Len())
}
for i := range fhs {
returns := reflect.ValueOf(binding.f).Call([]reflect.Value{actual.Index(i)})
if !returns[0].IsValid() {
t.Errorf("%s (%s) - Returned nil.", k, typ)
continue
}
valEq(t, k, returns[0], reflect.ValueOf(fhs[i].content))
}
}
}
}
}
// Unbinding tests
var unbinderTestCases = map[string]interface{}{
"int": 1,
"int8": int8(1),
"int16": int16(1),
"int32": int32(1),
"int64": int64(1),
"uint": 1,
"uint8": uint8(1),
"uint16": uint16(1),
"uint32": uint32(1),
"uint64": uint64(1),
"float32": float32(1.0),
"float64": float64(1.0),
"str": "hello",
"bool-true": true,
"bool-false": false,
"date": testDate,
"datetime": testDatetime,
"arr": []int{1, 2, 0, 3},
"2darr": [][]int{{0, 1}, {10, 11}},
"A": A{ID: 123, Name: "rob"},
"B": A{ID: 123, Name: "rob", B: B{Extra: "hello"}},
"pB": &A{ID: 123, Name: "rob", B: B{Extra: "hello"}},
"arrC": []A{
{
ID: 5,
Name: "rob",
B: B{"foo"},
},
{
ID: 8,
Name: "bill",
},
},
"m": map[string]string{"a": "foo", "b": "bar"},
"m2": map[int]string{1: "foo", 2: "bar"},
"m3": map[string]int{"a": 1, "b": 2},
}
// Some of the unbinding results are not exactly what is in ParamTestValues, since it
// serializes implicit zero values explicitly.
var unbinderOverrideAnswers = map[string]map[string]string{
"arr": {
"arr[0]": "1",
"arr[1]": "2",
"arr[2]": "0",
"arr[3]": "3",
},
"A": {
"A.ID": "123",
"A.Name": "rob",
"A.B.Extra": "",
},
"arrC": {
"arrC[0].ID": "5",
"arrC[0].Name": "rob",
"arrC[0].B.Extra": "foo",
"arrC[1].ID": "8",
"arrC[1].Name": "bill",
"arrC[1].B.Extra": "",
},
"m": {"m[a]": "foo", "m[b]": "bar"},
"m2": {"m2[1]": "foo", "m2[2]": "bar"},
"m3": {"m3[a]": "1", "m3[b]": "2"},
}
func TestUnbinder(t *testing.T) {
for k, v := range unbinderTestCases {
actual := make(map[string]string)
Unbind(actual, k, v)
// Get the expected key/values.
expected, ok := unbinderOverrideAnswers[k]
if !ok {
expected = make(map[string]string)
for k2, v2 := range ParamTestValues {
if k == k2 || strings.HasPrefix(k2, k+".") || strings.HasPrefix(k2, k+"[") {
expected[k2] = v2[0]
}
}
}
// Compare length and values.
if len(actual) != len(expected) {
t.Errorf("Length mismatch\nExpected length %d, actual %d\nExpected: %s\nActual: %s",
len(expected), len(actual), expected, actual)
}
for k, v := range actual {
if expected[k] != v {
t.Errorf("Value mismatch.\nExpected: %s\nActual: %s", expected, actual)
}
}
}
}
// Helpers
func valEq(t *testing.T, name string, actual, expected reflect.Value) {
switch expected.Kind() {
case reflect.Slice:
// Check the type/length/element type
if !eq(t, name+" (type)", actual.Kind(), expected.Kind()) ||
!eq(t, name+" (len)", actual.Len(), expected.Len()) ||
!eq(t, name+" (elem)", actual.Type().Elem(), expected.Type().Elem()) {
return
}
// Check value equality for each element.
for i := 0; i < actual.Len(); i++ {
valEq(t, fmt.Sprintf("%s[%d]", name, i), actual.Index(i), expected.Index(i))
}
case reflect.Ptr:
// Check equality on the element type.
valEq(t, name, actual.Elem(), expected.Elem())
case reflect.Map:
if !eq(t, name+" (len)", actual.Len(), expected.Len()) {
return
}
for _, key := range expected.MapKeys() {
expectedValue := expected.MapIndex(key)
actualValue := actual.MapIndex(key)
if actualValue.IsValid() {
valEq(t, fmt.Sprintf("%s[%s]", name, key), actualValue, expectedValue)
} else {
t.Errorf("Expected key %s not found", key)
}
}
default:
eq(t, name, actual.Interface(), expected.Interface())
}
}
func init() {
DateFormat = DefaultDateFormat
DateTimeFormat = DefaultDateTimeFormat
TimeFormats = append(TimeFormats, DefaultDateFormat, DefaultDateTimeFormat, "01/02/2006")
}
================================================
FILE: cache/cache.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package cache
import (
"errors"
"time"
)
// Length of time to cache an item.
const (
DefaultExpiryTime = time.Duration(0)
ForEverNeverExpiry = time.Duration(-1)
)
// Getter is an interface for getting / decoding an element from a cache.
type Getter interface {
// Get the content associated with the given key. decoding it into the given
// pointer.
//
// Returns:
// - nil if the value was successfully retrieved and ptrValue set
// - ErrCacheMiss if the value was not in the cache
// - an implementation specific error otherwise
Get(key string, ptrValue interface{}) error
}
// Cache is an interface to an expiring cache. It behaves (and is modeled) like
// the Memcached interface. It is keyed by strings (250 bytes at most).
//
// Many callers will make exclusive use of Set and Get, but more exotic
// functions are also available.
//
// Example
//
// Here is a typical Get/Set interaction:
//
// var items []*Item
// if err := cache.Get("items", &items); err != nil {
// items = loadItems()
// go cache.Set("items", items, cache.DefaultExpiryTime)
// }
//
// Note that the caller will frequently not wait for Set() to complete.
//
// Errors
//
// It is assumed that callers will infrequently check returned errors, since any
// request should be fulfillable without finding anything in the cache. As a
// result, all errors other than ErrCacheMiss and ErrNotStored will be logged to
// revel.ERROR, so that the developer does not need to check the return value to
// discover things like deserialization or connection errors.
type Cache interface {
// The Cache implements a Getter.
Getter
// Set the given key/value in the cache, overwriting any existing value
// associated with that key. Keys may be at most 250 bytes in length.
//
// Returns:
// - nil on success
// - an implementation specific error otherwise
Set(key string, value interface{}, expires time.Duration) error
// Get the content associated multiple keys at once. On success, the caller
// may decode the values one at a time from the returned Getter.
//
// Returns:
// - the value getter, and a nil error if the operation completed.
// - an implementation specific error otherwise
GetMulti(keys ...string) (Getter, error)
// Delete the given key from the cache.
//
// Returns:
// - nil on a successful delete
// - ErrCacheMiss if the value was not in the cache
// - an implementation specific error otherwise
Delete(key string) error
// Add the given key/value to the cache ONLY IF the key does not already exist.
//
// Returns:
// - nil if the value was added to the cache
// - ErrNotStored if the key was already present in the cache
// - an implementation-specific error otherwise
Add(key string, value interface{}, expires time.Duration) error
// Set the given key/value in the cache ONLY IF the key already exists.
//
// Returns:
// - nil if the value was replaced
// - ErrNotStored if the key does not exist in the cache
// - an implementation specific error otherwise
Replace(key string, value interface{}, expires time.Duration) error
// Increment the value stored at the given key by the given amount.
// The value silently wraps around upon exceeding the uint64 range.
//
// Returns the new counter value if the operation was successful, or:
// - ErrCacheMiss if the key was not found in the cache
// - an implementation specific error otherwise
Increment(key string, n uint64) (newValue uint64, err error)
// Decrement the value stored at the given key by the given amount.
// The value is capped at 0 on underflow, with no error returned.
//
// Returns the new counter value if the operation was successful, or:
// - ErrCacheMiss if the key was not found in the cache
// - an implementation specific error otherwise
Decrement(key string, n uint64) (newValue uint64, err error)
// Expire all cache entries immediately.
// This is not implemented for the memcached cache (intentionally).
// Returns an implementation specific error if the operation failed.
Flush() error
}
var (
Instance Cache
ErrCacheMiss = errors.New("revel/cache: key not found")
ErrNotStored = errors.New("revel/cache: not stored")
ErrInvalidValue = errors.New("revel/cache: invalid value")
)
// The package implements the Cache interface (as sugar).
func Get(key string, ptrValue interface{}) error { return Instance.Get(key, ptrValue) }
func GetMulti(keys ...string) (Getter, error) { return Instance.GetMulti(keys...) }
func Delete(key string) error { return Instance.Delete(key) }
func Increment(key string, n uint64) (newValue uint64, err error) { return Instance.Increment(key, n) }
func Decrement(key string, n uint64) (newValue uint64, err error) { return Instance.Decrement(key, n) }
func Flush() error { return Instance.Flush() }
func Set(key string, value interface{}, expires time.Duration) error {
return Instance.Set(key, value, expires)
}
func Add(key string, value interface{}, expires time.Duration) error {
return Instance.Add(key, value, expires)
}
func Replace(key string, value interface{}, expires time.Duration) error {
return Instance.Replace(key, value, expires)
}
================================================
FILE: cache/cache_test.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package cache
import (
"math"
"testing"
"time"
)
// Tests against a generic Cache interface.
// They should pass for all implementations.
type cacheFactory func(*testing.T, time.Duration) Cache
// Test typical cache interactions.
func typicalGetSet(t *testing.T, newCache cacheFactory) {
var err error
cache := newCache(t, time.Hour)
value := "foo"
if err = cache.Set("value", value, DefaultExpiryTime); err != nil {
t.Errorf("Error setting a value: %s", err)
}
value = ""
err = cache.Get("value", &value)
if err != nil {
t.Errorf("Error getting a value: %s", err)
}
if value != "foo" {
t.Errorf("Expected to get foo back, got %s", value)
}
}
// Test the increment-decrement cases.
func incrDecr(t *testing.T, newCache cacheFactory) {
var err error
cache := newCache(t, time.Hour)
// Normal increment / decrement operation.
if err = cache.Set("int", 10, ForEverNeverExpiry); err != nil {
t.Errorf("Error setting int: %s", err)
}
time.Sleep(time.Second)
newValue, err := cache.Increment("int", 50)
if err != nil {
t.Errorf("Error incrementing int: %s", err)
}
if newValue != 60 {
t.Errorf("Expected 60, was %d", newValue)
}
if newValue, err = cache.Decrement("int", 50); err != nil {
t.Errorf("Error decrementing: %s", err)
}
if newValue != 10 {
t.Errorf("Expected 10, was %d", newValue)
}
// Increment wraparound
newValue, err = cache.Increment("int", math.MaxUint64-5)
if err != nil {
t.Errorf("Error wrapping around: %s", err)
}
if newValue != 4 {
t.Errorf("Expected wraparound 4, got %d", newValue)
}
// Decrement capped at 0
newValue, err = cache.Decrement("int", 25)
if err != nil {
t.Errorf("Error decrementing below 0: %s", err)
}
if newValue != 0 {
t.Errorf("Expected capped at 0, got %d", newValue)
}
}
func expiration(t *testing.T, newCache cacheFactory) {
// memcached does not support expiration times less than 1 second.
var err error
cache := newCache(t, time.Second)
// Test Set w/ DefaultExpiryTime
value := 10
if err = cache.Set("int", value, DefaultExpiryTime); err != nil {
t.Errorf("Set failed: %s", err)
}
time.Sleep(2 * time.Second)
if err = cache.Get("int", &value); err != ErrCacheMiss {
t.Errorf("Expected CacheMiss, but got: %s", err)
}
// Test Set w/ short time
if err = cache.Set("int", value, time.Second); err != nil {
t.Errorf("Set failed: %s", err)
}
time.Sleep(2 * time.Second)
if err = cache.Get("int", &value); err != ErrCacheMiss {
t.Errorf("Expected CacheMiss, but got: %s", err)
}
// Test Set w/ longer time.
if err = cache.Set("int", value, time.Hour); err != nil {
t.Errorf("Set failed: %s", err)
}
time.Sleep(2 * time.Second)
if err = cache.Get("int", &value); err != nil {
t.Errorf("Expected to get the value, but got: %s", err)
}
// Test Set w/ forever.
if err = cache.Set("int", value, ForEverNeverExpiry); err != nil {
t.Errorf("Set failed: %s", err)
}
time.Sleep(2 * time.Second)
if err = cache.Get("int", &value); err != nil {
t.Errorf("Expected to get the value, but got: %s", err)
}
}
func emptyCache(t *testing.T, newCache cacheFactory) {
var err error
cache := newCache(t, time.Hour)
err = cache.Get("notexist", 0)
if err == nil {
t.Errorf("Error expected for non-existent key")
}
if err != ErrCacheMiss {
t.Errorf("Expected ErrCacheMiss for non-existent key: %s", err)
}
err = cache.Delete("notexist")
if err != ErrCacheMiss {
t.Errorf("Expected ErrCacheMiss for non-existent key: %s", err)
}
_, err = cache.Increment("notexist", 1)
if err != ErrCacheMiss {
t.Errorf("Expected cache miss incrementing non-existent key: %s", err)
}
_, err = cache.Decrement("notexist", 1)
if err != ErrCacheMiss {
t.Errorf("Expected cache miss decrementing non-existent key: %s", err)
}
}
func testReplace(t *testing.T, newCache cacheFactory) {
var err error
cache := newCache(t, time.Hour)
// Replace in an empty cache.
if err = cache.Replace("notexist", 1, ForEverNeverExpiry); err != ErrNotStored {
t.Errorf("Replace in empty cache: expected ErrNotStored, got: %s", err)
}
// Set a value of 1, and replace it with 2
if err = cache.Set("int", 1, time.Second); err != nil {
t.Errorf("Unexpected error: %s", err)
}
if err = cache.Replace("int", 2, time.Second); err != nil {
t.Errorf("Unexpected error: %s", err)
}
var i int
if err = cache.Get("int", &i); err != nil {
t.Errorf("Unexpected error getting a replaced item: %s", err)
}
if i != 2 {
t.Errorf("Expected 2, got %d", i)
}
// Wait for it to expire and replace with 3 (unsuccessfully).
time.Sleep(2 * time.Second)
if err = cache.Replace("int", 3, time.Second); err != ErrNotStored {
t.Errorf("Expected ErrNotStored, got: %s", err)
}
if err = cache.Get("int", &i); err != ErrCacheMiss {
t.Errorf("Expected cache miss, got: %s", err)
}
}
func testAdd(t *testing.T, newCache cacheFactory) {
var err error
cache := newCache(t, time.Hour)
// Add to an empty cache.
if err = cache.Add("int", 1, time.Second*3); err != nil {
t.Errorf("Unexpected error adding to empty cache: %s", err)
}
// Try to add again. (fail)
if err = cache.Add("int", 2, time.Second*3); err != nil {
if err != ErrNotStored {
t.Errorf("Expected ErrNotStored adding dupe to cache: %s", err)
}
}
// Wait for it to expire, and add again.
time.Sleep(8 * time.Second)
if err = cache.Add("int", 3, time.Second*5); err != nil {
t.Errorf("Unexpected error adding to cache: %s", err)
}
// Get and verify the value.
var i int
if err = cache.Get("int", &i); err != nil {
t.Errorf("Unexpected error: %s", err)
}
if i != 3 {
t.Errorf("Expected 3, got: %d", i)
}
}
func testGetMulti(t *testing.T, newCache cacheFactory) {
cache := newCache(t, time.Hour)
m := map[string]interface{}{
"str": "foo",
"num": 42,
"foo": struct{ Bar string }{"baz"},
}
var keys []string
for key, value := range m {
keys = append(keys, key)
if err := cache.Set(key, value, time.Second*30); err != nil {
t.Errorf("Error setting a value: %s", err)
}
}
g, err := cache.GetMulti(keys...)
if err != nil {
t.Errorf("Error in get-multi: %s", err)
}
var str string
if err = g.Get("str", &str); err != nil || str != "foo" {
t.Errorf("Error getting str: %s / %s", err, str)
}
var num int
if err = g.Get("num", &num); err != nil || num != 42 {
t.Errorf("Error getting num: %s / %v", err, num)
}
var foo struct{ Bar string }
if err = g.Get("foo", &foo); err != nil || foo.Bar != "baz" {
t.Errorf("Error getting foo: %s / %v", err, foo)
}
}
================================================
FILE: cache/init.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package cache
import (
"strings"
"time"
"github.com/revel/revel"
)
var cacheLog = revel.RevelLog.New("section", "cache")
func init() {
revel.OnAppStart(func() {
// Set the default expiration time.
defaultExpiration := time.Hour // The default for the default is one hour.
if expireStr, found := revel.Config.String("cache.expires"); found {
var err error
if defaultExpiration, err = time.ParseDuration(expireStr); err != nil {
cacheLog.Panic("Could not parse default cache expiration duration " + expireStr + ": " + err.Error())
}
}
// make sure you aren't trying to use both memcached and redis
if revel.Config.BoolDefault("cache.memcached", false) && revel.Config.BoolDefault("cache.redis", false) {
cacheLog.Panic("You've configured both memcached and redis, please only include configuration for one cache!")
}
// Use memcached?
if revel.Config.BoolDefault("cache.memcached", false) {
hosts := strings.Split(revel.Config.StringDefault("cache.hosts", ""), ",")
if len(hosts) == 0 {
cacheLog.Panic("Memcache enabled but no memcached hosts specified!")
}
Instance = NewMemcachedCache(hosts, defaultExpiration)
return
}
// Use Redis (share same config as memcached)?
if revel.Config.BoolDefault("cache.redis", false) {
hosts := strings.Split(revel.Config.StringDefault("cache.hosts", ""), ",")
if len(hosts) == 0 {
cacheLog.Panic("Redis enabled but no Redis hosts specified!")
}
if len(hosts) > 1 {
cacheLog.Panic("Redis currently only supports one host!")
}
password := revel.Config.StringDefault("cache.redis.password", "")
Instance = NewRedisCache(hosts[0], password, defaultExpiration)
return
}
// By default, use the in-memory cache.
Instance = NewInMemoryCache(defaultExpiration)
})
}
================================================
FILE: cache/inmemory.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package cache
import (
"fmt"
"reflect"
"sync"
"time"
"github.com/patrickmn/go-cache"
)
type InMemoryCache struct {
cache cache.Cache // Only expose the methods we want to make available
mu sync.RWMutex // For increment / decrement prevent reads and writes
}
func NewInMemoryCache(defaultExpiration time.Duration) InMemoryCache {
return InMemoryCache{cache: *cache.New(defaultExpiration, time.Minute), mu: sync.RWMutex{}}
}
func (c InMemoryCache) Get(key string, ptrValue interface{}) error {
c.mu.RLock()
defer c.mu.RUnlock()
value, found := c.cache.Get(key)
if !found {
return ErrCacheMiss
}
v := reflect.ValueOf(ptrValue)
if v.Type().Kind() == reflect.Ptr && v.Elem().CanSet() {
v.Elem().Set(reflect.ValueOf(value))
return nil
}
err := fmt.Errorf("revel/cache: attempt to get %s, but can not set value %v", key, v)
cacheLog.Error(err.Error())
return err
}
func (c InMemoryCache) GetMulti(keys ...string) (Getter, error) {
return c, nil
}
func (c InMemoryCache) Set(key string, value interface{}, expires time.Duration) error {
c.mu.Lock()
defer c.mu.Unlock()
// NOTE: go-cache understands the values of DefaultExpiryTime and ForEverNeverExpiry
c.cache.Set(key, value, expires)
return nil
}
func (c InMemoryCache) Add(key string, value interface{}, expires time.Duration) error {
c.mu.Lock()
defer c.mu.Unlock()
err := c.cache.Add(key, value, expires)
if err != nil {
return ErrNotStored
}
return err
}
func (c InMemoryCache) Replace(key string, value interface{}, expires time.Duration) error {
c.mu.Lock()
defer c.mu.Unlock()
if err := c.cache.Replace(key, value, expires); err != nil {
return ErrNotStored
}
return nil
}
func (c InMemoryCache) Delete(key string) error {
c.mu.RLock()
defer c.mu.RUnlock()
if _, found := c.cache.Get(key); !found {
return ErrCacheMiss
}
c.cache.Delete(key)
return nil
}
func (c InMemoryCache) Increment(key string, n uint64) (newValue uint64, err error) {
c.mu.Lock()
defer c.mu.Unlock()
if _, found := c.cache.Get(key); !found {
return 0, ErrCacheMiss
}
if err = c.cache.Increment(key, int64(n)); err != nil {
return
}
return c.convertTypeToUint64(key)
}
func (c InMemoryCache) Decrement(key string, n uint64) (newValue uint64, err error) {
c.mu.Lock()
defer c.mu.Unlock()
if nv, err := c.convertTypeToUint64(key); err != nil {
return 0, err
} else {
// Stop from going below zero
if n > nv {
n = nv
}
}
if err = c.cache.Decrement(key, int64(n)); err != nil {
return
}
return c.convertTypeToUint64(key)
}
func (c InMemoryCache) Flush() error {
c.mu.Lock()
defer c.mu.Unlock()
c.cache.Flush()
return nil
}
// Fetches and returns the converted type to a uint64.
func (c InMemoryCache) convertTypeToUint64(key string) (newValue uint64, err error) {
v, found := c.cache.Get(key)
if !found {
return newValue, ErrCacheMiss
}
switch v.(type) {
case int:
newValue = uint64(v.(int))
case int8:
newValue = uint64(v.(int8))
case int16:
newValue = uint64(v.(int16))
case int32:
newValue = uint64(v.(int32))
case int64:
newValue = uint64(v.(int64))
case uint:
newValue = uint64(v.(uint))
case uintptr:
newValue = uint64(v.(uintptr))
case uint8:
newValue = uint64(v.(uint8))
case uint16:
newValue = uint64(v.(uint16))
case uint32:
newValue = uint64(v.(uint32))
case uint64:
newValue = v.(uint64)
case float32:
newValue = uint64(v.(float32))
case float64:
newValue = uint64(v.(float64))
default:
err = ErrInvalidValue
}
return
}
================================================
FILE: cache/inmemory_test.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package cache
import (
"testing"
"time"
)
var newInMemoryCache = func(_ *testing.T, defaultExpiration time.Duration) Cache {
return NewInMemoryCache(defaultExpiration)
}
// Test typical cache interactions.
func TestInMemoryCache_TypicalGetSet(t *testing.T) {
typicalGetSet(t, newInMemoryCache)
}
// Test the increment-decrement cases.
func TestInMemoryCache_IncrDecr(t *testing.T) {
incrDecr(t, newInMemoryCache)
}
func TestInMemoryCache_Expiration(t *testing.T) {
expiration(t, newInMemoryCache)
}
func TestInMemoryCache_EmptyCache(t *testing.T) {
emptyCache(t, newInMemoryCache)
}
func TestInMemoryCache_Replace(t *testing.T) {
testReplace(t, newInMemoryCache)
}
func TestInMemoryCache_Add(t *testing.T) {
testAdd(t, newInMemoryCache)
}
func TestInMemoryCache_GetMulti(t *testing.T) {
testGetMulti(t, newInMemoryCache)
}
================================================
FILE: cache/memcached.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package cache
import (
"errors"
"time"
"github.com/bradfitz/gomemcache/memcache"
"github.com/revel/revel/logger"
)
// MemcachedCache wraps the Memcached client to meet the Cache interface.
type MemcachedCache struct {
*memcache.Client
defaultExpiration time.Duration
}
func NewMemcachedCache(hostList []string, defaultExpiration time.Duration) MemcachedCache {
return MemcachedCache{memcache.New(hostList...), defaultExpiration}
}
func (c MemcachedCache) Set(key string, value interface{}, expires time.Duration) error {
return c.invoke((*memcache.Client).Set, key, value, expires)
}
func (c MemcachedCache) Add(key string, value interface{}, expires time.Duration) error {
return c.invoke((*memcache.Client).Add, key, value, expires)
}
func (c MemcachedCache) Replace(key string, value interface{}, expires time.Duration) error {
return c.invoke((*memcache.Client).Replace, key, value, expires)
}
func (c MemcachedCache) Get(key string, ptrValue interface{}) error {
item, err := c.Client.Get(key)
if err != nil {
return convertMemcacheError(err)
}
return Deserialize(item.Value, ptrValue)
}
func (c MemcachedCache) GetMulti(keys ...string) (Getter, error) {
items, err := c.Client.GetMulti(keys)
if err != nil {
return nil, convertMemcacheError(err)
}
return ItemMapGetter(items), nil
}
func (c MemcachedCache) Delete(key string) error {
return convertMemcacheError(c.Client.Delete(key))
}
func (c MemcachedCache) Increment(key string, delta uint64) (newValue uint64, err error) {
newValue, err = c.Client.Increment(key, delta)
return newValue, convertMemcacheError(err)
}
func (c MemcachedCache) Decrement(key string, delta uint64) (newValue uint64, err error) {
newValue, err = c.Client.Decrement(key, delta)
return newValue, convertMemcacheError(err)
}
func (c MemcachedCache) Flush() error {
err := errors.New("Flush: can not flush memcached")
cacheLog.Error(err.Error())
return err
}
func (c MemcachedCache) invoke(f func(*memcache.Client, *memcache.Item) error,
key string, value interface{}, expires time.Duration) error {
switch expires {
case DefaultExpiryTime:
expires = c.defaultExpiration
case ForEverNeverExpiry:
expires = time.Duration(0)
}
b, err := Serialize(value)
if err != nil {
return err
}
return convertMemcacheError(f(c.Client, &memcache.Item{
Key: key,
Value: b,
Expiration: int32(expires / time.Second),
}))
}
// ItemMapGetter implements a Getter on top of the returned item map.
type ItemMapGetter map[string]*memcache.Item
func (g ItemMapGetter) Get(key string, ptrValue interface{}) error {
item, ok := g[key]
if !ok {
return ErrCacheMiss
}
return Deserialize(item.Value, ptrValue)
}
func convertMemcacheError(err error) error {
switch err {
case nil:
return nil
case memcache.ErrCacheMiss:
return ErrCacheMiss
case memcache.ErrNotStored:
return ErrNotStored
}
cacheLog.Error("convertMemcacheError:", "error", err, "trace", logger.NewCallStack())
return err
}
================================================
FILE: cache/memcached_test.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package cache
import (
"net"
"testing"
"time"
)
// These tests require memcached running on localhost:11211 (the default).
const testServer = "localhost:11211"
var newMemcachedCache = func(t *testing.T, defaultExpiration time.Duration) Cache {
c, err := net.Dial("tcp", testServer)
if err == nil {
if _, err = c.Write([]byte("flush_all\r\n")); err != nil {
t.Errorf("Write failed: %s", err)
}
_ = c.Close()
return NewMemcachedCache([]string{testServer}, defaultExpiration)
}
t.Errorf("couldn't connect to memcached on %s", testServer)
t.FailNow()
panic("")
}
func TestMemcachedCache_TypicalGetSet(t *testing.T) {
typicalGetSet(t, newMemcachedCache)
}
func TestMemcachedCache_IncrDecr(t *testing.T) {
incrDecr(t, newMemcachedCache)
}
func TestMemcachedCache_Expiration(t *testing.T) {
expiration(t, newMemcachedCache)
}
func TestMemcachedCache_EmptyCache(t *testing.T) {
emptyCache(t, newMemcachedCache)
}
func TestMemcachedCache_Replace(t *testing.T) {
testReplace(t, newMemcachedCache)
}
func TestMemcachedCache_Add(t *testing.T) {
testAdd(t, newMemcachedCache)
}
func TestMemcachedCache_GetMulti(t *testing.T) {
testGetMulti(t, newMemcachedCache)
}
================================================
FILE: cache/redis.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package cache
import (
"time"
"github.com/gomodule/redigo/redis"
"github.com/revel/revel"
)
// RedisCache wraps the Redis client to meet the Cache interface.
type RedisCache struct {
pool *redis.Pool
defaultExpiration time.Duration
}
// NewRedisCache returns a new RedisCache with given parameters
// until redigo supports sharding/clustering, only one host will be in hostList.
func NewRedisCache(host string, password string, defaultExpiration time.Duration) RedisCache {
pool := &redis.Pool{
MaxIdle: revel.Config.IntDefault("cache.redis.maxidle", 5),
MaxActive: revel.Config.IntDefault("cache.redis.maxactive", 0),
IdleTimeout: time.Duration(revel.Config.IntDefault("cache.redis.idletimeout", 240)) * time.Second,
Dial: func() (redis.Conn, error) {
protocol := revel.Config.StringDefault("cache.redis.protocol", "tcp")
toc := time.Millisecond * time.Duration(revel.Config.IntDefault("cache.redis.timeout.connect", 10000))
tor := time.Millisecond * time.Duration(revel.Config.IntDefault("cache.redis.timeout.read", 5000))
tow := time.Millisecond * time.Duration(revel.Config.IntDefault("cache.redis.timeout.write", 5000))
c, err := redis.Dial(protocol, host,
redis.DialConnectTimeout(toc),
redis.DialReadTimeout(tor),
redis.DialWriteTimeout(tow))
if err != nil {
return nil, err
}
if len(password) > 0 {
if _, err = c.Do("AUTH", password); err != nil {
_ = c.Close()
return nil, err
}
} else {
// check with PING
if _, err = c.Do("PING"); err != nil {
_ = c.Close()
return nil, err
}
}
return c, err
},
// custom connection test method
TestOnBorrow: func(c redis.Conn, t time.Time) error {
_, err := c.Do("PING")
return err
},
}
return RedisCache{pool, defaultExpiration}
}
func (c RedisCache) Set(key string, value interface{}, expires time.Duration) error {
conn := c.pool.Get()
defer func() {
_ = conn.Close()
}()
return c.invoke(conn.Do, key, value, expires)
}
func (c RedisCache) Add(key string, value interface{}, expires time.Duration) error {
conn := c.pool.Get()
defer func() {
_ = conn.Close()
}()
existed, err := exists(conn, key)
if err != nil {
return err
} else if existed {
return ErrNotStored
}
return c.invoke(conn.Do, key, value, expires)
}
func (c RedisCache) Replace(key string, value interface{}, expires time.Duration) error {
conn := c.pool.Get()
defer func() {
_ = conn.Close()
}()
existed, err := exists(conn, key)
if err != nil {
return err
} else if !existed {
return ErrNotStored
}
err = c.invoke(conn.Do, key, value, expires)
if value == nil {
return ErrNotStored
}
return err
}
func (c RedisCache) Get(key string, ptrValue interface{}) error {
conn := c.pool.Get()
defer func() {
_ = conn.Close()
}()
raw, err := conn.Do("GET", key)
if err != nil {
return err
} else if raw == nil {
return ErrCacheMiss
}
item, err := redis.Bytes(raw, err)
if err != nil {
return err
}
return Deserialize(item, ptrValue)
}
func generalizeStringSlice(strs []string) []interface{} {
ret := make([]interface{}, len(strs))
for i, str := range strs {
ret[i] = str
}
return ret
}
func (c RedisCache) GetMulti(keys ...string) (Getter, error) {
conn := c.pool.Get()
defer func() {
_ = conn.Close()
}()
items, err := redis.Values(conn.Do("MGET", generalizeStringSlice(keys)...))
if err != nil {
return nil, err
} else if items == nil {
return nil, ErrCacheMiss
}
m := make(map[string][]byte)
for i, key := range keys {
m[key] = nil
if i < len(items) && items[i] != nil {
s, ok := items[i].([]byte)
if ok {
m[key] = s
}
}
}
return RedisItemMapGetter(m), nil
}
func exists(conn redis.Conn, key string) (bool, error) {
return redis.Bool(conn.Do("EXISTS", key))
}
func (c RedisCache) Delete(key string) error {
conn := c.pool.Get()
defer func() {
_ = conn.Close()
}()
existed, err := redis.Bool(conn.Do("DEL", key))
if err == nil && !existed {
err = ErrCacheMiss
}
return err
}
func (c RedisCache) Increment(key string, delta uint64) (uint64, error) {
conn := c.pool.Get()
defer func() {
_ = conn.Close()
}()
// Check for existence *before* increment as per the cache contract.
// redis will auto create the key, and we don't want that. Since we need to do increment
// ourselves instead of natively via INCRBY (redis doesn't support wrapping), we get the value
// and do the exists check this way to minimize calls to Redis
val, err := conn.Do("GET", key)
if err != nil {
return 0, err
} else if val == nil {
return 0, ErrCacheMiss
}
currentVal, err := redis.Int64(val, nil)
if err != nil {
return 0, err
}
sum := currentVal + int64(delta)
_, err = conn.Do("SET", key, sum)
if err != nil {
return 0, err
}
return uint64(sum), nil
}
func (c RedisCache) Decrement(key string, delta uint64) (newValue uint64, err error) {
conn := c.pool.Get()
defer func() {
_ = conn.Close()
}()
// Check for existence *before* increment as per the cache contract.
// redis will auto create the key, and we don't want that, hence the exists call
existed, err := exists(conn, key)
if err != nil {
return 0, err
} else if !existed {
return 0, ErrCacheMiss
}
// Decrement contract says you can only go to 0
// so we go fetch the value and if the delta is greater than the amount,
// 0 out the value
currentVal, err := redis.Int64(conn.Do("GET", key))
if err != nil {
return 0, err
}
if delta > uint64(currentVal) {
var tempint int64
tempint, err = redis.Int64(conn.Do("DECRBY", key, currentVal))
return uint64(tempint), err
}
tempint, err := redis.Int64(conn.Do("DECRBY", key, delta))
return uint64(tempint), err
}
func (c RedisCache) Flush() error {
conn := c.pool.Get()
defer func() {
_ = conn.Close()
}()
_, err := conn.Do("FLUSHALL")
return err
}
func (c RedisCache) invoke(f func(string, ...interface{}) (interface{}, error),
key string, value interface{}, expires time.Duration) error {
switch expires {
case DefaultExpiryTime:
expires = c.defaultExpiration
case ForEverNeverExpiry:
expires = time.Duration(0)
}
b, err := Serialize(value)
if err != nil {
return err
}
conn := c.pool.Get()
defer func() {
_ = conn.Close()
}()
if expires > 0 {
_, err = f("SETEX", key, int32(expires/time.Second), b)
return err
}
_, err = f("SET", key, b)
return err
}
// RedisItemMapGetter implements a Getter on top of the returned item map.
type RedisItemMapGetter map[string][]byte
func (g RedisItemMapGetter) Get(key string, ptrValue interface{}) error {
item, ok := g[key]
if !ok {
return ErrCacheMiss
}
return Deserialize(item, ptrValue)
}
================================================
FILE: cache/redis_test.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package cache
import (
"net"
"testing"
"time"
"github.com/revel/config"
"github.com/revel/revel"
)
// These tests require redis server running on localhost:6379 (the default).
const redisTestServer = "localhost:6379"
var newRedisCache = func(t *testing.T, defaultExpiration time.Duration) Cache {
revel.Config = config.NewContext()
c, err := net.Dial("tcp", redisTestServer)
if err == nil {
if _, err = c.Write([]byte("flush_all\r\n")); err != nil {
t.Errorf("Write failed: %s", err)
}
_ = c.Close()
redisCache := NewRedisCache(redisTestServer, "", defaultExpiration)
if err = redisCache.Flush(); err != nil {
t.Errorf("Flush failed: %s", err)
}
return redisCache
}
t.Errorf("couldn't connect to redis on %s", redisTestServer)
t.FailNow()
panic("")
}
func TestRedisCache_TypicalGetSet(t *testing.T) {
typicalGetSet(t, newRedisCache)
}
func TestRedisCache_IncrDecr(t *testing.T) {
incrDecr(t, newRedisCache)
}
func TestRedisCache_Expiration(t *testing.T) {
expiration(t, newRedisCache)
}
func TestRedisCache_EmptyCache(t *testing.T) {
emptyCache(t, newRedisCache)
}
func TestRedisCache_Replace(t *testing.T) {
testReplace(t, newRedisCache)
}
func TestRedisCache_Add(t *testing.T) {
testAdd(t, newRedisCache)
}
func TestRedisCache_GetMulti(t *testing.T) {
testGetMulti(t, newRedisCache)
}
================================================
FILE: cache/serialization.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package cache
import (
"bytes"
"encoding/gob"
"reflect"
"strconv"
)
// Serialize transforms the given value into bytes following these rules:
// - If value is a byte array, it is returned as-is.
// - If value is an int or uint type, it is returned as the ASCII representation
// - Else, encoding/gob is used to serialize
func Serialize(value interface{}) ([]byte, error) {
if data, ok := value.([]byte); ok {
return data, nil
}
switch v := reflect.ValueOf(value); v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return []byte(strconv.FormatInt(v.Int(), 10)), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return []byte(strconv.FormatUint(v.Uint(), 10)), nil
}
var b bytes.Buffer
encoder := gob.NewEncoder(&b)
if err := encoder.Encode(value); err != nil {
cacheLog.Error("Serialize: gob encoding failed", "value", value, "error", err)
return nil, err
}
return b.Bytes(), nil
}
// Deserialize transforms bytes produced by Serialize back into a Go object,
// storing it into "ptr", which must be a pointer to the value type.
func Deserialize(byt []byte, ptr interface{}) (err error) {
if data, ok := ptr.(*[]byte); ok {
*data = byt
return
}
if v := reflect.ValueOf(ptr); v.Kind() == reflect.Ptr {
switch p := v.Elem(); p.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
var i int64
i, err = strconv.ParseInt(string(byt), 10, 64)
if err != nil {
cacheLog.Error("Deserialize: failed to parse int", "value", string(byt), "error", err)
} else {
p.SetInt(i)
}
return
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
var i uint64
i, err = strconv.ParseUint(string(byt), 10, 64)
if err != nil {
cacheLog.Error("Deserialize: failed to parse uint", "value", string(byt), "error", err)
} else {
p.SetUint(i)
}
return
}
}
b := bytes.NewBuffer(byt)
decoder := gob.NewDecoder(b)
if err = decoder.Decode(ptr); err != nil {
cacheLog.Error("Deserialize: glob decoding failed", "error", err)
return
}
return
}
================================================
FILE: cache/serialization_test.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package cache
import (
"reflect"
"testing"
)
type Struct1 struct {
X int
}
func (s Struct1) Method1() {}
type Interface1 interface {
Method1()
}
var (
struct1 = Struct1{1}
ptrStruct = &Struct1{2}
emptyIface interface{} = Struct1{3}
iface1 Interface1 = Struct1{4}
sliceStruct = []Struct1{{5}, {6}, {7}}
ptrSliceStruct = []*Struct1{{8}, {9}, {10}}
valueMap = map[string]interface{}{
"bytes": []byte{0x61, 0x62, 0x63, 0x64},
"string": "string",
"bool": true,
"int": 5,
"int8": int8(5),
"int16": int16(5),
"int32": int32(5),
"int64": int64(5),
"uint": uint(5),
"uint8": uint8(5),
"uint16": uint16(5),
"uint32": uint32(5),
"uint64": uint64(5),
"float32": float32(5),
"float64": float64(5),
"array": [5]int{1, 2, 3, 4, 5},
"slice": []int{1, 2, 3, 4, 5},
"emptyIf": emptyIface,
"Iface1": iface1,
"map": map[string]string{"foo": "bar"},
"ptrStruct": ptrStruct,
"struct1": struct1,
"sliceStruct": sliceStruct,
"ptrSliceStruct": ptrSliceStruct,
}
)
// Test passing all kinds of data between serialize and deserialize.
func TestRoundTrip(t *testing.T) {
for _, expected := range valueMap {
bytes, err := Serialize(expected)
if err != nil {
t.Error(err)
continue
}
ptrActual := reflect.New(reflect.TypeOf(expected)).Interface()
err = Deserialize(bytes, ptrActual)
if err != nil {
t.Error(err)
continue
}
actual := reflect.ValueOf(ptrActual).Elem().Interface()
if !reflect.DeepEqual(expected, actual) {
t.Errorf("(expected) %T %v != %T %v (actual)", expected, expected, actual, actual)
}
}
}
================================================
FILE: compress.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package revel
import (
"compress/gzip"
"compress/zlib"
"io"
"net/http"
"strconv"
"strings"
)
var compressionTypes = [...]string{
"gzip",
"deflate",
}
var compressableMimes = [...]string{
"text/plain",
"text/csv",
"text/html",
"text/xml",
"text/css",
"application/json",
"application/xml",
"application/xhtml+xml",
"application/rss+xml",
"application/javascript",
"application/x-javascript",
}
// Local log instance for this class.
var compressLog = RevelLog.New("section", "compress")
// WriteFlusher interface for compress writer.
type WriteFlusher interface {
io.Writer // An IO Writer
io.Closer // A closure
Flush() error /// A flush function
}
// The compressed writer.
type CompressResponseWriter struct {
Header *BufferedServerHeader // The header
ControllerResponse *Response // The response
OriginalWriter io.Writer // The writer
compressWriter WriteFlusher // The flushed writer
compressionType string // The compression type
headersWritten bool // True if written
closeNotify chan bool // The notify channel to close
parentNotify <-chan bool // The parent chanel to receive the closed event
closed bool // True if closed
}
// CompressFilter does compression of response body in gzip/deflate if
// `results.compressed=true` in the app.conf.
func CompressFilter(c *Controller, fc []Filter) {
if c.Response.Out.internalHeader.Server != nil && Config.BoolDefault("results.compressed", false) {
if c.Response.Status != http.StatusNoContent && c.Response.Status != http.StatusNotModified {
if found, compressType, compressWriter := detectCompressionType(c.Request, c.Response); found {
writer := CompressResponseWriter{
ControllerResponse: c.Response,
OriginalWriter: c.Response.GetWriter(),
compressWriter: compressWriter,
compressionType: compressType,
headersWritten: false,
closeNotify: make(chan bool, 1),
closed: false,
}
// Swap out the header with our own
writer.Header = NewBufferedServerHeader(c.Response.Out.internalHeader.Server)
c.Response.Out.internalHeader.Server = writer.Header
if w, ok := c.Response.GetWriter().(http.CloseNotifier); ok {
writer.parentNotify = w.CloseNotify()
}
c.Response.SetWriter(&writer)
}
} else {
compressLog.Debug("CompressFilter: Compression disabled for response ", "status", c.Response.Status)
}
}
fc[0](c, fc[1:])
}
// Called to notify the writer is closing.
func (c CompressResponseWriter) CloseNotify() <-chan bool {
if c.parentNotify != nil {
return c.parentNotify
}
return c.closeNotify
}
// Prepare the headers.
func (c *CompressResponseWriter) prepareHeaders() {
if c.compressionType != "" {
responseMime := ""
if t := c.Header.Get("Content-Type"); len(t) > 0 {
responseMime = t[0]
}
responseMime = strings.TrimSpace(strings.SplitN(responseMime, ";", 2)[0])
shouldEncode := false
if len(c.Header.Get("Content-Encoding")) == 0 {
for _, compressableMime := range compressableMimes {
if responseMime == compressableMime {
shouldEncode = true
c.Header.Set("Content-Encoding", c.compressionType)
c.Header.Del("Content-Length")
break
}
}
}
if !shouldEncode {
c.compressWriter = nil
c.compressionType = ""
}
}
c.Header.Release()
}
// Write the headers.
func (c *CompressResponseWriter) WriteHeader(status int) {
if c.closed {
return
}
c.headersWritten = true
c.prepareHeaders()
c.Header.SetStatus(status)
}
// Close the writer.
func (c *CompressResponseWriter) Close() error {
if c.closed {
return nil
}
if !c.headersWritten {
c.prepareHeaders()
}
if c.compressionType != "" {
c.Header.Del("Content-Length")
if err := c.compressWriter.Close(); err != nil {
// TODO When writing directly to stream, an error will be generated
compressLog.Error("Close: Error closing compress writer", "type", c.compressionType, "error", err)
}
}
// Non-blocking write to the closenotifier, if we for some reason should
// get called multiple times
select {
case c.closeNotify <- true:
default:
}
c.closed = true
return nil
}
// Write to the underling buffer.
func (c *CompressResponseWriter) Write(b []byte) (int, error) {
if c.closed {
return 0, io.ErrClosedPipe
}
// Abort if parent has been closed
if c.parentNotify != nil {
select {
case <-c.parentNotify:
return 0, io.ErrClosedPipe
default:
}
}
// Abort if we ourselves have been closed
if c.closed {
return 0, io.ErrClosedPipe
}
if !c.headersWritten {
c.prepareHeaders()
c.headersWritten = true
}
if c.compressionType != "" {
return c.compressWriter.Write(b)
}
return c.OriginalWriter.Write(b)
}
// DetectCompressionType method detects the compression type
// from header "Accept-Encoding".
func detectCompressionType(req *Request, resp *Response) (found bool, compressionType string, compressionKind WriteFlusher) {
if Config.BoolDefault("results.compressed", false) {
acceptedEncodings := strings.Split(req.GetHttpHeader("Accept-Encoding"), ",")
largestQ := 0.0
chosenEncoding := len(compressionTypes)
// I have fixed one edge case for issue #914
// But it's better to cover all possible edge cases or
// Adapt to https://github.com/golang/gddo/blob/master/httputil/header/header.go#L172
for _, encoding := range acceptedEncodings {
encoding = strings.TrimSpace(encoding)
encodingParts := strings.SplitN(encoding, ";", 2)
// If we are the format "gzip;q=0.8"
if len(encodingParts) > 1 {
q := strings.TrimSpace(encodingParts[1])
if len(q) == 0 || !strings.HasPrefix(q, "q=") {
continue
}
// Strip off the q=
num, err := strconv.ParseFloat(q[2:], 32)
if err != nil {
continue
}
if num >= largestQ && num > 0 {
if encodingParts[0] == "*" {
chosenEncoding = 0
largestQ = num
continue
}
for i, encoding := range compressionTypes {
if encoding == encodingParts[0] {
if i < chosenEncoding {
largestQ = num
chosenEncoding = i
}
break
}
}
}
} else {
// If we can accept anything, chose our preferred method.
if encodingParts[0] == "*" {
chosenEncoding = 0
largestQ = 1
break
}
// This is for just plain "gzip"
for i, encoding := range compressionTypes {
if encoding == encodingParts[0] {
if i < chosenEncoding {
largestQ = 1.0
chosenEncoding = i
}
break
}
}
}
}
if largestQ == 0 {
return
}
compressionType = compressionTypes[chosenEncoding]
switch compressionType {
case "gzip":
compressionKind = gzip.NewWriter(resp.GetWriter())
found = true
case "deflate":
compressionKind = zlib.NewWriter(resp.GetWriter())
found = true
}
}
return
}
// BufferedServerHeader will not send content out until the Released is called, from that point on it will act normally
// It implements all the ServerHeader.
type BufferedServerHeader struct {
cookieList []string // The cookie list
headerMap map[string][]string // The header map
status int // The status
released bool // True if released
original ServerHeader // The original header
}
// Creates a new instance based on the ServerHeader.
func NewBufferedServerHeader(o ServerHeader) *BufferedServerHeader {
return &BufferedServerHeader{original: o, headerMap: map[string][]string{}}
}
// Sets the cookie.
func (bsh *BufferedServerHeader) SetCookie(cookie string) {
if bsh.released {
bsh.original.SetCookie(cookie)
} else {
bsh.cookieList = append(bsh.cookieList, cookie)
}
}
// Returns a cookie.
func (bsh *BufferedServerHeader) GetCookie(key string) (ServerCookie, error) {
return bsh.original.GetCookie(key)
}
// Sets (replace) the header key.
func (bsh *BufferedServerHeader) Set(key string, value string) {
if bsh.released {
bsh.original.Set(key, value)
} else {
bsh.headerMap[key] = []string{value}
}
}
// Add (append) to a key this value.
func (bsh *BufferedServerHeader) Add(key string, value string) {
if bsh.released {
bsh.original.Set(key, value)
} else {
old := []string{}
if v, found := bsh.headerMap[key]; found {
old = v
}
bsh.headerMap[key] = append(old, value)
}
}
// Delete this key.
func (bsh *BufferedServerHeader) Del(key string) {
if bsh.released {
bsh.original.Del(key)
} else {
delete(bsh.headerMap, key)
}
}
// Get this key.
func (bsh *BufferedServerHeader) Get(key string) (value []string) {
if bsh.released {
value = bsh.original.Get(key)
} else {
if v, found := bsh.headerMap[key]; found && len(v) > 0 {
value = v
} else {
value = bsh.original.Get(key)
}
}
return
}
// Get all header keys.
func (bsh *BufferedServerHeader) GetKeys() (value []string) {
if bsh.released {
value = bsh.original.GetKeys()
} else {
value = bsh.original.GetKeys()
for key := range bsh.headerMap {
found := false
for _, v := range value {
if v == key {
found = true
break
}
}
if !found {
value = append(value, key)
}
}
}
return
}
// Set the status.
func (bsh *BufferedServerHeader) SetStatus(statusCode int) {
if bsh.released {
bsh.original.SetStatus(statusCode)
} else {
bsh.status = statusCode
}
}
// Release the header and push the results to the original.
func (bsh *BufferedServerHeader) Release() {
bsh.released = true
for k, v := range bsh.headerMap {
for _, r := range v {
bsh.original.Set(k, r)
}
}
for _, c := range bsh.cookieList {
bsh.original.SetCookie(c)
}
if bsh.status > 0 {
bsh.original.SetStatus(bsh.status)
}
}
================================================
FILE: compress_test.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package revel
import (
"net/http/httptest"
"strings"
"testing"
)
// Test that the render response is as expected.
func TestBenchmarkCompressed(t *testing.T) {
startFakeBookingApp()
resp := httptest.NewRecorder()
c := NewTestController(resp, showRequest)
if err := c.SetAction("Hotels", "Show"); err != nil {
t.Errorf("SetAction failed: %s", err)
}
Config.SetOption("results.compressed", "true")
result := Hotels{c}.Show(3)
result.Apply(c.Request, c.Response)
if !strings.Contains(resp.Body.String(), "300 Main St.") {
t.Errorf("Failed to find hotel address in action response:\n%s", resp.Body)
}
}
func BenchmarkRenderCompressed(b *testing.B) {
startFakeBookingApp()
resp := httptest.NewRecorder()
resp.Body = nil
c := NewTestController(resp, showRequest)
if err := c.SetAction("Hotels", "Show"); err != nil {
b.Errorf("SetAction failed: %s", err)
}
Config.SetOption("results.compressed", "true")
b.ResetTimer()
hotels := Hotels{c}
for i := 0; i < b.N; i++ {
hotels.Show(3).Apply(c.Request, c.Response)
}
}
func BenchmarkRenderUnCompressed(b *testing.B) {
startFakeBookingApp()
resp := httptest.NewRecorder()
resp.Body = nil
c := NewTestController(resp, showRequest)
if err := c.SetAction("Hotels", "Show"); err != nil {
b.Errorf("SetAction failed: %s", err)
}
Config.SetOption("results.compressed", "false")
b.ResetTimer()
hotels := Hotels{c}
for i := 0; i < b.N; i++ {
hotels.Show(3).Apply(c.Request, c.Response)
}
}
================================================
FILE: conf/mime-types.conf
================================================
3dm=x-world/x-3dmf
3dmf=x-world/x-3dmf
7z=application/x-7z-compressed
a=application/octet-stream
aab=application/x-authorware-bin
aam=application/x-authorware-map
aas=application/x-authorware-seg
abc=text/vndabc
ace=application/x-ace-compressed
acgi=text/html
afl=video/animaflex
ai=application/postscript
aif=audio/aiff
aifc=audio/aiff
aiff=audio/aiff
aim=application/x-aim
aip=text/x-audiosoft-intra
alz=application/x-alz-compressed
ani=application/x-navi-animation
aos=application/x-nokia-9000-communicator-add-on-software
aps=application/mime
arc=application/x-arc-compressed
arj=application/arj
art=image/x-jg
asf=video/x-ms-asf
asm=text/x-asm
asp=text/asp
asx=application/x-mplayer2
au=audio/basic
avi=video/x-msvideo
avs=video/avs-video
bcpio=application/x-bcpio
bin=application/mac-binary
bmp=image/bmp
boo=application/book
book=application/book
boz=application/x-bzip2
bsh=application/x-bsh
bz2=application/x-bzip2
bz=application/x-bzip
c++=text/plain
c=text/x-c
cab=application/vnd.ms-cab-compressed
cat=application/vndms-pkiseccat
cc=text/x-c
ccad=application/clariscad
cco=application/x-cocoa
cdf=application/cdf
cer=application/pkix-cert
cha=application/x-chat
chat=application/x-chat
chrt=application/vnd.kde.kchart
class=application/java
# ? class=application/java-vm
com=text/plain
conf=text/plain
cpio=application/x-cpio
cpp=text/x-c
cpt=application/mac-compactpro
crl=application/pkcs-crl
crt=application/pkix-cert
crx=application/x-chrome-extension
csh=text/x-scriptcsh
css=text/css
csv=text/csv
cxx=text/plain
dar=application/x-dar
dcr=application/x-director
deb=application/x-debian-package
deepv=application/x-deepv
def=text/plain
der=application/x-x509-ca-cert
dif=video/x-dv
dir=application/x-director
divx=video/divx
dl=video/dl
dmg=application/x-apple-diskimage
doc=application/msword
dot=application/msword
dp=application/commonground
drw=application/drafting
dump=application/octet-stream
dv=video/x-dv
dvi=application/x-dvi
dwf=drawing/x-dwf=(old)
dwg=application/acad
dxf=application/dxf
dxr=application/x-director
el=text/x-scriptelisp
elc=application/x-bytecodeelisp=(compiled=elisp)
eml=message/rfc822
env=application/x-envoy
eps=application/postscript
es=application/x-esrehber
etx=text/x-setext
evy=application/envoy
exe=application/octet-stream
f77=text/x-fortran
f90=text/x-fortran
f=text/x-fortran
fdf=application/vndfdf
fif=application/fractals
fli=video/fli
flo=image/florian
flv=video/x-flv
flx=text/vndfmiflexstor
fmf=video/x-atomic3d-feature
for=text/x-fortran
fpx=image/vndfpx
frl=application/freeloader
funk=audio/make
g3=image/g3fax
g=text/plain
gif=image/gif
gl=video/gl
gsd=audio/x-gsm
gsm=audio/x-gsm
gsp=application/x-gsp
gss=application/x-gss
gtar=application/x-gtar
gz=application/x-compressed
gzip=application/x-gzip
h=text/x-h
hdf=application/x-hdf
help=application/x-helpfile
hgl=application/vndhp-hpgl
hh=text/x-h
hlb=text/x-script
hlp=application/hlp
hpg=application/vndhp-hpgl
hpgl=application/vndhp-hpgl
hqx=application/binhex
hta=application/hta
htc=text/x-component
htm=text/html
html=text/html
htmls=text/html
htt=text/webviewhtml
htx=text/html
ice=x-conference/x-cooltalk
ico=image/x-icon
ics=text/calendar
icz=text/calendar
idc=text/plain
ief=image/ief
iefs=image/ief
iges=application/iges
igs=application/iges
ima=application/x-ima
imap=application/x-httpd-imap
inf=application/inf
ins=application/x-internett-signup
ip=application/x-ip2
isu=video/x-isvideo
it=audio/it
iv=application/x-inventor
ivr=i-world/i-vrml
ivy=application/x-livescreen
jam=audio/x-jam
jav=text/x-java-source
java=text/x-java-source
jcm=application/x-java-commerce
jfif-tbnl=image/jpeg
jfif=image/jpeg
jnlp=application/x-java-jnlp-file
jpe=image/jpeg
jpeg=image/jpeg
jpg=image/jpeg
jps=image/x-jps
js=application/javascript
json=application/json
jut=image/jutvision
kar=audio/midi
karbon=application/vnd.kde.karbon
kfo=application/vnd.kde.kformula
flw=application/vnd.kde.kivio
kml=application/vnd.google-earth.kml+xml
kmz=application/vnd.google-earth.kmz
kon=application/vnd.kde.kontour
kpr=application/vnd.kde.kpresenter
kpt=application/vnd.kde.kpresenter
ksp=application/vnd.kde.kspread
kwd=application/vnd.kde.kword
kwt=application/vnd.kde.kword
ksh=text/x-scriptksh
la=audio/nspaudio
lam=audio/x-liveaudio
latex=application/x-latex
lha=application/lha
lhx=application/octet-stream
list=text/plain
lma=audio/nspaudio
log=text/plain
lsp=text/x-scriptlisp
lst=text/plain
lsx=text/x-la-asf
ltx=application/x-latex
lzh=application/octet-stream
lzx=application/lzx
m1v=video/mpeg
m2a=audio/mpeg
m2v=video/mpeg
m3u=audio/x-mpegurl
m=text/x-m
man=application/x-troff-man
manifest=text/cache-manifest
map=application/x-navimap
mar=text/plain
mbd=application/mbedlet
mc$=application/x-magic-cap-package-10
mcd=application/mcad
mcf=text/mcf
mcp=application/netmc
me=application/x-troff-me
mht=message/rfc822
mhtml=message/rfc822
mid=application/x-midi
midi=application/x-midi
mif=application/x-frame
mime=message/rfc822
mjf=audio/x-vndaudioexplosionmjuicemediafile
mjpg=video/x-motion-jpeg
mm=application/base64
mme=application/base64
mod=audio/mod
moov=video/quicktime
mov=video/quicktime
movie=video/x-sgi-movie
mp2=audio/mpeg
mp3=audio/mpeg3
mp4=video/mp4
mpa=audio/mpeg
mpc=application/x-project
mpe=video/mpeg
mpeg=video/mpeg
mpg=video/mpeg
mpga=audio/mpeg
mpp=application/vndms-project
mpt=application/x-project
mpv=application/x-project
mpx=application/x-project
mrc=application/marc
ms=application/x-troff-ms
mv=video/x-sgi-movie
my=audio/make
mzz=application/x-vndaudioexplosionmzz
nap=image/naplps
naplps=image/naplps
nc=application/x-netcdf
ncm=application/vndnokiaconfiguration-message
nif=image/x-niff
niff=image/x-niff
nix=application/x-mix-transfer
nsc=application/x-conference
nvd=application/x-navidoc
o=application/octet-stream
oda=application/oda
odb=application/vnd.oasis.opendocument.database
odc=application/vnd.oasis.opendocument.chart
odf=application/vnd.oasis.opendocument.formula
odg=application/vnd.oasis.opendocument.graphics
odi=application/vnd.oasis.opendocument.image
odm=application/vnd.oasis.opendocument.text-master
odp=application/vnd.oasis.opendocument.presentation
ods=application/vnd.oasis.opendocument.spreadsheet
odt=application/vnd.oasis.opendocument.text
oga=audio/ogg
ogg=audio/ogg
ogv=video/ogg
omc=application/x-omc
omcd=application/x-omcdatamaker
omcr=application/x-omcregerator
otc=application/vnd.oasis.opendocument.chart-template
otf=application/vnd.oasis.opendocument.formula-template
otg=application/vnd.oasis.opendocument.graphics-template
oth=application/vnd.oasis.opendocument.text-web
oti=application/vnd.oasis.opendocument.image-template
otm=application/vnd.oasis.opendocument.text-master
otp=application/vnd.oasis.opendocument.presentation-template
ots=application/vnd.oasis.opendocument.spreadsheet-template
ott=application/vnd.oasis.opendocument.text-template
p10=application/pkcs10
p12=application/pkcs-12
p7a=application/x-pkcs7-signature
p7c=application/pkcs7-mime
p7m=application/pkcs7-mime
p7r=application/x-pkcs7-certreqresp
p7s=application/pkcs7-signature
p=text/x-pascal
part=application/pro_eng
pas=text/pascal
pbm=image/x-portable-bitmap
pcl=application/vndhp-pcl
pct=image/x-pict
pcx=image/x-pcx
pdb=chemical/x-pdb
pdf=application/pdf
pfunk=audio/make
pgm=image/x-portable-graymap
pic=image/pict
pict=image/pict
pkg=application/x-newton-compatible-pkg
pko=application/vndms-pkipko
pl=text/x-scriptperl
plx=application/x-pixclscript
pm4=application/x-pagemaker
pm5=application/x-pagemaker
pm=text/x-scriptperl-module
png=image/png
pnm=application/x-portable-anymap
pot=application/mspowerpoint
pov=model/x-pov
ppa=application/vndms-powerpoint
ppm=image/x-portable-pixmap
pps=application/mspowerpoint
ppt=application/mspowerpoint
ppz=application/mspowerpoint
pre=application/x-freelance
prt=application/pro_eng
ps=application/postscript
psd=application/octet-stream
pvu=paleovu/x-pv
pwz=application/vndms-powerpoint
py=text/x-scriptphyton
pyc=applicaiton/x-bytecodepython
qcp=audio/vndqcelp
qd3=x-world/x-3dmf
qd3d=x-world/x-3dmf
qif=image/x-quicktime
qt=video/quicktime
qtc=video/x-qtc
qti=image/x-quicktime
qtif=image/x-quicktime
ra=audio/x-pn-realaudio
ram=audio/x-pn-realaudio
rar=application/x-rar-compressed
ras=application/x-cmu-raster
rast=image/cmu-raster
rexx=text/x-scriptrexx
rf=image/vndrn-realflash
rgb=image/x-rgb
rm=application/vndrn-realmedia
rmi=audio/mid
rmm=audio/x-pn-realaudio
rmp=audio/x-pn-realaudio
rng=application/ringing-tones
rnx=application/vndrn-realplayer
roff=application/x-troff
rp=image/vndrn-realpix
rpm=audio/x-pn-realaudio-plugin
rt=text/vndrn-realtext
rtf=text/richtext
rtx=text/richtext
rv=video/vndrn-realvideo
s=text/x-asm
s3m=audio/s3m
s7z=application/x-7z-compressed
saveme=application/octet-stream
sbk=application/x-tbook
scm=text/x-scriptscheme
sdml=text/plain
sdp=application/sdp
sdr=application/sounder
sea=application/sea
set=application/set
sgm=text/x-sgml
sgml=text/x-sgml
sh=text/x-scriptsh
shar=application/x-bsh
shtml=text/x-server-parsed-html
sid=audio/x-psid
skd=application/x-koan
skm=application/x-koan
skp=application/x-koan
skt=application/x-koan
sit=application/x-stuffit
sitx=application/x-stuffitx
sl=application/x-seelogo
smi=application/smil
smil=application/smil
snd=audio/basic
sol=application/solids
spc=text/x-speech
spl=application/futuresplash
spr=application/x-sprite
sprite=application/x-sprite
spx=audio/ogg
src=application/x-wais-source
ssi=text/x-server-parsed-html
ssm=application/streamingmedia
sst=application/vndms-pkicertstore
step=application/step
stl=application/sla
stp=application/step
sv4cpio=application/x-sv4cpio
sv4crc=application/x-sv4crc
svf=image/vnddwg
svg=image/svg+xml
svr=application/x-world
swf=application/x-shockwave-flash
t=application/x-troff
talk=text/x-speech
tar=application/x-tar
tbk=application/toolbook
tcl=text/x-scripttcl
tcsh=text/x-scripttcsh
tex=application/x-tex
texi=application/x-texinfo
texinfo=application/x-texinfo
text=text/plain
tgz=application/gnutar
tif=image/tiff
tiff=image/tiff
tr=application/x-troff
tsi=audio/tsp-audio
tsp=application/dsptype
tsv=text/tab-separated-values
turbot=image/florian
txt=text/plain
uil=text/x-uil
uni=text/uri-list
unis=text/uri-list
unv=application/i-deas
uri=text/uri-list
uris=text/uri-list
ustar=application/x-ustar
uu=text/x-uuencode
uue=text/x-uuencode
vcd=application/x-cdlink
vcf=text/x-vcard
vcard=text/x-vcard
vcs=text/x-vcalendar
vda=application/vda
vdo=video/vdo
vew=application/groupwise
viv=video/vivo
vivo=video/vivo
vmd=application/vocaltec-media-desc
vmf=application/vocaltec-media-file
voc=audio/voc
vos=video/vosaic
vox=audio/voxware
vqe=audio/x-twinvq-plugin
vqf=audio/x-twinvq
vql=audio/x-twinvq-plugin
vrml=application/x-vrml
vrt=x-world/x-vrt
vsd=application/x-visio
vst=application/x-visio
vsw=application/x-visio
wasm=application/wasm
w60=application/wordperfect60
w61=application/wordperfect61
w6w=application/msword
wav=audio/wav
wb1=application/x-qpro
wbmp=image/vnd.wap.wbmp
web=application/vndxara
wiz=application/msword
wk1=application/x-123
wmf=windows/metafile
wml=text/vnd.wap.wml
wmlc=application/vnd.wap.wmlc
wmls=text/vnd.wap.wmlscript
wmlsc=application/vnd.wap.wmlscriptc
word=application/msword
wp5=application/wordperfect
wp6=application/wordperfect
wp=application/wordperfect
wpd=application/wordperfect
wq1=application/x-lotus
wri=application/mswrite
wrl=application/x-world
wrz=model/vrml
wsc=text/scriplet
wsrc=application/x-wais-source
wtk=application/x-wintalk
x-png=image/png
xbm=image/x-xbitmap
xdr=video/x-amt-demorun
xgz=xgl/drawing
xif=image/vndxiff
xl=application/excel
xla=application/excel
xlb=application/excel
xlc=application/excel
xld=application/excel
xlk=application/excel
xll=application/excel
xlm=application/excel
xls=application/excel
xlt=application/excel
xlv=application/excel
xlw=application/excel
xm=audio/xm
xml=text/xml
xmz=xgl/movie
xpix=application/x-vndls-xpix
xpm=image/x-xpixmap
xsr=video/x-amt-showrun
xwd=image/x-xwd
xyz=chemical/x-pdb
z=application/x-compress
zip=application/zip
zoo=application/octet-stream
zsh=text/x-scriptzsh
# Office 2007 mess - http://wdg.uncc.edu/Microsoft_Office_2007_MIME_Types_for_Apache_and_IIS
docx=application/vnd.openxmlformats-officedocument.wordprocessingml.document
docm=application/vnd.ms-word.document.macroEnabled.12
dotx=application/vnd.openxmlformats-officedocument.wordprocessingml.template
dotm=application/vnd.ms-word.template.macroEnabled.12
xlsx=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
xlsm=application/vnd.ms-excel.sheet.macroEnabled.12
xltx=application/vnd.openxmlformats-officedocument.spreadsheetml.template
xltm=application/vnd.ms-excel.template.macroEnabled.12
xlsb=application/vnd.ms-excel.sheet.binary.macroEnabled.12
xlam=application/vnd.ms-excel.addin.macroEnabled.12
pptx=application/vnd.openxmlformats-officedocument.presentationml.presentation
pptm=application/vnd.ms-powerpoint.presentation.macroEnabled.12
ppsx=application/vnd.openxmlformats-officedocument.presentationml.slideshow
ppsm=application/vnd.ms-powerpoint.slideshow.macroEnabled.12
potx=application/vnd.openxmlformats-officedocument.presentationml.template
potm=application/vnd.ms-powerpoint.template.macroEnabled.12
ppam=application/vnd.ms-powerpoint.addin.macroEnabled.12
sldx=application/vnd.openxmlformats-officedocument.presentationml.slide
sldm=application/vnd.ms-powerpoint.slide.macroEnabled.12
thmx=application/vnd.ms-officetheme
onetoc=application/onenote
onetoc2=application/onenote
onetmp=application/onenote
onepkg=application/onenote
# koffice
# iWork
key=application/x-iwork-keynote-sffkey
kth=application/x-iwork-keynote-sffkth
nmbtemplate=application/x-iwork-numbers-sfftemplate
numbers=application/x-iwork-numbers-sffnumbers
pages=application/x-iwork-pages-sffpages
template=application/x-iwork-pages-sfftemplate
# Extensions for Mozilla apps (Firefox and friends)
xpi=application/x-xpinstall
# Opera extensions
oex=application/x-opera-extension
================================================
FILE: controller.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package revel
import (
"errors"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"time"
"github.com/revel/revel/logger"
"github.com/revel/revel/session"
"github.com/revel/revel/utils"
)
// Controller Revel's controller structure that gets embedded in user defined
// controllers.
type Controller struct {
Name string // The controller name, e.g. "Application"
Type *ControllerType // A description of the controller type.
MethodName string // The method name, e.g. "Index"
MethodType *MethodType // A description of the invoked action type.
AppController interface{} // The controller that was instantiated. embeds revel.Controller
Action string // The fully qualified action name, e.g. "App.Index"
ClientIP string // holds IP address of request came from
Request *Request
Response *Response
Result Result
Flash Flash // User cookie, cleared after 1 request.
Session session.Session // Session, stored using the session engine specified
Params *Params // Parameters from URL and form (including multipart).
Args map[string]interface{} // Per-request scratch space.
ViewArgs map[string]interface{} // Variables passed to the template.
Validation *Validation // Data validation helpers
Log logger.MultiLogger // Context Logger
}
// The map of controllers, controllers are mapped by using the namespace|controller_name as the key.
var (
controllers = make(map[string]*ControllerType)
controllerLog = RevelLog.New("section", "controller")
)
// NewController returns new controller instance for Request and Response.
func NewControllerEmpty() *Controller {
return &Controller{Request: NewRequest(nil), Response: NewResponse(nil)}
}
// New controller, creates a new instance wrapping the request and response in it.
func NewController(context ServerContext) *Controller {
c := NewControllerEmpty()
c.SetController(context)
return c
}
// Sets the request and the response for the controller.
func (c *Controller) SetController(context ServerContext) {
c.Request.SetRequest(context.GetRequest())
c.Response.SetResponse(context.GetResponse())
c.Request.controller = c
c.Params = new(Params)
c.Args = map[string]interface{}{}
c.ViewArgs = map[string]interface{}{
"RunMode": RunMode,
"DevMode": DevMode,
}
}
func (c *Controller) Destroy() {
// When the instantiated controller gets injected
// It inherits this method, so we need to
// check to see if the controller is nil before performing
// any actions
if c == nil {
return
}
if c.AppController != nil {
c.resetAppControllerFields()
// Return this instance to the pool
appController := c.AppController
c.AppController = nil
if RevelConfig.Controller.Reuse {
RevelConfig.Controller.CachedMap[c.Name].Push(appController)
}
}
c.Request.Destroy()
c.Response.Destroy()
c.Params = nil
c.Args = nil
c.ViewArgs = nil
c.Name = ""
c.Type = nil
c.MethodName = ""
c.MethodType = nil
c.Action = ""
c.ClientIP = ""
c.Result = nil
c.Flash = Flash{}
c.Session = session.NewSession()
c.Params = nil
c.Validation = nil
c.Log = nil
}
// FlashParams serializes the contents of Controller.Params to the Flash
// cookie.
func (c *Controller) FlashParams() {
for key, vals := range c.Params.Values {
c.Flash.Out[key] = strings.Join(vals, ",")
}
}
func (c *Controller) SetCookie(cookie *http.Cookie) {
c.Response.Out.internalHeader.SetCookie(cookie.String())
}
type ErrorCoder interface {
HTTPCode() int
}
func (c *Controller) RenderError(err error) Result {
if coder, ok := err.(ErrorCoder); ok {
c.setStatusIfNil(coder.HTTPCode())
} else {
c.setStatusIfNil(http.StatusInternalServerError)
}
return ErrorResult{c.ViewArgs, err}
}
func (c *Controller) setStatusIfNil(status int) {
if c.Response.Status == 0 {
c.Response.Status = status
}
}
// Render a template corresponding to the calling Controller method.
// Arguments will be added to c.ViewArgs prior to rendering the template.
// They are keyed on their local identifier.
//
// For example:
//
// func (c Users) ShowUser(id int) revel.Result {
// user := loadUser(id)
// return c.Render(user)
// }
//
// This action will render views/Users/ShowUser.html, passing in an extra
// key-value "user": (User).
//
// This is the slower magical version which uses the runtime
// to determine
// 1) Set c.ViewArgs to the arguments passed into this function
// 2) How to call the RenderTemplate by building the following line
// c.RenderTemplate(c.Name + "/" + c.MethodType.Name + "." + c.Request.Format)
//
// If you want your code to run faster it is recommended you add the template values directly
// to the c.ViewArgs and call c.RenderTemplate directly.
func (c *Controller) Render(extraViewArgs ...interface{}) Result {
c.setStatusIfNil(http.StatusOK)
// Get the calling function line number.
_, _, line, ok := runtime.Caller(1)
if !ok {
controllerLog.Error("Render: Failed to get Caller information")
}
// Get the extra ViewArgs passed in.
if renderArgNames, ok := c.MethodType.RenderArgNames[line]; ok {
if len(renderArgNames) == len(extraViewArgs) {
for i, extraRenderArg := range extraViewArgs {
c.ViewArgs[renderArgNames[i]] = extraRenderArg
}
} else {
controllerLog.Error(fmt.Sprint(len(renderArgNames), "RenderArg names found for",
len(extraViewArgs), "extra ViewArgs"))
}
} else {
controllerLog.Error(fmt.Sprint("No RenderArg names found for Render call on line", line,
"(Action", c.Action, ")"), "stack", logger.NewCallStack())
}
return c.RenderTemplate(c.Name + "/" + c.MethodType.Name + "." + c.Request.Format)
}
// RenderTemplate method does less magical way to render a template.
// Renders the given template, using the current ViewArgs.
func (c *Controller) RenderTemplate(templatePath string) Result {
c.setStatusIfNil(http.StatusOK)
// Get the Template.
lang, _ := c.ViewArgs[CurrentLocaleViewArg].(string)
template, err := MainTemplateLoader.TemplateLang(templatePath, lang)
if err != nil {
return c.RenderError(err)
}
return &RenderTemplateResult{
Template: template,
ViewArgs: c.ViewArgs,
}
}
// TemplateOutput returns the result of the template rendered using the controllers ViewArgs.
func (c *Controller) TemplateOutput(templatePath string) (data []byte, err error) {
return TemplateOutputArgs(templatePath, c.ViewArgs)
}
// RenderJSON uses encoding/json.Marshal to return JSON to the client.
func (c *Controller) RenderJSON(o interface{}) Result {
c.setStatusIfNil(http.StatusOK)
return RenderJSONResult{o, ""}
}
// RenderJSONP renders JSONP result using encoding/json.Marshal.
func (c *Controller) RenderJSONP(callback string, o interface{}) Result {
c.setStatusIfNil(http.StatusOK)
return RenderJSONResult{o, callback}
}
// RenderXML uses encoding/xml.Marshal to return XML to the client.
func (c *Controller) RenderXML(o interface{}) Result {
c.setStatusIfNil(http.StatusOK)
return RenderXMLResult{o}
}
// RenderText renders plaintext in response, printf style.
func (c *Controller) RenderText(text string, objs ...interface{}) Result {
c.setStatusIfNil(http.StatusOK)
finalText := text
if len(objs) > 0 {
finalText = fmt.Sprintf(text, objs...)
}
return &RenderTextResult{finalText}
}
// RenderHTML renders html in response.
func (c *Controller) RenderHTML(html string) Result {
c.setStatusIfNil(http.StatusOK)
return &RenderHTMLResult{html}
}
// Todo returns an HTTP 501 Not Implemented "todo" indicating that the
// action isn't done yet.
func (c *Controller) Todo() Result {
c.Response.Status = http.StatusNotImplemented
controllerLog.Debug("Todo: Not implemented function", "action", c.Action)
return c.RenderError(&Error{
Title: "TODO",
Description: "This action is not implemented",
})
}
// NotFound returns an HTTP 404 Not Found response whose body is the
// formatted string of msg and objs.
func (c *Controller) NotFound(msg string, objs ...interface{}) Result {
finalText := msg
if len(objs) > 0 {
finalText = fmt.Sprintf(msg, objs...)
}
c.Response.Status = http.StatusNotFound
return c.RenderError(&Error{
Title: "Not Found",
Description: finalText,
})
}
// Forbidden returns an HTTP 403 Forbidden response whose body is the
// formatted string of msg and objs.
func (c *Controller) Forbidden(msg string, objs ...interface{}) Result {
finalText := msg
if len(objs) > 0 {
finalText = fmt.Sprintf(msg, objs...)
}
c.Response.Status = http.StatusForbidden
return c.RenderError(&Error{
Title: "Forbidden",
Description: finalText,
})
}
// RenderFileName returns a file indicated by the path as provided via the filename.
// It can be either displayed inline or downloaded as an attachment.
// The name and size are taken from the file info.
func (c *Controller) RenderFileName(filename string, delivery ContentDisposition) Result {
f, err := os.Open(filename)
if err != nil {
c.Log.Errorf("Cant open file: %v", err)
return c.RenderError(err)
}
return c.RenderFile(f, delivery)
}
// RenderFile returns a file, either displayed inline or downloaded
// as an attachment. The name and size are taken from the file info.
func (c *Controller) RenderFile(file *os.File, delivery ContentDisposition) Result {
c.setStatusIfNil(http.StatusOK)
var (
modtime = time.Now()
fileInfo, err = file.Stat()
)
if err != nil {
controllerLog.Error("RenderFile: error", "error", err)
}
if fileInfo != nil {
modtime = fileInfo.ModTime()
}
return c.RenderBinary(file, filepath.Base(file.Name()), delivery, modtime)
}
// RenderBinary is like RenderFile() except that it instead of a file on disk,
// it renders data from memory (which could be a file that has not been written,
// the output from some function, or bytes streamed from somewhere else, as long
// it implements io.Reader). When called directly on something generated or
// streamed, modtime should mostly likely be time.Now().
func (c *Controller) RenderBinary(memfile io.Reader, filename string, delivery ContentDisposition, modtime time.Time) Result {
c.setStatusIfNil(http.StatusOK)
return &BinaryResult{
Reader: memfile,
Name: filename,
Delivery: delivery,
Length: -1, // http.ServeContent gets the length itself unless memfile is a stream.
ModTime: modtime,
}
}
// Redirect to an action or to a URL.
// c.Redirect(Controller.Action)
// c.Redirect("/controller/action")
// c.Redirect("/controller/%d/action", id)
func (c *Controller) Redirect(val interface{}, args ...interface{}) Result {
c.setStatusIfNil(http.StatusFound)
if url, ok := val.(string); ok {
if len(args) == 0 {
return &RedirectToURLResult{url}
}
return &RedirectToURLResult{fmt.Sprintf(url, args...)}
}
return &RedirectToActionResult{val, args}
}
// This stats returns some interesting stats based on what is cached in memory
// and what is available directly.
func (c *Controller) Stats() map[string]interface{} {
result := CurrentEngine.Stats()
if RevelConfig.Controller.Reuse {
result["revel-controllers"] = RevelConfig.Controller.Stack.String()
for key, appStack := range RevelConfig.Controller.CachedMap {
result["app-"+key] = appStack.String()
}
}
return result
}
// Message performs a lookup for the given message name using the given
// arguments using the current language defined for this controller.
//
// The current language is set by the i18n plugin.
func (c *Controller) Message(message string, args ...interface{}) string {
return MessageFunc(c.Request.Locale, message, args...)
}
// SetAction sets the action that is being invoked in the current request.
// It sets the following properties: Name, Action, Type, MethodType.
func (c *Controller) SetAction(controllerName, methodName string) error {
return c.SetTypeAction(controllerName, methodName, nil)
}
// SetAction sets the assigns the Controller type, sets the action and initializes the controller.
func (c *Controller) SetTypeAction(controllerName, methodName string, typeOfController *ControllerType) error {
// Look up the controller and method types.
if typeOfController == nil {
if c.Type = ControllerTypeByName(controllerName, anyModule); c.Type == nil {
return errors.New("revel/controller: failed to find controller " + controllerName)
}
} else {
c.Type = typeOfController
}
// Note method name is case insensitive search
if c.MethodType = c.Type.Method(methodName); c.MethodType == nil {
return errors.New("revel/controller: failed to find action " + controllerName + "." + methodName)
}
c.Name, c.MethodName = c.Type.Type.Name(), c.MethodType.Name
c.Action = c.Name + "." + c.MethodName
// Update Logger with controller and namespace
if c.Log != nil {
c.Log = c.Log.New("action", c.Action, "namespace", c.Type.Namespace)
}
if RevelConfig.Controller.Reuse {
if _, ok := RevelConfig.Controller.CachedMap[c.Name]; !ok {
// Create a new stack for this controller
localType := c.Type.Type
RevelConfig.Controller.CachedMap[c.Name] = utils.NewStackLock(
RevelConfig.Controller.CachedStackSize,
RevelConfig.Controller.CachedStackMaxSize,
func() interface{} {
return reflect.New(localType).Interface()
})
}
// Instantiate the controller.
c.AppController = RevelConfig.Controller.CachedMap[c.Name].Pop()
} else {
c.AppController = reflect.New(c.Type.Type).Interface()
}
c.setAppControllerFields()
return nil
}
func ControllerTypeByName(controllerName string, moduleSource *Module) (c *ControllerType) {
var found bool
if c, found = controllers[controllerName]; !found {
// Backup, passed in controllerName should be in lower case, but may not be
if c, found = controllers[strings.ToLower(controllerName)]; !found {
controllerLog.Debug("ControllerTypeByName: Cannot find controller in controllers map ", "controller", controllerName)
// Search for the controller by name
for _, cType := range controllers {
testControllerName := strings.ToLower(cType.Type.Name())
if testControllerName == strings.ToLower(controllerName) && (cType.ModuleSource == moduleSource || moduleSource == anyModule) {
controllerLog.Warn("ControllerTypeByName: Matched empty namespace controller ", "controller", controllerName, "namespace", cType.ModuleSource.Name)
c = cType
break
}
}
}
}
return
}
// Injects this instance (c) into the AppController instance.
func (c *Controller) setAppControllerFields() {
appController := reflect.ValueOf(c.AppController).Elem()
cValue := reflect.ValueOf(c)
for _, index := range c.Type.ControllerIndexes {
appController.FieldByIndex(index).Set(cValue)
}
}
// Removes this instance (c) from the AppController instance.
func (c *Controller) resetAppControllerFields() {
appController := reflect.ValueOf(c.AppController).Elem()
// Zero out controller
for _, index := range c.Type.ControllerIndexes {
appController.FieldByIndex(index).Set(reflect.Zero(reflect.TypeOf(c.AppController).Elem().FieldByIndex(index).Type))
}
}
func findControllers(appControllerType reflect.Type) (indexes [][]int) {
// It might be a multi-level embedding. To find the controllers, we follow
// every anonymous field, using breadth-first search.
type nodeType struct {
val reflect.Value
index []int
}
appControllerPtr := reflect.New(appControllerType)
queue := []nodeType{{appControllerPtr, []int{}}}
for len(queue) > 0 {
// Get the next value and de-reference it if necessary.
var (
node = queue[0]
elem = node.val
elemType = elem.Type()
)
if elemType.Kind() == reflect.Ptr {
elem = elem.Elem()
elemType = elem.Type()
}
queue = queue[1:]
// #944 if the type's Kind is not `Struct` move on,
// otherwise `elem.NumField()` will panic
if elemType.Kind() != reflect.Struct {
continue
}
// Look at all the struct fields.
for i := 0; i < elem.NumField(); i++ {
// If this is not an anonymous field, skip it.
structField := elemType.Field(i)
if !structField.Anonymous {
continue
}
fieldValue := elem.Field(i)
fieldType := structField.Type
// If it's a Controller, record the field indexes to get here.
if fieldType == controllerPtrType {
indexes = append(indexes, append(node.index, i))
continue
}
queue = append(queue,
nodeType{fieldValue, append(append([]int{}, node.index...), i)})
}
}
return
}
// RegisterController registers a Controller and its Methods with Revel.
func RegisterController(c interface{}, methods []*MethodType) {
// De-star the controller type
// (e.g. given TypeOf((*Application)(nil)), want TypeOf(Application))
elem := reflect.TypeOf(c).Elem()
// De-star all of the method arg types too.
for _, m := range methods {
m.lowerName = strings.ToLower(m.Name)
for _, arg := range m.Args {
arg.Type = arg.Type.Elem()
}
}
// Fetch module for controller, if none found controller must be part of the app
controllerModule := ModuleFromPath(elem.PkgPath(), true)
controllerType := AddControllerType(controllerModule, elem, methods)
controllerLog.Debug("RegisterController:Registered controller", "controller", controllerType.Name(), "module-name", controllerModule.Name)
}
================================================
FILE: controller_type.go
================================================
package revel
import (
"reflect"
"strings"
)
// Controller registry and types.
type ControllerType struct {
Namespace string // The namespace of the controller
ModuleSource *Module // The module for the controller
Type reflect.Type
Methods []*MethodType
ControllerIndexes [][]int // FieldByIndex to all embedded *Controllers
ControllerEvents *ControllerTypeEvents
}
type ControllerTypeEvents struct {
Before, After, Finally, Panic []*ControllerFieldPath
}
// The controller field path provides the caller the ability to invoke the call
// directly.
type ControllerFieldPath struct {
IsPointer bool
FieldIndexPath []int
FunctionCall reflect.Value
}
type MethodType struct {
Name string
Args []*MethodArg
RenderArgNames map[int][]string
lowerName string
Index int
}
type MethodArg struct {
Name string
Type reflect.Type
}
// Adds the controller to the controllers map using its namespace, also adds it to the module list of controllers.
// If the controller is in the main application it is added without its namespace as well.
func AddControllerType(moduleSource *Module, controllerType reflect.Type, methods []*MethodType) (newControllerType *ControllerType) {
if moduleSource == nil {
moduleSource = appModule
}
newControllerType = &ControllerType{ModuleSource: moduleSource, Type: controllerType, Methods: methods, ControllerIndexes: findControllers(controllerType)}
newControllerType.ControllerEvents = NewControllerTypeEvents(newControllerType)
newControllerType.Namespace = moduleSource.Namespace()
controllerName := newControllerType.Name()
// Store the first controller only in the controllers map with the unmapped namespace.
if _, found := controllers[controllerName]; !found {
controllers[controllerName] = newControllerType
newControllerType.ModuleSource.AddController(newControllerType)
if newControllerType.ModuleSource == appModule {
// Add the controller mapping into the global namespace
controllers[newControllerType.ShortName()] = newControllerType
}
} else {
controllerLog.Errorf("Error, attempt to register duplicate controller as %s", controllerName)
}
controllerLog.Debugf("Registered controller: %s", controllerName)
return
}
// Method searches for a given exported method (case insensitive).
func (ct *ControllerType) Method(name string) *MethodType {
lowerName := strings.ToLower(name)
for _, method := range ct.Methods {
if method.lowerName == lowerName {
return method
}
}
return nil
}
// The controller name with the namespace.
func (ct *ControllerType) Name() string {
return ct.Namespace + ct.ShortName()
}
// The controller name without the namespace.
func (ct *ControllerType) ShortName() string {
return strings.ToLower(ct.Type.Name())
}
func NewControllerTypeEvents(c *ControllerType) (ce *ControllerTypeEvents) {
ce = &ControllerTypeEvents{}
// Parse the methods for the controller type, assign any control methods
checkType := c.Type
ce.check(checkType, []int{})
return
}
// Add in before after panic and finally, recursive call
// Befores are ordered in revers, everything else is in order of first encountered.
func (cte *ControllerTypeEvents) check(theType reflect.Type, fieldPath []int) {
typeChecker := func(checkType reflect.Type) {
for index := 0; index < checkType.NumMethod(); index++ {
m := checkType.Method(index)
// Must be two arguments, the second returns the controller type
// Go cannot differentiate between promoted methods and
// embedded methods, this allows the embedded method to be run
// https://github.com/golang/go/issues/21162
if m.Type.NumOut() == 2 && m.Type.Out(1) == checkType {
if checkType.Kind() == reflect.Ptr {
controllerLog.Debug("Found controller type event method pointer", "name", checkType.Elem().Name(), "methodname", m.Name)
} else {
controllerLog.Debug("Found controller type event method", "name", checkType.Name(), "methodname", m.Name)
}
controllerFieldPath := newFieldPath(checkType.Kind() == reflect.Ptr, m.Func, fieldPath)
switch strings.ToLower(m.Name) {
case "before":
cte.Before = append([]*ControllerFieldPath{controllerFieldPath}, cte.Before...)
case "after":
cte.After = append(cte.After, controllerFieldPath)
case "panic":
cte.Panic = append(cte.Panic, controllerFieldPath)
case "finally":
cte.Finally = append(cte.Finally, controllerFieldPath)
}
}
}
}
// Check methods of both types
typeChecker(theType)
typeChecker(reflect.PtrTo(theType))
// Check for any sub controllers, ignore any pointers to controllers revel.Controller
for i := 0; i < theType.NumField(); i++ {
v := theType.Field(i)
switch v.Type.Kind() {
case reflect.Struct:
cte.check(v.Type, append(fieldPath, i))
}
}
}
func newFieldPath(isPointer bool, value reflect.Value, fieldPath []int) *ControllerFieldPath {
return &ControllerFieldPath{IsPointer: isPointer, FunctionCall: value, FieldIndexPath: fieldPath}
}
func (fieldPath *ControllerFieldPath) Invoke(value reflect.Value, input []reflect.Value) (result []reflect.Value) {
for _, index := range fieldPath.FieldIndexPath {
// You can only fetch fields from non pointers
if value.Type().Kind() == reflect.Ptr {
value = value.Elem().Field(index)
} else {
value = value.Field(index)
}
}
if fieldPath.IsPointer && value.Type().Kind() != reflect.Ptr {
value = value.Addr()
} else if !fieldPath.IsPointer && value.Type().Kind() == reflect.Ptr {
value = value.Elem()
}
return fieldPath.FunctionCall.Call(append([]reflect.Value{value}, input...))
}
================================================
FILE: errors.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package revel
import (
"fmt"
"path/filepath"
"runtime/debug"
"strconv"
"strings"
)
// Error description, used as an argument to the error template.
type Error struct {
SourceType string // The type of source that failed to build.
Title, Path, Description string // Description of the error, as presented to the user.
Line, Column int // Where the error was encountered.
SourceLines []string // The entire source file, split into lines.
Stack string // The raw stack trace string from debug.Stack().
MetaError string // Error that occurred producing the error page.
Link string // A configurable link to wrap the error source in
}
// SourceLine structure to hold the per-source-line details.
type SourceLine struct {
Source string
Line int
IsError bool
}
// NewErrorFromPanic method finds the deepest stack from in user code and
// provide a code listing of that, on the line that eventually triggered
// the panic. Returns nil if no relevant stack frame can be found.
func NewErrorFromPanic(err interface{}) *Error {
// Parse the filename and line from the originating line of app code.
// /Users/robfig/code/gocode/src/revel/examples/booking/app/controllers/hotels.go:191 (0x44735)
stack := string(debug.Stack())
frame, basePath := findRelevantStackFrame(stack)
if frame == -1 {
return nil
}
stack = stack[frame:]
stackElement := stack[:strings.Index(stack, "\n")]
colonIndex := strings.LastIndex(stackElement, ":")
filename := stackElement[:colonIndex]
var line int
fmt.Sscan(stackElement[colonIndex+1:], &line)
// Show an error page.
description := "Unspecified error"
if err != nil {
description = fmt.Sprint(err)
}
lines, readErr := ReadLines(filename)
if readErr != nil {
utilLog.Error("Unable to read file", "file", filename, "error", readErr)
}
return &Error{
Title: "Runtime Panic",
Path: filename[len(basePath):],
Line: line,
Description: description,
SourceLines: lines,
Stack: stack,
}
}
// Error method constructs a plaintext version of the error, taking
// account that fields are optionally set. Returns e.g. Compilation Error
// (in views/header.html:51): expected right delim in end; got "}".
func (e *Error) Error() string {
loc := ""
if e.Path != "" {
line := ""
if e.Line != 0 {
line = fmt.Sprintf(":%d", e.Line)
}
loc = fmt.Sprintf("(in %s%s)", e.Path, line)
}
header := loc
if e.Title != "" {
if loc != "" {
header = fmt.Sprintf("%s %s: ", e.Title, loc)
} else {
header = fmt.Sprintf("%s: ", e.Title)
}
}
return fmt.Sprintf("%s%s Stack: %s", header, e.Description, e.Stack)
}
// ContextSource method returns a snippet of the source around
// where the error occurred.
func (e *Error) ContextSource() []SourceLine {
if e.SourceLines == nil {
return nil
}
start := (e.Line - 1) - 5
if start < 0 {
start = 0
}
end := (e.Line - 1) + 5
if end > len(e.SourceLines) {
end = len(e.SourceLines)
}
lines := make([]SourceLine, end-start)
for i, src := range e.SourceLines[start:end] {
fileLine := start + i + 1
lines[i] = SourceLine{src, fileLine, fileLine == e.Line}
}
return lines
}
// SetLink method prepares a link and assign to Error.Link attribute.
func (e *Error) SetLink(errorLink string) {
errorLink = strings.Replace(errorLink, "{{Path}}", e.Path, -1)
errorLink = strings.Replace(errorLink, "{{Line}}", strconv.Itoa(e.Line), -1)
e.Link = "<a href=" + errorLink + ">" + e.Path + ":" + strconv.Itoa(e.Line) + "</a>"
}
// Return the character index of the first relevant stack frame, or -1 if none were found.
// Additionally it returns the base path of the tree in which the identified code resides.
func findRelevantStackFrame(stack string) (int, string) {
// Find first item in SourcePath that isn't in RevelPath.
// If first item is in RevelPath, keep track of position, trim and check again.
partialStack := stack
sourcePath := filepath.ToSlash(SourcePath)
revelPath := filepath.ToSlash(RevelPath)
sumFrame := 0
for {
frame := strings.Index(partialStack, sourcePath)
revelFrame := strings.Index(partialStack, revelPath)
if frame == -1 {
break
} else if frame != revelFrame {
return sumFrame + frame, SourcePath
} else {
// Need to at least trim off the first character so this frame isn't caught again.
partialStack = partialStack[frame+1:]
sumFrame += frame + 1
}
}
for _, module := range Modules {
if frame := strings.Index(stack, filepath.ToSlash(module.Path)); frame != -1 {
return frame, module.Path
}
}
return -1, ""
}
================================================
FILE: event.go
================================================
package revel
type (
// The event type.
Event int
// The event response.
EventResponse int
// The handler signature.
EventHandler func(typeOf Event, value interface{}) (responseOf EventResponse)
)
const (
// Event type when templates are going to be refreshed (receivers are registered template engines added to the template.engine conf option).
TEMPLATE_REFRESH_REQUESTED Event = iota
// Event type when templates are refreshed (receivers are registered template engines added to the template.engine conf option).
TEMPLATE_REFRESH_COMPLETED
// Event type before all module loads, events thrown to handlers added to AddInitEventHandler.
// Event type before all module loads, events thrown to handlers added to AddInitEventHandler.
REVEL_BEFORE_MODULES_LOADED
// Event type after all module loads, events thrown to handlers added to AddInitEventHandler.
REVEL_AFTER_MODULES_LOADED
// Event type before server engine is initialized, receivers are active server engine and handlers added to AddInitEventHandler.
ENGINE_BEFORE_INITIALIZED
// Event type before server engine is started, receivers are active server engine and handlers added to AddInitEventHandler.
ENGINE_STARTED
// Event raised when the engine is told to shutdown.
ENGINE_SHUTDOWN_REQUEST
// Event type after server engine is stopped, receivers are active server engine and handlers added to AddInitEventHandler.
ENGINE_SHUTDOWN
// Called before routes are refreshed.
ROUTE_REFRESH_REQUESTED
// Called after routes have been refreshed.
ROUTE_REFRESH_COMPLETED
// Fired when a panic is caught during the startup process.
REVEL_FAILURE
)
// Fires system events from revel.
func RaiseEvent(key Event, value interface{}) (response EventResponse) {
utilLog.Info("Raising event", "len", len(initEventList))
for _, handler := range initEventList {
response |= handler(key, value)
}
return
}
// Add event handler to listen for all system events.
func AddInitEventHandler(handler EventHandler) {
initEventList = append(initEventList, handler)
return
}
================================================
FILE: event_test.go
================================================
package revel_test
import (
"testing"
"github.com/revel/revel"
"github.com/stretchr/testify/assert"
)
// Test that the event handler can be attached and it dispatches the event received.
func TestEventHandler(t *testing.T) {
counter := 0
newListener := func(typeOf revel.Event, value interface{}) (responseOf revel.EventResponse) {
if typeOf == revel.REVEL_FAILURE {
counter++
}
return
}
// Attach the same handlder twice so we expect to see the response twice as well
revel.AddInitEventHandler(newListener)
revel.AddInitEventHandler(newListener)
revel.RaiseEvent(revel.REVEL_AFTER_MODULES_LOADED, nil)
revel.RaiseEvent(revel.REVEL_FAILURE, nil)
assert.Equal(t, counter, 2, "Expected event handler to have been called")
}
================================================
FILE: fakeapp_test.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package revel
import (
"os"
"path/filepath"
"reflect"
)
type Hotel struct {
HotelID int
Name, Address string
City, State, Zip string
Country string
Price int
}
type Hotels struct {
*Controller
}
type Static struct {
*Controller
}
type Implicit struct {
*Controller
}
type Application struct {
*Controller
}
func (c Hotels) Show(id int) Result {
title := "View Hotel"
hotel := &Hotel{id, "A Hotel", "300 Main St.", "New York", "NY", "10010", "USA", 300}
// The line number below must match the one with the code : RenderArgNames: map[int][]string{43: {"title", "hotel"}},
return c.Render(title, hotel)
}
func (c Hotels) Book(id int) Result {
hotel := &Hotel{id, "A Hotel", "300 Main St.", "New York", "NY", "10010", "USA", 300}
return c.RenderJSON(hotel)
}
func (c Hotels) Index() Result {
return c.RenderText("Hello, World!")
}
func (c Static) Serve(prefix, path string) Result {
var basePath, dirName string
if !filepath.IsAbs(dirName) {
basePath = BasePath
}
fname := filepath.Join(basePath, prefix, path)
file, err := os.Open(fname)
if os.IsNotExist(err) {
return c.NotFound("")
} else if err != nil {
RevelLog.Errorf("Problem opening file (%s): %s ", fname, err)
return c.NotFound("This was found but not sure why we couldn't open it.")
}
return c.RenderFile(file, "")
}
// Register controllers is in its own function so the route test can use it as well.
func registerControllers() {
controllers = make(map[string]*ControllerType)
RaiseEvent(ROUTE_REFRESH_REQUESTED, nil)
RegisterController((*Hotels)(nil),
[]*MethodType{
{
Name: "Index",
},
{
Name: "Show",
Args: []*MethodArg{
{"id", reflect.TypeOf((*int)(nil))},
},
RenderArgNames: map[int][]string{41: {"title", "hotel"}},
},
{
Name: "Book",
Args: []*MethodArg{
{"id", reflect.TypeOf((*int)(nil))},
},
},
})
RegisterController((*Static)(nil),
[]*MethodType{
{
Name: "Serve",
Args: []*MethodArg{
{Name: "prefix", Type: reflect.TypeOf((*string)(nil))},
{Name: "filepath", Type: reflect.TypeOf((*string)(nil))},
},
RenderArgNames: map[int][]string{},
},
})
RegisterController((*Implicit)(nil),
[]*MethodType{
{
Name: "Implicit",
Args: []*MethodArg{
{Name: "prefix", Type: reflect.TypeOf((*string)(nil))},
{Name: "filepath", Type: reflect.TypeOf((*string)(nil))},
},
RenderArgNames: map[int][]string{},
},
})
RegisterController((*Application)(nil),
[]*MethodType{
{
Name: "Application",
Args: []*MethodArg{
{Name: "prefix", Type: reflect.TypeOf((*string)(nil))},
{Name: "filepath", Type: reflect.TypeOf((*string)(nil))},
},
RenderArgNames: map[int][]string{},
},
{
Name: "Index",
Args: []*MethodArg{
{Name: "foo", Type: reflect.TypeOf((*string)(nil))},
{Name: "bar", Type: reflect.TypeOf((*string)(nil))},
},
RenderArgNames: map[int][]string{},
},
})
}
func startFakeBookingApp() {
Init("prod", "github.com/revel/revel/testdata", "")
MainTemplateLoader = NewTemplateLoader([]string{ViewsPath, filepath.Join(RevelPath, "templates")})
if err := MainTemplateLoader.Refresh(); err != nil {
RevelLog.Fatal("Template error", "error", err)
}
registerControllers()
InitServerEngine(9000, GO_NATIVE_SERVER_ENGINE)
RaiseEvent(ENGINE_BEFORE_INITIALIZED, nil)
InitServer()
RaiseEvent(ENGINE_STARTED, nil)
}
================================================
FILE: field.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package revel
import (
"reflect"
"strings"
)
// Field represents a data field that may be collected in a web form.
type Field struct {
Name string
Error *ValidationError
viewArgs map[string]interface{}
controller *Controller
}
func NewField(name string, viewArgs map[string]interface{}) *Field {
err, _ := viewArgs["errors"].(map[string]*ValidationError)[name]
controller, _ := viewArgs["_controller"].(*Controller)
return &Field{
Name: name,
Error: err,
viewArgs: viewArgs,
controller: controller,
}
}
// ID returns an identifier suitable for use as an HTML id.
func (f *Field) ID() string {
return strings.Replace(f.Name, ".", "_", -1)
}
// Flash returns the flashed value of this Field.
func (f *Field) Flash() string {
v, _ := f.viewArgs["flash"].(map[string]string)[f.Name]
return v
}
// Options returns the option list of this Field.
func (f *Field) Options() []string {
if f.viewArgs["options"] == nil {
return nil
}
v, _ := f.viewArgs["options"].(map[string][]string)[f.Name]
return v
}
// FlashArray returns the flashed value of this Field as a list split on comma.
func (f *Field) FlashArray() []string {
v := f.Flash()
if v == "" {
return []string{}
}
return strings.Split(v, ",")
}
// Value returns the current value of this Field.
func (f *Field) Value() interface{} {
pieces := strings.Split(f.Name, ".")
answer, ok := f.viewArgs[pieces[0]]
if !ok {
return ""
}
val := reflect.ValueOf(answer)
for i := 1; i < len(pieces); i++ {
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
val = val.FieldByName(pieces[i])
if !val.IsValid() {
return ""
}
}
return val.Interface()
}
// ErrorClass returns ErrorCSSClass if this field has a validation error, else empty string.
func (f *Field) ErrorClass() string {
if f.Error != nil {
if errorClass, ok := f.viewArgs["ERROR_CLASS"]; ok {
return errorClass.(string)
}
return ErrorCSSClass
}
return ""
}
// Get the short name and translate it.
func (f *Field) ShortName() string {
name := f.Name
if i := strings.LastIndex(name, "."); i > 0 {
name = name[i+1:]
}
return f.Translate(name)
}
// Translate the text.
func (f *Field) Translate(text string, args ...interface{}) string {
if f.controller != nil {
text = f.controller.Message(text, args...)
}
return text
}
================================================
FILE: filter.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package revel
// Filter type definition for Revel's filter.
type Filter func(c *Controller, filterChain []Filter)
// Filters is the default set of global filters.
// It may be set by the application on initialization.
var Filters = []Filter{
PanicFilter, // Recover from panics and display an error page instead.
RouterFilter, // Use the routing table to select the right Action.
FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
ParamsFilter, // Parse parameters into Controller.Params.
SessionFilter, // Restore and write the session cookie.
FlashFilter, // Restore and write the flash cookie.
ValidationFilter, // Restore kept validation errors and save new ones from cookie.
I18nFilter, // Resolve the requested language.
InterceptorFilter, // Run interceptors around the action.
CompressFilter, // Compress the result.
BeforeAfterFilter,
ActionInvoker, // Invoke the action.
}
// NilFilter and NilChain are helpful in writing filter tests.
var (
NilFilter = func(_ *Controller, _ []Filter) {}
NilChain = []Filter{NilFilter}
)
================================================
FILE: filterconfig.go
================================================
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package revel
import (
"reflect"
"strings"
)
// Map from "Controller" or "Controller.Method" to the Filter chain.
var filterOverrides = make(map[string][]Filter)
// FilterConfigurator allows the developer configure the filter chain on a
// per-controller or per-action basis. The filter configuration is applied by
// the FilterConfiguringFilter, which is itself a filter stage. For example,
//
// Assuming:
// Filters = []Filter{
// RouterFilter,
// FilterConfiguringFilter,
// SessionFilter,
// ActionInvoker,
// }
//
// Add:
// FilterAction(App.Action).
// Add(OtherFilter)
//
// => RouterFilter, FilterConfiguringFilter, SessionFilter, OtherFilter, ActionInvoker
//
// Remove:
// FilterAction(App.Action).
// Remove(SessionFilter)
//
// => RouterFilter, FilterConfiguringFilter, OtherFilter, ActionInvoker
//
// Insert:
// FilterAction(App.Action).
// Insert(OtherFilter, revel.BEFORE, SessionFilter)
//
// => RouterFilter, FilterConfiguringFilter, OtherFilter, SessionFilter, ActionInvoker
//
// Filter modifications may be combined between Controller and Action. For example:
// FilterController(App{}).
// Add(Filter1)
// FilterAction(App.Action).
// Add(Filter2)
//
// .. would result in App.Action being filtered by both Filter1 and Filter2.
//
// Note: the last filter stage is not subject to the configurator. In
// particular, Add() adds a filter to the second-to-last place.
type FilterConfigurator struct {
key string // e.g. "App", "App.Action"
controllerName string // e.g. "App"
}
func newFilterConfigurator(controllerName, methodName string) FilterConfigurator {
if methodName == "" {
return FilterConfigurator{controllerName, controllerName}
}
return FilterConfigurator{controllerName + "." + methodName, controllerName}
}
// FilterController returns a configurator for the filters applied to all
// actions on the given controller instance. For example:
// FilterController(MyController{})
func FilterController(controllerInstance interface{}) FilterConfigurator {
t := reflect.TypeOf(controllerInstance)
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
return newFilterConfigurator(t.Name(), "")
}
// FilterAction returns a configurator for the filters applied to the given
// controller method. For example:
// FilterAction(MyController.MyAction)
func FilterAction(methodRef interface{}) FilterConfigurator {
var (
methodValue = reflect.ValueOf(methodRef)
methodType = methodValue.Type()
)
if methodType.Kind() != reflect.Func || methodType.NumIn() == 0 {
panic("Expecting a controller method reference (e.g. Controller.Action), got a " +
methodType.String())
}
controllerType := methodType.In(0)
method := FindMethod(controllerType, methodValue)
if method == nil {
panic("Action not found on controller " + controllerType.Name())
}
for controllerType.Kind() == reflect.Ptr {
controllerType = controllerType.Elem()
}
return newFilterConfigurator(controllerType.Name(), method.Name)
}
// Add the given filter in the second-to-last position in the filter chain.
// (Second-to-last so that it is before ActionInvoker).
func (conf FilterConfigurator) Add(f Filter) FilterConfigurator {
conf.apply(func(fc []Filter) []Filter {
return conf.addFilter(f, fc)
})
return conf
}
func (conf FilterConfigurator) addFilter(f Filter, fc []Filter) []Filter {
return append(fc[:len(fc)-1], f, fc[len(fc)-1])
}
// Remove a filter from the filter chain.
func (conf FilterConfigurator) Remove(target Filter) FilterConfigurator {
conf.apply(func(fc []Filter) []Filter {
return conf.rmFilter(target, fc)
})
return conf
}
func (conf FilterConfigurator) rmFilter(target Filter, fc []Filter) []Filter {
for i, f := range fc {
if FilterEq(f, target) {
return append(fc[:i], fc[i+1:]...)
}
}
return fc
}
// Insert a filter into the filter chain before or after another.
// This may be called with the BEFORE or AFTER constants, for example:
// revel.FilterAction(App.Index).
// Insert(MyFilter, revel.BEFORE, revel.ActionInvoker).
// Insert(MyFilter2, revel.AFTER, revel.PanicFilter)
func (conf FilterConfigurator) Insert(insert Filter, where When, target Filter) FilterConfigurator {
if where != BEFORE && where != AFTER {
panic("where must be BEFORE or AFTER")
}
conf.apply(func(fc []Filter) []Filter {
return conf.insertFilter(insert, where, target, fc)
})
return conf
}
func (conf FilterConfigurator) insertFilter(insert Filter, where When, target Filter, fc []Filter) []Filter {
for i, f := range fc {
if FilterEq(f, target) {
if where == BEFORE {
return append(fc[:i], append([]Filter{insert}, fc[i:]...)...)
}
return append(fc[:i+1], append([]Filter{insert}, fc[i+1:]...)...)
}
}
return fc
}
// getChain returns the filter chain that applies to the given controller or
// action. If no overrides are configured, then a copy of the default filter
// chain is returned.
func (conf FilterConfigurator) getChain() []Filter {
var filters []Filter
if filters = getOverrideChain(conf.controllerName, conf.key); filters == nil {
// The override starts with all filters after FilterConfiguringFilter
for i, f := range Filters {
if FilterEq(f, FilterConfiguringFilter) {
filters = make([]Filter, len(Filters)-i-1)
copy(filters, Filters[i+1:])
break
}
}
if filters == nil {
panic("FilterConfiguringFilter not found in revel.Filters.")
}
}
return filters
}
// apply applies the given functional change to the filter overrides.
// No other function modifies the filterOverrides map.
func (conf FilterConfigurator) apply(f func([]Filter) []Filter) {
// Updates any actions that have had their filters overridden, if this is a
// Controller configurator.
if conf.controllerName == conf.key {
for k, v := range filterOverrides {
if strings.HasPrefix(k, conf.controllerName+".") {
filterOverrides[k] = f(v)
}
}
}
// Update the Controller or Action overrides.
filterOverrides[conf.key] = f(conf.getChain())
}
// FilterEq returns true if the two filters reference the same filter.
func FilterE
gitextract_z1a8uhvh/ ├── .codebeatsettings ├── .gitignore ├── .travis.yml ├── AUTHORS ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── before_after_filter.go ├── before_after_filter_test.go ├── binder.go ├── binder_test.go ├── cache/ │ ├── cache.go │ ├── cache_test.go │ ├── init.go │ ├── inmemory.go │ ├── inmemory_test.go │ ├── memcached.go │ ├── memcached_test.go │ ├── redis.go │ ├── redis_test.go │ ├── serialization.go │ └── serialization_test.go ├── compress.go ├── compress_test.go ├── conf/ │ └── mime-types.conf ├── controller.go ├── controller_type.go ├── errors.go ├── event.go ├── event_test.go ├── fakeapp_test.go ├── field.go ├── filter.go ├── filterconfig.go ├── filterconfig_test.go ├── flash.go ├── go.mod ├── go.sum ├── http.go ├── i18n.go ├── i18n_test.go ├── intercept.go ├── intercept_test.go ├── invoker.go ├── invoker_test.go ├── libs.go ├── libs_test.go ├── logger/ │ ├── composite_multihandler.go │ ├── doc.go │ ├── handlers.go │ ├── init.go │ ├── init_test.go │ ├── log_function_map.go │ ├── logger.go │ ├── revel_logger.go │ ├── terminal_format.go │ ├── utils.go │ └── wrap_handlers.go ├── logger.go ├── model/ │ ├── revel_container.go │ ├── revel_controller.go │ ├── revel_paths.go │ └── revel_unit.go ├── module.go ├── namespace.go ├── panic.go ├── params.go ├── params_test.go ├── results.go ├── results_test.go ├── revel.go ├── revel_hooks.go ├── revel_test.go ├── router.go ├── router_test.go ├── server-engine.go ├── server.go ├── server_adapter_go.go ├── server_test.go ├── session/ │ ├── init.go │ ├── session.go │ ├── session_cookie_test.go │ └── session_test.go ├── session_adapter_cookie.go ├── session_engine.go ├── session_filter.go ├── template.go ├── template_adapter_go.go ├── template_engine.go ├── template_functions.go ├── templates/ │ └── errors/ │ ├── 403.html │ ├── 403.json │ ├── 403.txt │ ├── 403.xml │ ├── 404-dev.html │ ├── 404.html │ ├── 404.json │ ├── 404.txt │ ├── 404.xml │ ├── 405.html │ ├── 405.json │ ├── 405.txt │ ├── 405.xml │ ├── 500-dev.html │ ├── 500.html │ ├── 500.json │ ├── 500.txt │ └── 500.xml ├── testdata/ │ ├── app/ │ │ └── views/ │ │ ├── footer.html │ │ ├── header.html │ │ └── hotels/ │ │ └── show.html │ ├── conf/ │ │ ├── app.conf │ │ └── routes │ ├── i18n/ │ │ ├── config/ │ │ │ └── test_app.conf │ │ └── messages/ │ │ ├── dutch_messages.nl │ │ ├── english_messages.en │ │ ├── english_messages2.en │ │ └── invalid_message_file_name.txt │ └── public/ │ └── js/ │ └── sessvars.js ├── testing/ │ ├── testsuite.go │ └── testsuite_test.go ├── util.go ├── util_test.go ├── utils/ │ ├── simplestack.go │ └── simplestack_test.go ├── validation.go ├── validation_test.go ├── validators.go ├── validators_test.go ├── version.go └── watcher.go
SYMBOL INDEX (1135 symbols across 90 files)
FILE: before_after_filter.go
function BeforeAfterFilter (line 9) | func BeforeAfterFilter(c *Controller, fc []Filter) {
function beforeAfterFilterInvoke (line 33) | func beforeAfterFilterInvoke(method When, c *Controller) (r *reflect.Val...
FILE: before_after_filter_test.go
type bafTestController (line 11) | type bafTestController struct
method Before (line 15) | func (c bafTestController) Before() (Result, bafTestController) {
method Index (line 19) | func (c bafTestController) Index() Result {
type failingFilter (line 24) | type failingFilter struct
method FailIfCalled (line 28) | func (f failingFilter) FailIfCalled(c *Controller, filterChain []Filte...
function TestInterceptorsNotCalledIfBeforeReturns (line 32) | func TestInterceptorsNotCalledIfBeforeReturns(t *testing.T) {
FILE: binder.go
type Binder (line 21) | type Binder struct
function ValueBinder (line 50) | func ValueBinder(f func(value string, typ reflect.Type) reflect.Value) f...
constant DefaultDateFormat (line 62) | DefaultDateFormat = "2006-01-02"
constant DefaultDateTimeFormat (line 63) | DefaultDateTimeFormat = "2006-01-02 15:04"
type sliceValue (line 201) | type sliceValue struct
function bindSlice (line 210) | func bindSlice(params *Params, name string, typ reflect.Type) reflect.Va...
function nextKey (line 288) | func nextKey(key string) string {
function unbindSlice (line 296) | func unbindSlice(output map[string]string, name string, val interface{}) {
function bindStruct (line 303) | func bindStruct(params *Params, name string, typ reflect.Type) reflect.V...
function unbindStruct (line 345) | func unbindStruct(output map[string]string, name string, iface interface...
function getMultipartFile (line 360) | func getMultipartFile(params *Params, name string) multipart.File {
function bindFile (line 371) | func bindFile(params *Params, name string, typ reflect.Type) reflect.Val...
function bindByteArray (line 407) | func bindByteArray(params *Params, name string, typ reflect.Type) reflec...
function bindReadSeeker (line 418) | func bindReadSeeker(params *Params, name string, typ reflect.Type) refle...
function bindMap (line 427) | func bindMap(params *Params, name string, typ reflect.Type) reflect.Value {
function unbindMap (line 464) | func unbindMap(output map[string]string, name string, iface interface{}) {
function Bind (line 475) | func Bind(params *Params, name string, typ reflect.Type) reflect.Value {
function BindValue (line 482) | func BindValue(val string, typ reflect.Type) reflect.Value {
function BindFile (line 486) | func BindFile(fileHeader *multipart.FileHeader, typ reflect.Type) reflec...
function Unbind (line 490) | func Unbind(output map[string]string, name string, val interface{}) {
function binderForType (line 500) | func binderForType(typ reflect.Type) (Binder, bool) {
function init (line 514) | func init() {
FILE: binder_test.go
type A (line 22) | type A struct
type B (line 30) | type B struct
function TestJsonBinder (line 189) | func TestJsonBinder(t *testing.T) {
function TestBinder (line 217) | func TestBinder(t *testing.T) {
function TestUnbinder (line 357) | func TestUnbinder(t *testing.T) {
function valEq (line 388) | func valEq(t *testing.T, name string, actual, expected reflect.Value) {
function init (line 424) | func init() {
FILE: cache/cache.go
constant DefaultExpiryTime (line 14) | DefaultExpiryTime = time.Duration(0)
constant ForEverNeverExpiry (line 15) | ForEverNeverExpiry = time.Duration(-1)
type Getter (line 19) | type Getter interface
type Cache (line 55) | type Cache interface
function Get (line 131) | func Get(key string, ptrValue interface{}) error { retu...
function GetMulti (line 132) | func GetMulti(keys ...string) (Getter, error) { retu...
function Delete (line 133) | func Delete(key string) error { retu...
function Increment (line 134) | func Increment(key string, n uint64) (newValue uint64, err error) { retu...
function Decrement (line 135) | func Decrement(key string, n uint64) (newValue uint64, err error) { retu...
function Flush (line 136) | func Flush() error { retu...
function Set (line 137) | func Set(key string, value interface{}, expires time.Duration) error {
function Add (line 141) | func Add(key string, value interface{}, expires time.Duration) error {
function Replace (line 145) | func Replace(key string, value interface{}, expires time.Duration) error {
FILE: cache/cache_test.go
type cacheFactory (line 15) | type cacheFactory
function typicalGetSet (line 18) | func typicalGetSet(t *testing.T, newCache cacheFactory) {
function incrDecr (line 38) | func incrDecr(t *testing.T, newCache cacheFactory) {
function expiration (line 81) | func expiration(t *testing.T, newCache cacheFactory) {
function emptyCache (line 123) | func emptyCache(t *testing.T, newCache cacheFactory) {
function testReplace (line 151) | func testReplace(t *testing.T, newCache cacheFactory) {
function testAdd (line 186) | func testAdd(t *testing.T, newCache cacheFactory) {
function testGetMulti (line 217) | func testGetMulti(t *testing.T, newCache cacheFactory) {
FILE: cache/init.go
function init (line 16) | func init() {
FILE: cache/inmemory.go
type InMemoryCache (line 16) | type InMemoryCache struct
method Get (line 25) | func (c InMemoryCache) Get(key string, ptrValue interface{}) error {
method GetMulti (line 45) | func (c InMemoryCache) GetMulti(keys ...string) (Getter, error) {
method Set (line 49) | func (c InMemoryCache) Set(key string, value interface{}, expires time...
method Add (line 57) | func (c InMemoryCache) Add(key string, value interface{}, expires time...
method Replace (line 67) | func (c InMemoryCache) Replace(key string, value interface{}, expires ...
method Delete (line 76) | func (c InMemoryCache) Delete(key string) error {
method Increment (line 86) | func (c InMemoryCache) Increment(key string, n uint64) (newValue uint6...
method Decrement (line 99) | func (c InMemoryCache) Decrement(key string, n uint64) (newValue uint6...
method Flush (line 117) | func (c InMemoryCache) Flush() error {
method convertTypeToUint64 (line 126) | func (c InMemoryCache) convertTypeToUint64(key string) (newValue uint6...
function NewInMemoryCache (line 21) | func NewInMemoryCache(defaultExpiration time.Duration) InMemoryCache {
FILE: cache/inmemory_test.go
function TestInMemoryCache_TypicalGetSet (line 17) | func TestInMemoryCache_TypicalGetSet(t *testing.T) {
function TestInMemoryCache_IncrDecr (line 22) | func TestInMemoryCache_IncrDecr(t *testing.T) {
function TestInMemoryCache_Expiration (line 26) | func TestInMemoryCache_Expiration(t *testing.T) {
function TestInMemoryCache_EmptyCache (line 30) | func TestInMemoryCache_EmptyCache(t *testing.T) {
function TestInMemoryCache_Replace (line 34) | func TestInMemoryCache_Replace(t *testing.T) {
function TestInMemoryCache_Add (line 38) | func TestInMemoryCache_Add(t *testing.T) {
function TestInMemoryCache_GetMulti (line 42) | func TestInMemoryCache_GetMulti(t *testing.T) {
FILE: cache/memcached.go
type MemcachedCache (line 16) | type MemcachedCache struct
method Set (line 25) | func (c MemcachedCache) Set(key string, value interface{}, expires tim...
method Add (line 29) | func (c MemcachedCache) Add(key string, value interface{}, expires tim...
method Replace (line 33) | func (c MemcachedCache) Replace(key string, value interface{}, expires...
method Get (line 37) | func (c MemcachedCache) Get(key string, ptrValue interface{}) error {
method GetMulti (line 45) | func (c MemcachedCache) GetMulti(keys ...string) (Getter, error) {
method Delete (line 53) | func (c MemcachedCache) Delete(key string) error {
method Increment (line 57) | func (c MemcachedCache) Increment(key string, delta uint64) (newValue ...
method Decrement (line 62) | func (c MemcachedCache) Decrement(key string, delta uint64) (newValue ...
method Flush (line 67) | func (c MemcachedCache) Flush() error {
method invoke (line 73) | func (c MemcachedCache) invoke(f func(*memcache.Client, *memcache.Item...
function NewMemcachedCache (line 21) | func NewMemcachedCache(hostList []string, defaultExpiration time.Duratio...
type ItemMapGetter (line 94) | type ItemMapGetter
method Get (line 96) | func (g ItemMapGetter) Get(key string, ptrValue interface{}) error {
function convertMemcacheError (line 105) | func convertMemcacheError(err error) error {
FILE: cache/memcached_test.go
constant testServer (line 14) | testServer = "localhost:11211"
function TestMemcachedCache_TypicalGetSet (line 30) | func TestMemcachedCache_TypicalGetSet(t *testing.T) {
function TestMemcachedCache_IncrDecr (line 34) | func TestMemcachedCache_IncrDecr(t *testing.T) {
function TestMemcachedCache_Expiration (line 38) | func TestMemcachedCache_Expiration(t *testing.T) {
function TestMemcachedCache_EmptyCache (line 42) | func TestMemcachedCache_EmptyCache(t *testing.T) {
function TestMemcachedCache_Replace (line 46) | func TestMemcachedCache_Replace(t *testing.T) {
function TestMemcachedCache_Add (line 50) | func TestMemcachedCache_Add(t *testing.T) {
function TestMemcachedCache_GetMulti (line 54) | func TestMemcachedCache_GetMulti(t *testing.T) {
FILE: cache/redis.go
type RedisCache (line 15) | type RedisCache struct
method Set (line 62) | func (c RedisCache) Set(key string, value interface{}, expires time.Du...
method Add (line 70) | func (c RedisCache) Add(key string, value interface{}, expires time.Du...
method Replace (line 85) | func (c RedisCache) Replace(key string, value interface{}, expires tim...
method Get (line 105) | func (c RedisCache) Get(key string, ptrValue interface{}) error {
method GetMulti (line 131) | func (c RedisCache) GetMulti(keys ...string) (Getter, error) {
method Delete (line 161) | func (c RedisCache) Delete(key string) error {
method Increment (line 173) | func (c RedisCache) Increment(key string, delta uint64) (uint64, error) {
method Decrement (line 200) | func (c RedisCache) Decrement(key string, delta uint64) (newValue uint...
method Flush (line 229) | func (c RedisCache) Flush() error {
method invoke (line 238) | func (c RedisCache) invoke(f func(string, ...interface{}) (interface{}...
function NewRedisCache (line 22) | func NewRedisCache(host string, password string, defaultExpiration time....
function generalizeStringSlice (line 123) | func generalizeStringSlice(strs []string) []interface{} {
function exists (line 157) | func exists(conn redis.Conn, key string) (bool, error) {
type RedisItemMapGetter (line 264) | type RedisItemMapGetter
method Get (line 266) | func (g RedisItemMapGetter) Get(key string, ptrValue interface{}) error {
FILE: cache/redis_test.go
constant redisTestServer (line 17) | redisTestServer = "localhost:6379"
function TestRedisCache_TypicalGetSet (line 40) | func TestRedisCache_TypicalGetSet(t *testing.T) {
function TestRedisCache_IncrDecr (line 44) | func TestRedisCache_IncrDecr(t *testing.T) {
function TestRedisCache_Expiration (line 48) | func TestRedisCache_Expiration(t *testing.T) {
function TestRedisCache_EmptyCache (line 52) | func TestRedisCache_EmptyCache(t *testing.T) {
function TestRedisCache_Replace (line 56) | func TestRedisCache_Replace(t *testing.T) {
function TestRedisCache_Add (line 60) | func TestRedisCache_Add(t *testing.T) {
function TestRedisCache_GetMulti (line 64) | func TestRedisCache_GetMulti(t *testing.T) {
FILE: cache/serialization.go
function Serialize (line 18) | func Serialize(value interface{}) ([]byte, error) {
function Deserialize (line 41) | func Deserialize(byt []byte, ptr interface{}) (err error) {
FILE: cache/serialization_test.go
type Struct1 (line 12) | type Struct1 struct
method Method1 (line 16) | func (s Struct1) Method1() {}
type Interface1 (line 18) | type Interface1 interface
function TestRoundTrip (line 59) | func TestRoundTrip(t *testing.T) {
FILE: compress.go
type WriteFlusher (line 39) | type WriteFlusher interface
type CompressResponseWriter (line 46) | type CompressResponseWriter struct
method CloseNotify (line 89) | func (c CompressResponseWriter) CloseNotify() <-chan bool {
method prepareHeaders (line 97) | func (c *CompressResponseWriter) prepareHeaders() {
method WriteHeader (line 126) | func (c *CompressResponseWriter) WriteHeader(status int) {
method Close (line 136) | func (c *CompressResponseWriter) Close() error {
method Write (line 161) | func (c *CompressResponseWriter) Write(b []byte) (int, error) {
function CompressFilter (line 60) | func CompressFilter(c *Controller, fc []Filter) {
function detectCompressionType (line 190) | func detectCompressionType(req *Request, resp *Response) (found bool, co...
type BufferedServerHeader (line 273) | type BufferedServerHeader struct
method SetCookie (line 287) | func (bsh *BufferedServerHeader) SetCookie(cookie string) {
method GetCookie (line 296) | func (bsh *BufferedServerHeader) GetCookie(key string) (ServerCookie, ...
method Set (line 301) | func (bsh *BufferedServerHeader) Set(key string, value string) {
method Add (line 310) | func (bsh *BufferedServerHeader) Add(key string, value string) {
method Del (line 323) | func (bsh *BufferedServerHeader) Del(key string) {
method Get (line 332) | func (bsh *BufferedServerHeader) Get(key string) (value []string) {
method GetKeys (line 346) | func (bsh *BufferedServerHeader) GetKeys() (value []string) {
method SetStatus (line 368) | func (bsh *BufferedServerHeader) SetStatus(statusCode int) {
method Release (line 377) | func (bsh *BufferedServerHeader) Release() {
function NewBufferedServerHeader (line 282) | func NewBufferedServerHeader(o ServerHeader) *BufferedServerHeader {
FILE: compress_test.go
function TestBenchmarkCompressed (line 14) | func TestBenchmarkCompressed(t *testing.T) {
function BenchmarkRenderCompressed (line 29) | func BenchmarkRenderCompressed(b *testing.B) {
function BenchmarkRenderUnCompressed (line 46) | func BenchmarkRenderUnCompressed(b *testing.B) {
FILE: controller.go
type Controller (line 26) | type Controller struct
method SetController (line 67) | func (c *Controller) SetController(context ServerContext) {
method Destroy (line 79) | func (c *Controller) Destroy() {
method FlashParams (line 118) | func (c *Controller) FlashParams() {
method SetCookie (line 124) | func (c *Controller) SetCookie(cookie *http.Cookie) {
method RenderError (line 132) | func (c *Controller) RenderError(err error) Result {
method setStatusIfNil (line 142) | func (c *Controller) setStatusIfNil(status int) {
method Render (line 170) | func (c *Controller) Render(extraViewArgs ...interface{}) Result {
method RenderTemplate (line 199) | func (c *Controller) RenderTemplate(templatePath string) Result {
method TemplateOutput (line 216) | func (c *Controller) TemplateOutput(templatePath string) (data []byte,...
method RenderJSON (line 221) | func (c *Controller) RenderJSON(o interface{}) Result {
method RenderJSONP (line 228) | func (c *Controller) RenderJSONP(callback string, o interface{}) Result {
method RenderXML (line 235) | func (c *Controller) RenderXML(o interface{}) Result {
method RenderText (line 242) | func (c *Controller) RenderText(text string, objs ...interface{}) Resu...
method RenderHTML (line 253) | func (c *Controller) RenderHTML(html string) Result {
method Todo (line 261) | func (c *Controller) Todo() Result {
method NotFound (line 272) | func (c *Controller) NotFound(msg string, objs ...interface{}) Result {
method Forbidden (line 286) | func (c *Controller) Forbidden(msg string, objs ...interface{}) Result {
method RenderFileName (line 301) | func (c *Controller) RenderFileName(filename string, delivery ContentD...
method RenderFile (line 312) | func (c *Controller) RenderFile(file *os.File, delivery ContentDisposi...
method RenderBinary (line 333) | func (c *Controller) RenderBinary(memfile io.Reader, filename string, ...
method Redirect (line 349) | func (c *Controller) Redirect(val interface{}, args ...interface{}) Re...
method Stats (line 363) | func (c *Controller) Stats() map[string]interface{} {
method Message (line 378) | func (c *Controller) Message(message string, args ...interface{}) stri...
method SetAction (line 384) | func (c *Controller) SetAction(controllerName, methodName string) error {
method SetTypeAction (line 389) | func (c *Controller) SetTypeAction(controllerName, methodName string, ...
method setAppControllerFields (line 454) | func (c *Controller) setAppControllerFields() {
method resetAppControllerFields (line 463) | func (c *Controller) resetAppControllerFields() {
function NewControllerEmpty (line 55) | func NewControllerEmpty() *Controller {
function NewController (line 60) | func NewController(context ServerContext) *Controller {
type ErrorCoder (line 128) | type ErrorCoder interface
function ControllerTypeByName (line 433) | func ControllerTypeByName(controllerName string, moduleSource *Module) (...
function findControllers (line 471) | func findControllers(appControllerType reflect.Type) (indexes [][]int) {
function RegisterController (line 524) | func RegisterController(c interface{}, methods []*MethodType) {
FILE: controller_type.go
type ControllerType (line 9) | type ControllerType struct
method Method (line 72) | func (ct *ControllerType) Method(name string) *MethodType {
method Name (line 83) | func (ct *ControllerType) Name() string {
method ShortName (line 88) | func (ct *ControllerType) ShortName() string {
type ControllerTypeEvents (line 18) | type ControllerTypeEvents struct
method check (line 102) | func (cte *ControllerTypeEvents) check(theType reflect.Type, fieldPath...
type ControllerFieldPath (line 24) | type ControllerFieldPath struct
method Invoke (line 150) | func (fieldPath *ControllerFieldPath) Invoke(value reflect.Value, inpu...
type MethodType (line 30) | type MethodType struct
type MethodArg (line 38) | type MethodArg struct
function AddControllerType (line 45) | func AddControllerType(moduleSource *Module, controllerType reflect.Type...
function NewControllerTypeEvents (line 92) | func NewControllerTypeEvents(c *ControllerType) (ce *ControllerTypeEvent...
function newFieldPath (line 146) | func newFieldPath(isPointer bool, value reflect.Value, fieldPath []int) ...
FILE: errors.go
type Error (line 16) | type Error struct
method Error (line 74) | func (e *Error) Error() string {
method ContextSource (line 96) | func (e *Error) ContextSource() []SourceLine {
method SetLink (line 118) | func (e *Error) SetLink(errorLink string) {
type SourceLine (line 27) | type SourceLine struct
function NewErrorFromPanic (line 36) | func NewErrorFromPanic(err interface{}) *Error {
function findRelevantStackFrame (line 127) | func findRelevantStackFrame(stack string) (int, string) {
FILE: event.go
type Event (line 5) | type Event
type EventResponse (line 7) | type EventResponse
type EventHandler (line 9) | type EventHandler
constant TEMPLATE_REFRESH_REQUESTED (line 14) | TEMPLATE_REFRESH_REQUESTED Event = iota
constant TEMPLATE_REFRESH_COMPLETED (line 16) | TEMPLATE_REFRESH_COMPLETED
constant REVEL_BEFORE_MODULES_LOADED (line 20) | REVEL_BEFORE_MODULES_LOADED
constant REVEL_AFTER_MODULES_LOADED (line 22) | REVEL_AFTER_MODULES_LOADED
constant ENGINE_BEFORE_INITIALIZED (line 25) | ENGINE_BEFORE_INITIALIZED
constant ENGINE_STARTED (line 27) | ENGINE_STARTED
constant ENGINE_SHUTDOWN_REQUEST (line 30) | ENGINE_SHUTDOWN_REQUEST
constant ENGINE_SHUTDOWN (line 33) | ENGINE_SHUTDOWN
constant ROUTE_REFRESH_REQUESTED (line 36) | ROUTE_REFRESH_REQUESTED
constant ROUTE_REFRESH_COMPLETED (line 38) | ROUTE_REFRESH_COMPLETED
constant REVEL_FAILURE (line 41) | REVEL_FAILURE
function RaiseEvent (line 45) | func RaiseEvent(key Event, value interface{}) (response EventResponse) {
function AddInitEventHandler (line 54) | func AddInitEventHandler(handler EventHandler) {
FILE: event_test.go
function TestEventHandler (line 11) | func TestEventHandler(t *testing.T) {
FILE: fakeapp_test.go
type Hotel (line 13) | type Hotel struct
type Hotels (line 21) | type Hotels struct
method Show (line 37) | func (c Hotels) Show(id int) Result {
method Book (line 44) | func (c Hotels) Book(id int) Result {
method Index (line 49) | func (c Hotels) Index() Result {
type Static (line 25) | type Static struct
method Serve (line 53) | func (c Static) Serve(prefix, path string) Result {
type Implicit (line 29) | type Implicit struct
type Application (line 33) | type Application struct
function registerControllers (line 72) | func registerControllers() {
function startFakeBookingApp (line 138) | func startFakeBookingApp() {
FILE: field.go
type Field (line 13) | type Field struct
method ID (line 32) | func (f *Field) ID() string {
method Flash (line 37) | func (f *Field) Flash() string {
method Options (line 43) | func (f *Field) Options() []string {
method FlashArray (line 52) | func (f *Field) FlashArray() []string {
method Value (line 61) | func (f *Field) Value() interface{} {
method ErrorClass (line 83) | func (f *Field) ErrorClass() string {
method ShortName (line 94) | func (f *Field) ShortName() string {
method Translate (line 103) | func (f *Field) Translate(text string, args ...interface{}) string {
function NewField (line 20) | func NewField(name string, viewArgs map[string]interface{}) *Field {
FILE: filter.go
type Filter (line 8) | type Filter
FILE: filterconfig.go
type FilterConfigurator (line 55) | type FilterConfigurator struct
method Add (line 106) | func (conf FilterConfigurator) Add(f Filter) FilterConfigurator {
method addFilter (line 113) | func (conf FilterConfigurator) addFilter(f Filter, fc []Filter) []Filt...
method Remove (line 118) | func (conf FilterConfigurator) Remove(target Filter) FilterConfigurator {
method rmFilter (line 125) | func (conf FilterConfigurator) rmFilter(target Filter, fc []Filter) []...
method Insert (line 139) | func (conf FilterConfigurator) Insert(insert Filter, where When, targe...
method insertFilter (line 149) | func (conf FilterConfigurator) insertFilter(insert Filter, where When,...
method getChain (line 164) | func (conf FilterConfigurator) getChain() []Filter {
method apply (line 184) | func (conf FilterConfigurator) apply(f func([]Filter) []Filter) {
function newFilterConfigurator (line 60) | func newFilterConfigurator(controllerName, methodName string) FilterConf...
function FilterController (line 70) | func FilterController(controllerInstance interface{}) FilterConfigurator {
function FilterAction (line 81) | func FilterAction(methodRef interface{}) FilterConfigurator {
function FilterEq (line 200) | func FilterEq(a, b Filter) bool {
function FilterConfiguringFilter (line 206) | func FilterConfiguringFilter(c *Controller, fc []Filter) {
function getOverrideChain (line 215) | func getOverrideChain(controllerName, action string) []Filter {
FILE: filterconfig_test.go
type FakeController (line 9) | type FakeController struct
method Foo (line 11) | func (c FakeController) Foo() {}
method Bar (line 12) | func (c *FakeController) Bar() {}
function TestFilterConfiguratorKey (line 14) | func TestFilterConfiguratorKey(t *testing.T) {
function TestFilterConfigurator (line 36) | func TestFilterConfigurator(t *testing.T) {
function filterSliceEqual (line 130) | func filterSliceEqual(a, e []Filter) bool {
function getOverride (line 139) | func getOverride(methodName string) []Filter {
FILE: flash.go
type Flash (line 18) | type Flash struct
method Error (line 25) | func (f Flash) Error(msg string, args ...interface{}) {
method Success (line 35) | func (f Flash) Success(msg string, args ...interface{}) {
function FlashFilter (line 46) | func FlashFilter(c *Controller, fc []Filter) {
function restoreFlash (line 68) | func restoreFlash(req *Request) Flash {
FILE: http.go
type Request (line 23) | type Request struct
method SetRequest (line 85) | func (req *Request) SetRequest(r ServerRequest) {
method Cookie (line 101) | func (req *Request) Cookie(key string) (ServerCookie, error) {
method GetRequestURI (line 109) | func (req *Request) GetRequestURI() string {
method GetQuery (line 115) | func (req *Request) GetQuery() (v url.Values) {
method GetPath (line 121) | func (req *Request) GetPath() (path string) {
method GetBody (line 127) | func (req *Request) GetBody() (body io.Reader) {
method Context (line 133) | func (req *Request) Context() (c context.Context) {
method FormValue (line 139) | func (req *Request) FormValue(key string) (value string) {
method PostFormValue (line 144) | func (req *Request) PostFormValue(key string) (value string) {
method ParseForm (line 153) | func (req *Request) ParseForm() (e error) {
method GetForm (line 160) | func (req *Request) GetForm() (url.Values, error) {
method MultipartReader (line 177) | func (req *Request) MultipartReader() (*multipart.Reader, error) {
method ParseMultipartForm (line 187) | func (req *Request) ParseMultipartForm(_ int64) (e error) {
method Args (line 196) | func (req *Request) Args() map[string]interface{} {
method GetMultipartForm (line 201) | func (req *Request) GetMultipartForm() (ServerMultipartForm, error) {
method Destroy (line 211) | func (req *Request) Destroy() {
method UserAgent (line 253) | func (r *Request) UserAgent() string {
method Referer (line 258) | func (req *Request) Referer() string {
method GetHttpHeader (line 263) | func (req *Request) GetHttpHeader(key string) string {
method GetValue (line 268) | func (r *Request) GetValue(key int) (value interface{}) {
type Response (line 49) | type Response struct
method SetResponse (line 226) | func (resp *Response) SetResponse(r ServerResponse) {
method Destroy (line 245) | func (resp *Response) Destroy() {
method WriteHeader (line 276) | func (resp *Response) WriteHeader(defaultStatusCode int, defaultConten...
method SetStatus (line 287) | func (resp *Response) SetStatus(statusCode int) {
method GetWriter (line 296) | func (resp *Response) GetWriter() (writer io.Writer) {
method SetWriter (line 308) | func (resp *Response) SetWriter(writer io.Writer) bool {
method GetStreamWriter (line 315) | func (resp *Response) GetStreamWriter() (writer StreamWriter) {
type OutResponse (line 57) | type OutResponse struct
method Destroy (line 234) | func (o *OutResponse) Destroy() {
method Header (line 323) | func (o *OutResponse) Header() *RevelHeader {
method Write (line 328) | func (o *OutResponse) Write(data []byte) (int, error) {
type RevelHeader (line 65) | type RevelHeader struct
method Destroy (line 240) | func (h *RevelHeader) Destroy() {
method Set (line 333) | func (h *RevelHeader) Set(key, value string) {
method Add (line 340) | func (h *RevelHeader) Add(key, value string) {
method SetCookie (line 347) | func (h *RevelHeader) SetCookie(cookie string) {
method SetStatus (line 354) | func (h *RevelHeader) SetStatus(status int) {
method Get (line 361) | func (h *RevelHeader) Get(key string) (value string) {
method GetAll (line 370) | func (h *RevelHeader) GetAll(key string) (values []string) {
function NewResponse (line 70) | func NewResponse(w ServerResponse) (r *Response) {
function NewRequest (line 77) | func NewRequest(r ServerRequest) *Request {
type MultipartForm (line 171) | type MultipartForm struct
function newMultipareForm (line 182) | func newMultipareForm(s ServerMultipartForm) (f *MultipartForm) {
function ResolveContentType (line 380) | func ResolveContentType(req *Request) string {
function ResolveFormat (line 392) | func ResolveFormat(req *Request) string {
type AcceptLanguage (line 428) | type AcceptLanguage struct
type AcceptLanguages (line 434) | type AcceptLanguages
method Len (line 436) | func (al AcceptLanguages) Len() int { return len(al) }
method Swap (line 437) | func (al AcceptLanguages) Swap(i, j int) { al[i], al[j] = al[j], ...
method Less (line 438) | func (al AcceptLanguages) Less(i, j int) bool { return al[i].Quality >...
method String (line 439) | func (al AcceptLanguages) String() string {
function ResolveAcceptLanguage (line 463) | func ResolveAcceptLanguage(req *Request) AcceptLanguages {
FILE: i18n.go
constant CurrentLocaleViewArg (line 20) | CurrentLocaleViewArg = "currentLocale"
constant messageFilesDirectory (line 22) | messageFilesDirectory = "messages"
constant messageFilePattern (line 23) | messageFilePattern = `^\w+\.[a-zA-Z]{2}$`
constant defaultUnknownFormat (line 24) | defaultUnknownFormat = "??? %s ???"
constant unknownFormatConfigKey (line 25) | unknownFormatConfigKey = "i18n.unknown_format"
constant defaultLanguageOption (line 26) | defaultLanguageOption = "i18n.default_language"
constant localeCookieConfigKey (line 27) | localeCookieConfigKey = "i18n.cookie"
function MessageLanguages (line 46) | func MessageLanguages() []string {
function Message (line 59) | func Message(locale, message string, args ...interface{}) string {
function parseLocale (line 108) | func parseLocale(locale string) (language, region string) {
function getUnknownValueFormat (line 118) | func getUnknownValueFormat() string {
function loadMessages (line 123) | func loadMessages(path string) {
function loadMessageFile (line 142) | func loadMessageFile(path string, info os.FileInfo, osError error) error {
function parseMessagesFile (line 173) | func parseMessagesFile(path string) (messageConfig *config.Config, err e...
function parseLocaleFromFileName (line 178) | func parseLocaleFromFileName(file string) string {
function init (line 183) | func init() {
function I18nFilter (line 190) | func I18nFilter(c *Controller, fc []Filter) {
function setCurrentLocaleControllerArguments (line 216) | func setCurrentLocaleControllerArguments(c *Controller, locale string) {
function hasAcceptLanguageHeader (line 225) | func hasAcceptLanguageHeader(request *Request) (bool, string) {
function hasLocaleCookie (line 234) | func hasLocaleCookie(request *Request) (bool, string) {
FILE: i18n_test.go
constant testDataPath (line 18) | testDataPath string = "testdata/i18n"
constant testConfigPath (line 19) | testConfigPath string = "testdata/i18n/config"
constant testConfigName (line 20) | testConfigName string = "test_app.conf"
function TestI18nLoadMessages (line 23) | func TestI18nLoadMessages(t *testing.T) {
function TestI18nMessage (line 32) | func TestI18nMessage(t *testing.T) {
function TestI18nMessageWithDefaultLocale (line 85) | func TestI18nMessageWithDefaultLocale(t *testing.T) {
function TestHasLocaleCookie (line 97) | func TestHasLocaleCookie(t *testing.T) {
function TestHasLocaleCookieWithInvalidConfig (line 111) | func TestHasLocaleCookieWithInvalidConfig(t *testing.T) {
function TestHasAcceptLanguageHeader (line 121) | func TestHasAcceptLanguageHeader(t *testing.T) {
function TestBeforeRequest (line 130) | func TestBeforeRequest(t *testing.T) {
function TestI18nMessageUnknownValueFormat (line 149) | func TestI18nMessageUnknownValueFormat(t *testing.T) {
function BenchmarkI18nLoadMessages (line 167) | func BenchmarkI18nLoadMessages(b *testing.B) {
function BenchmarkI18nMessage (line 179) | func BenchmarkI18nMessage(b *testing.B) {
function BenchmarkI18nMessageWithArguments (line 185) | func BenchmarkI18nMessageWithArguments(b *testing.B) {
function BenchmarkI18nMessageWithFoldingAndArguments (line 197) | func BenchmarkI18nMessageWithFoldingAndArguments(b *testing.B) {
function excludeFromTimer (line 210) | func excludeFromTimer(b *testing.B, f func()) {
function loadTestI18nConfig (line 216) | func loadTestI18nConfig(t *testing.T) {
function loadTestI18nConfigWithoutLanguageCookieOption (line 226) | func loadTestI18nConfigWithoutLanguageCookieOption(t *testing.T) {
function loadTestI18nConfigWithUnknowFormatOption (line 231) | func loadTestI18nConfigWithUnknowFormatOption(t *testing.T) {
function buildRequestWithCookie (line 236) | func buildRequestWithCookie(name, value string) *Controller {
function buildRequestWithAcceptLanguages (line 256) | func buildRequestWithAcceptLanguages(acceptLanguages ...string) *Control...
function buildEmptyRequest (line 267) | func buildEmptyRequest() *Controller {
FILE: intercept.go
type InterceptorFunc (line 42) | type InterceptorFunc
type InterceptorMethod (line 44) | type InterceptorMethod interface
type When (line 46) | type When
constant BEFORE (line 49) | BEFORE When = iota
constant AFTER (line 50) | AFTER
constant PANIC (line 51) | PANIC
constant FINALLY (line 52) | FINALLY
type InterceptTarget (line 55) | type InterceptTarget
constant AllControllers (line 58) | AllControllers InterceptTarget = iota
type Interception (line 61) | type Interception struct
method Invoke (line 74) | func (i Interception) Invoke(val reflect.Value, target *reflect.Value)...
function InterceptorFilter (line 95) | func InterceptorFilter(c *Controller, fc []Filter) {
function invokeInterceptors (line 114) | func invokeInterceptors(when When, c *Controller) {
function InterceptFunc (line 141) | func InterceptFunc(intc InterceptorFunc, when When, target interface{}) {
function InterceptMethod (line 154) | func InterceptMethod(intc InterceptorMethod, when When) {
type interceptorItem (line 171) | type interceptorItem struct
type interceptorItemList (line 176) | type interceptorItemList
method Len (line 178) | func (a interceptorItemList) Len() int { return len(a) }
method Swap (line 179) | func (a interceptorItemList) Swap(i, j int) { a[i], a[j] = a[j], ...
method Less (line 180) | func (a interceptorItemList) Less(i, j int) bool { return a[i].Level <...
type reverseInterceptorItemList (line 182) | type reverseInterceptorItemList
method Len (line 184) | func (a reverseInterceptorItemList) Len() int { return len(a) }
method Swap (line 185) | func (a reverseInterceptorItemList) Swap(i, j int) { a[i], a[j] =...
method Less (line 186) | func (a reverseInterceptorItemList) Less(i, j int) bool { return a[i]....
function getInterceptors (line 187) | func getInterceptors(when When, val reflect.Value) interceptorItemList {
function findTarget (line 213) | func findTarget(val reflect.Value, target reflect.Type) (int, reflect.Va...
FILE: intercept_test.go
type InterceptController (line 18) | type InterceptController struct
method methN (line 28) | func (c InterceptController) methN() Result { return nil }
method methP (line 29) | func (c *InterceptController) methP() Result { return nil }
type InterceptControllerN (line 19) | type InterceptControllerN struct
method methNN (line 31) | func (c InterceptControllerN) methNN() Result { return nil }
method methNP (line 32) | func (c *InterceptControllerN) methNP() Result { return nil }
type InterceptControllerP (line 20) | type InterceptControllerP struct
method methPN (line 33) | func (c InterceptControllerP) methPN() Result { return nil }
method methPP (line 34) | func (c *InterceptControllerP) methPP() Result { return nil }
type InterceptControllerNP (line 21) | type InterceptControllerNP struct
function TestInvokeArgType (line 54) | func TestInvokeArgType(t *testing.T) {
function testInterceptorController (line 64) | func testInterceptorController(t *testing.T, appControllerPtr reflect.Va...
function testInterception (line 84) | func testInterception(t *testing.T, intc *interceptorItem, arg reflect.V...
FILE: invoker.go
function ActionInvoker (line 17) | func ActionInvoker(c *Controller, _ []Filter) {
FILE: invoker_test.go
type P (line 16) | type P struct
type PN (line 18) | type PN struct
type PNN (line 20) | type PNN struct
type P2 (line 23) | type P2 struct
type PP2 (line 25) | type PP2 struct
function TestFindControllers (line 32) | func TestFindControllers(t *testing.T) {
function checkSearchResults (line 46) | func checkSearchResults(t *testing.T, obj interface{}, expected [][]int) {
function TestSetAction (line 53) | func TestSetAction(t *testing.T) {
function BenchmarkSetAction (line 84) | func BenchmarkSetAction(b *testing.B) {
function BenchmarkInvoker (line 123) | func BenchmarkInvoker(b *testing.B) {
FILE: libs.go
function Sign (line 19) | func Sign(message string) string {
function Verify (line 33) | func Verify(message, sig string) bool {
function ToBool (line 46) | func ToBool(val interface{}) bool {
function Atob (line 78) | func Atob(v string) bool {
FILE: libs_test.go
function TestToBooleanForFalse (line 9) | func TestToBooleanForFalse(t *testing.T) {
function TestToBooleanForTrue (line 25) | func TestToBooleanForTrue(t *testing.T) {
FILE: logger.go
function init (line 29) | func init() {
function initLoggers (line 37) | func initLoggers() {
function setAppLog (line 46) | func setAppLog(appLog logger.MultiLogger, appHandler *logger.CompositeMu...
FILE: logger/composite_multihandler.go
type CompositeMultiHandler (line 11) | type CompositeMultiHandler struct
method Log (line 24) | func (h *CompositeMultiHandler) Log(r *Record) (err error) {
method SetHandler (line 47) | func (h *CompositeMultiHandler) SetHandler(handler LogHandler, replace...
method SetHandlers (line 83) | func (h *CompositeMultiHandler) SetHandlers(handler LogHandler, option...
method SetJson (line 94) | func (h *CompositeMultiHandler) SetJson(writer io.Writer, options *Log...
method SetJsonFile (line 106) | func (h *CompositeMultiHandler) SetJsonFile(filePath string, options *...
method SetTerminal (line 117) | func (h *CompositeMultiHandler) SetTerminal(writer io.Writer, options ...
method SetTerminalFile (line 146) | func (h *CompositeMultiHandler) SetTerminalFile(filePath string, optio...
method Disable (line 157) | func (h *CompositeMultiHandler) Disable(levels ...LogLevel) {
function NewCompositeMultiHandler (line 19) | func NewCompositeMultiHandler() (*CompositeMultiHandler, LogHandler) {
FILE: logger/handlers.go
type LevelFilterHandler (line 8) | type LevelFilterHandler struct
method Log (line 20) | func (h LevelFilterHandler) Log(r *Record) error {
function LevelHandler (line 15) | func LevelHandler(lvl LogLevel, h LogHandler) LogHandler {
function MinLevelHandler (line 29) | func MinLevelHandler(lvl LogLevel, h LogHandler) LogHandler {
function NotLevelHandler (line 37) | func NotLevelHandler(lvl LogLevel, h LogHandler) LogHandler {
function CallerFileHandler (line 43) | func CallerFileHandler(h LogHandler) LogHandler {
function CallerFuncHandler (line 52) | func CallerFuncHandler(h LogHandler) LogHandler {
function MatchHandler (line 61) | func MatchHandler(key string, value interface{}, h LogHandler) LogHandler {
function MatchFilterHandler (line 72) | func MatchFilterHandler(key string, value interface{}, h LogHandler) Log...
function MatchAbHandler (line 79) | func MatchAbHandler(key string, value interface{}, a, b LogHandler) LogH...
function NilHandler (line 92) | func NilHandler() LogHandler {
function MatchMapHandler (line 99) | func MatchMapHandler(matchMap map[string]interface{}, a LogHandler) LogH...
function NotMatchMapHandler (line 104) | func NotMatchMapHandler(matchMap map[string]interface{}, a LogHandler) L...
function matchMapHandler (line 109) | func matchMapHandler(matchMap map[string]interface{}, inverse bool, a Lo...
function NotMatchHandler (line 133) | func NotMatchHandler(key string, value interface{}, h LogHandler) LogHan...
function MultiHandler (line 139) | func MultiHandler(hs ...LogHandler) LogHandler {
function StreamHandler (line 156) | func StreamHandler(wr io.Writer, fmtr LogFormat) LogHandler {
function FilterHandler (line 165) | func FilterHandler(fn func(r *Record) bool, h LogHandler) LogHandler {
type ListLogHandler (line 175) | type ListLogHandler struct
method Log (line 186) | func (ll *ListLogHandler) Log(r *Record) (err error) {
method Add (line 198) | func (ll *ListLogHandler) Add(h LogHandler) {
method Del (line 205) | func (ll *ListLogHandler) Del(h LogHandler) {
function NewListLogHandler (line 180) | func NewListLogHandler(h1, h2 LogHandler) *ListLogHandler {
FILE: logger/init.go
function InitializeFromConfig (line 14) | func InitializeFromConfig(basePath string, config *config.Context) (c *C...
function initAllLog (line 48) | func initAllLog(c *CompositeMultiHandler, basePath string, config *confi...
function initFilterLog (line 64) | func initFilterLog(c *CompositeMultiHandler, basePath string, config *co...
function initLogLevels (line 99) | func initLogLevels(c *CompositeMultiHandler, basePath string, config *co...
function initRequestLog (line 121) | func initRequestLog(c *CompositeMultiHandler, basePath string, config *c...
function initHandlerFor (line 149) | func initHandlerFor(c *CompositeMultiHandler, output, basePath string, o...
FILE: logger/init_test.go
type testCounter (line 19) | type testCounter struct
method Log (line 269) | func (c *testCounter) Log(r *logger.Record) error {
type testData (line 23) | type testData struct
method logTest (line 287) | func (td *testData) logTest(rootLog logger.MultiLogger, t *testing.T) {
method runLogTest (line 312) | func (td *testData) runLogTest(log logger.MultiLogger) {
method validate (line 320) | func (td *testData) validate(t *testing.T) {
type testResult (line 29) | type testResult struct
function TestSingleCases (line 59) | func TestSingleCases(t *testing.T) {
function TestFilterCases (line 112) | func TestFilterCases(t *testing.T) {
function TestNotFilterCases (line 169) | func TestNotFilterCases(t *testing.T) {
function TestOffCases (line 186) | func TestOffCases(t *testing.T) {
function TestDuplicateCases (line 203) | func TestDuplicateCases(t *testing.T) {
function TestContradictCases (line 232) | func TestContradictCases(t *testing.T) {
function TestAllCases (line 253) | func TestAllCases(t *testing.T) {
function counterInit (line 330) | func counterInit(tc *testCounter) {
FILE: logger/logger.go
type MultiLogger (line 14) | type MultiLogger interface
type LogHandler (line 68) | type LogHandler interface
type LogStackHandler (line 74) | type LogStackHandler interface
type ParentLogHandler (line 80) | type ParentLogHandler interface
type LogFormat (line 85) | type LogFormat interface
type LogLevel (line 90) | type LogLevel
type LogOptions (line 93) | type LogOptions struct
method SetExtendedOptions (line 177) | func (l *LogOptions) SetExtendedOptions(options ...interface{}) {
method GetStringDefault (line 184) | func (l *LogOptions) GetStringDefault(option, value string) string {
method GetIntDefault (line 192) | func (l *LogOptions) GetIntDefault(option string, value int) int {
method GetBoolDefault (line 200) | func (l *LogOptions) GetBoolDefault(option string, value bool) bool {
type Record (line 102) | type Record struct
type Lazy (line 111) | type Lazy struct
type CallStack (line 117) | type CallStack interface
function FormatFunc (line 124) | func FormatFunc(f func(*Record) []byte) LogFormat {
type formatFunc (line 128) | type formatFunc
method Format (line 130) | func (f formatFunc) Format(r *Record) []byte {
function NewRecord (line 134) | func NewRecord(message string, level LogLevel) *Record {
constant LvlCrit (line 139) | LvlCrit LogLevel = iota
constant LvlError (line 140) | LvlError
constant LvlWarn (line 141) | LvlWarn
constant LvlInfo (line 142) | LvlInfo
constant LvlDebug (line 143) | LvlDebug
type parentLogHandler (line 150) | type parentLogHandler struct
method SetChild (line 160) | func (p *parentLogHandler) SetChild(child LogHandler) LogHandler {
function NewParentLogHandler (line 155) | func NewParentLogHandler(callBack func(child LogHandler) LogHandler) Par...
function NewLogOptions (line 165) | func NewLogOptions(cfg *config.Context, replaceHandler bool, phandler Pa...
FILE: logger/revel_logger.go
type RevelLogger (line 12) | type RevelLogger struct
method Debugf (line 24) | func (rl *RevelLogger) Debugf(msg string, param ...interface{}) {
method Infof (line 29) | func (rl *RevelLogger) Infof(msg string, param ...interface{}) {
method Warnf (line 34) | func (rl *RevelLogger) Warnf(msg string, param ...interface{}) {
method Errorf (line 39) | func (rl *RevelLogger) Errorf(msg string, param ...interface{}) {
method Critf (line 44) | func (rl *RevelLogger) Critf(msg string, param ...interface{}) {
method Fatalf (line 49) | func (rl *RevelLogger) Fatalf(msg string, param ...interface{}) {
method Panicf (line 54) | func (rl *RevelLogger) Panicf(msg string, param ...interface{}) {
method Fatal (line 59) | func (rl *RevelLogger) Fatal(msg string, ctx ...interface{}) {
method Panic (line 65) | func (rl *RevelLogger) Panic(msg string, ctx ...interface{}) {
method New (line 71) | func (rl *RevelLogger) New(ctx ...interface{}) MultiLogger {
method SetStackDepth (line 77) | func (rl *RevelLogger) SetStackDepth(amount int) MultiLogger {
method SetHandler (line 90) | func (rl *RevelLogger) SetHandler(h LogHandler) {
function SetDefaultLog (line 18) | func SetDefaultLog(fromLog MultiLogger) {
function New (line 83) | func New(ctx ...interface{}) MultiLogger {
type callHandler (line 95) | type callHandler
method Log (line 99) | func (c callHandler) Log(log *log15.Record) error {
type ContextMap (line 127) | type ContextMap
method StringMap (line 130) | func (m ContextMap) StringMap() (newMap map[string]string) {
method Add (line 142) | func (m ContextMap) Add(key string, value interface{}) {
FILE: logger/terminal_format.go
constant timeFormat (line 14) | timeFormat = "2006-01-02T15:04:05-0700"
constant termTimeFormat (line 15) | termTimeFormat = "2006/01/02 15:04:05"
constant termSmallTimeFormat (line 16) | termSmallTimeFormat = "15:04:05"
constant floatFormat (line 17) | floatFormat = 'f'
constant errorKey (line 18) | errorKey = "REVEL_ERROR"
function TerminalFormatHandler (line 28) | func TerminalFormatHandler(noColor bool, smallDate bool) LogFormat {
function formatLogfmtValue (line 96) | func formatLogfmtValue(value interface{}) string {
function formatShared (line 125) | func formatShared(value interface{}) (result interface{}) {
function escapeString (line 157) | func escapeString(s string) string {
function JsonFormatEx (line 203) | func JsonFormatEx(pretty, lineSeparated bool) LogFormat {
function formatJsonValue (line 237) | func formatJsonValue(value interface{}) interface{} {
FILE: logger/utils.go
constant TEST_MODE_FLAG (line 23) | TEST_MODE_FLAG = "testModeFlag"
constant SPECIAL_USE_FLAG (line 25) | SPECIAL_USE_FLAG = "specialUseFlag"
function GetLogger (line 29) | func GetLogger(name string, logger MultiLogger) (l *log.Logger) {
type loggerRewrite (line 69) | type loggerRewrite struct
method Write (line 79) | func (lr loggerRewrite) Write(p []byte) (n int, err error) {
function NewCallStack (line 108) | func NewCallStack() interface{} {
FILE: logger/wrap_handlers.go
function FuncHandler (line 13) | func FuncHandler(fn func(r *Record) error) LogHandler {
type funcHandler (line 18) | type funcHandler
method Log (line 21) | func (h funcHandler) Log(r *Record) error {
function HandlerFunc (line 27) | func HandlerFunc(log func(message string, time time.Time, level LogLevel...
type remoteHandler (line 32) | type remoteHandler
method Log (line 35) | func (c remoteHandler) Log(record *Record) error {
function SyncHandler (line 42) | func SyncHandler(h LogHandler) LogHandler {
function LazyHandler (line 55) | func LazyHandler(h LogHandler) LogHandler {
function evaluateLazy (line 70) | func evaluateLazy(lz Lazy) (interface{}, error) {
FILE: model/revel_container.go
type RevelContainer (line 5) | type RevelContainer struct
FILE: model/revel_controller.go
type RevelController (line 5) | type RevelController struct
FILE: model/revel_paths.go
type RevelPaths (line 3) | type RevelPaths struct
FILE: model/revel_unit.go
constant APP (line 4) | APP RevelUnitType = 1
constant MODULE (line 5) | MODULE RevelUnitType = 2
constant REVEL (line 6) | REVEL RevelUnitType = 3
type RevelUnit (line 10) | type RevelUnit struct
type RevelUnitList (line 19) | type RevelUnitList
type RevelUnitType (line 20) | type RevelUnitType
FILE: module.go
type Module (line 14) | type Module struct
method Namespace (line 61) | func (m *Module) Namespace() (namespace string) {
method ControllerByName (line 67) | func (m *Module) ControllerByName(name, action string) (ctype *Control...
method AddController (line 82) | func (m *Module) AddController(ct *ControllerType) {
type ModuleCallbackInterface (line 22) | type ModuleCallbackInterface
constant namespaceSeperator (line 25) | namespaceSeperator = `\`
function RegisterModuleInit (line 36) | func RegisterModuleInit(callback ModuleCallbackInterface) {
function init (line 48) | func init() {
function ModuleFromPath (line 88) | func ModuleFromPath(packagePath string, addGopathToPath bool) (module *M...
function ModuleByName (line 112) | func ModuleByName(name string) (*Module, bool) {
function loadModules (line 130) | func loadModules() {
function addModule (line 171) | func addModule(name, importPath, modulePath string) {
FILE: namespace.go
function namespaceReplace (line 15) | func namespaceReplace(fileBytes []byte, module *Module) []byte {
FILE: panic.go
function PanicFilter (line 15) | func PanicFilter(c *Controller, fc []Filter) {
function handleInvocationPanic (line 26) | func handleInvocationPanic(c *Controller, err interface{}) {
FILE: params.go
type Params (line 24) | type Params struct
method Bind (line 84) | func (p *Params) Bind(dest interface{}, name string) {
method BindJSON (line 105) | func (p *Params) BindJSON(dest interface{}) error {
method calcValues (line 119) | func (p *Params) calcValues() url.Values {
function ParseParams (line 43) | func ParseParams(params *Params, req *Request) {
function ParamsFilter (line 166) | func ParamsFilter(c *Controller, fc []Filter) {
FILE: params_test.go
constant MultipartBoundary (line 20) | MultipartBoundary = "A"
constant MultipartFormData (line 21) | MultipartFormData = `--A
type fh (line 63) | type fh struct
function getMultipartRequest (line 81) | func getMultipartRequest() *http.Request {
function BenchmarkParams (line 91) | func BenchmarkParams(b *testing.B) {
function TestMultipartForm (line 100) | func TestMultipartForm(t *testing.T) {
function TestBind (line 125) | func TestBind(t *testing.T) {
function TestResolveAcceptLanguage (line 138) | func TestResolveAcceptLanguage(t *testing.T) {
function BenchmarkResolveAcceptLanguage (line 169) | func BenchmarkResolveAcceptLanguage(b *testing.B) {
function buildHTTPRequestWithAcceptLanguage (line 176) | func buildHTTPRequestWithAcceptLanguage(acceptLanguage string) *Request {
FILE: results.go
type Result (line 22) | type Result interface
type ErrorResult (line 29) | type ErrorResult struct
method Apply (line 36) | func (r ErrorResult) Apply(req *Request, resp *Response) {
type PlaintextErrorResult (line 121) | type PlaintextErrorResult struct
method Apply (line 126) | func (r PlaintextErrorResult) Apply(req *Request, resp *Response) {
type RenderTemplateResult (line 135) | type RenderTemplateResult struct
method Apply (line 140) | func (r *RenderTemplateResult) Apply(req *Request, resp *Response) {
method ToBytes (line 189) | func (r *RenderTemplateResult) ToBytes() (b *bytes.Buffer, err error) {
method renderOutput (line 206) | func (r *RenderTemplateResult) renderOutput(wr io.Writer) (err error) {
method compressHtml (line 224) | func (r *RenderTemplateResult) compressHtml(b *bytes.Buffer) (b2 *byte...
method renderError (line 265) | func (r *RenderTemplateResult) renderError(err error, req *Request, re...
type RenderHTMLResult (line 295) | type RenderHTMLResult struct
method Apply (line 299) | func (r RenderHTMLResult) Apply(req *Request, resp *Response) {
type RenderJSONResult (line 306) | type RenderJSONResult struct
method Apply (line 311) | func (r RenderJSONResult) Apply(req *Request, resp *Response) {
type RenderXMLResult (line 345) | type RenderXMLResult struct
method Apply (line 349) | func (r RenderXMLResult) Apply(req *Request, resp *Response) {
type RenderTextResult (line 369) | type RenderTextResult struct
method Apply (line 373) | func (r RenderTextResult) Apply(req *Request, resp *Response) {
type ContentDisposition (line 380) | type ContentDisposition
type BinaryResult (line 388) | type BinaryResult struct
method Apply (line 396) | func (r *BinaryResult) Apply(req *Request, resp *Response) {
type RedirectToURLResult (line 435) | type RedirectToURLResult struct
method Apply (line 439) | func (r *RedirectToURLResult) Apply(req *Request, resp *Response) {
type RedirectToActionResult (line 444) | type RedirectToActionResult struct
method Apply (line 449) | func (r *RedirectToActionResult) Apply(req *Request, resp *Response) {
function getRedirectURL (line 460) | func getRedirectURL(item interface{}, args []interface{}) (string, error) {
FILE: results_test.go
function TestRedirect (line 15) | func TestRedirect(t *testing.T) {
function TestBenchmarkRender (line 27) | func TestBenchmarkRender(t *testing.T) {
function BenchmarkRenderChunked (line 41) | func BenchmarkRenderChunked(b *testing.B) {
function BenchmarkRenderNotChunked (line 58) | func BenchmarkRenderNotChunked(b *testing.B) {
FILE: revel.go
constant RevelImportPath (line 22) | RevelImportPath = "github.com/revel/revel"
constant TEST_MODE_FLAG (line 26) | TEST_MODE_FLAG = "testModeFlag"
constant SPECIAL_USE_FLAG (line 27) | SPECIAL_USE_FLAG = "specialUseFlag"
function Init (line 100) | func Init(inputmode, importPath, srcPath string) {
function updateLog (line 207) | func updateLog(inputmode string) (returnMode string) {
function SetSecretKey (line 266) | func SetSecretKey(newKey []byte) error {
function ResolveImportPath (line 273) | func ResolveImportPath(importPath string) (string, error) {
function CheckInit (line 289) | func CheckInit() {
function findSrcPaths (line 297) | func findSrcPaths(importPath string) (revelSourcePath, appSourcePath str...
FILE: revel_hooks.go
type RevelHook (line 10) | type RevelHook struct
type RevelHooks (line 15) | type RevelHooks
method Run (line 27) | func (r RevelHooks) Run() {
method Add (line 37) | func (r RevelHooks) Add(fn func(), order ...int) RevelHooks {
method Len (line 46) | func (slice RevelHooks) Len() int {
method Less (line 51) | func (slice RevelHooks) Less(i, j int) bool {
method Swap (line 56) | func (slice RevelHooks) Swap(i, j int) {
function OnAppStart (line 96) | func OnAppStart(f func(), order ...int) {
function OnAppStop (line 101) | func OnAppStop(f func(), order ...int) {
FILE: revel_test.go
function NewTestController (line 7) | func NewTestController(w http.ResponseWriter, r *http.Request) *Controll...
FILE: router.go
constant httpStatusCode (line 25) | httpStatusCode = "404"
type Route (line 28) | type Route struct
method ActionPath (line 152) | func (route *Route) ActionPath() string {
type RouteMatch (line 44) | type RouteMatch struct
type ActionPathData (line 54) | type ActionPathData struct
function init (line 77) | func init() {
function NewRoute (line 91) | func NewRoute(moduleSource *Module, method, path, action, fixedArgs, rou...
function treePath (line 156) | func treePath(method, path string) string {
type Router (line 163) | type Router struct
method Route (line 170) | func (router *Router) Route(req *Request) (routeMatch *RouteMatch) {
method Refresh (line 248) | func (router *Router) Refresh() (err *Error) {
method updateTree (line 259) | func (router *Router) updateTree() *Error {
method Reverse (line 620) | func (router *Router) Reverse(action string, argValues map[string]stri...
method ReverseError (line 628) | func (router *Router) ReverseError(action string, argValues map[string...
function splitActionPath (line 293) | func splitActionPath(actionPathData *ActionPathData, actionPath string, ...
function parseRoutesFile (line 431) | func parseRoutesFile(moduleSource *Module, routesPath, joinedPath string...
function parseRoutes (line 443) | func parseRoutes(moduleSource *Module, routesPath, joinedPath, content s...
function validateRoute (line 507) | func validateRoute(route *Route) error {
function routeError (line 537) | func routeError(err error, routesPath, content string, n int) *Error {
function getModuleRoutes (line 561) | func getModuleRoutes(moduleName, joinedPath string, validate bool) (rout...
function parseRouteLine (line 593) | func parseRouteLine(line string) (method, path, action, fixedArgs string...
function NewRouter (line 603) | func NewRouter(routesPath string) *Router {
type ActionDefinition (line 610) | type ActionDefinition struct
method String (line 616) | func (a *ActionDefinition) String() string {
function RouterFilter (line 770) | func RouterFilter(c *Controller, fc []Filter) {
function HTTPMethodOverride (line 817) | func HTTPMethodOverride(c *Controller, fc []Filter) {
function init (line 855) | func init() {
FILE: router_test.go
function TestComputeRoute (line 97) | func TestComputeRoute(t *testing.T) {
constant TestRoutes (line 116) | TestRoutes = `
function TestRouteMatches (line 368) | func TestRouteMatches(t *testing.T) {
type ReverseRouteArgs (line 407) | type ReverseRouteArgs struct
function initControllers (line 546) | func initControllers() {
function TestReverseRouting (line 550) | func TestReverseRouting(t *testing.T) {
function BenchmarkRouter (line 566) | func BenchmarkRouter(b *testing.B) {
function BenchmarkLargeRouter (line 585) | func BenchmarkLargeRouter(b *testing.B) {
function BenchmarkRouterFilter (line 638) | func BenchmarkRouterFilter(b *testing.B) {
function TestOverrideMethodFilter (line 657) | func TestOverrideMethodFilter(t *testing.T) {
function eq (line 669) | func eq(t *testing.T, name string, a, b interface{}) bool {
FILE: server-engine.go
constant _ (line 18) | _ = iota
constant ENGINE_RESPONSE_STATUS (line 19) | ENGINE_RESPONSE_STATUS
constant ENGINE_WRITER (line 20) | ENGINE_WRITER
constant ENGINE_PARAMETERS (line 21) | ENGINE_PARAMETERS
constant ENGINE_PATH (line 22) | ENGINE_PATH
constant ENGINE_REQUEST (line 23) | ENGINE_REQUEST
constant ENGINE_RESPONSE (line 24) | ENGINE_RESPONSE
constant HTTP_QUERY (line 29) | HTTP_QUERY = ENGINE_PARAMETERS
constant HTTP_PATH (line 30) | HTTP_PATH = ENGINE_PATH
constant HTTP_BODY (line 31) | HTTP_BODY = iota + 1000
constant HTTP_FORM (line 32) | HTTP_FORM = iota + 1000
constant HTTP_MULTIPART_FORM (line 33) | HTTP_MULTIPART_FORM = iota + 1000
constant HTTP_METHOD (line 34) | HTTP_METHOD = iota + 1000
constant HTTP_REQUEST_URI (line 35) | HTTP_REQUEST_URI = iota + 1000
constant HTTP_REQUEST_CONTEXT (line 36) | HTTP_REQUEST_CONTEXT = iota + 1000
constant HTTP_REMOTE_ADDR (line 37) | HTTP_REMOTE_ADDR = iota + 1000
constant HTTP_HOST (line 38) | HTTP_HOST = iota + 1000
constant HTTP_URL (line 39) | HTTP_URL = iota + 1000
constant HTTP_SERVER_HEADER (line 40) | HTTP_SERVER_HEADER = iota + 1000
constant HTTP_STREAM_WRITER (line 41) | HTTP_STREAM_WRITER = iota + 1000
constant HTTP_WRITER (line 42) | HTTP_WRITER = ENGINE_WRITER
type ServerContext (line 46) | type ServerContext interface
type ServerRequest (line 52) | type ServerRequest interface
type ServerResponse (line 58) | type ServerResponse interface
type ServerWebSocket (line 62) | type ServerWebSocket interface
type ServerHeader (line 71) | type ServerHeader interface
type ServerCookie (line 83) | type ServerCookie interface
type ServerMultipartForm (line 88) | type ServerMultipartForm interface
type StreamWriter (line 93) | type StreamWriter interface
type ServerEngine (line 97) | type ServerEngine interface
type EngineInit (line 113) | type EngineInit struct
type ServerEngineEmpty (line 122) | type ServerEngineEmpty struct
method Get (line 223) | func (e *ServerEngineEmpty) Get(_ string) interface{} {
method Set (line 227) | func (e *ServerEngineEmpty) Set(_ string, _ interface{}) bool {
type ServerMux (line 126) | type ServerMux struct
type ServerMuxList (line 132) | type ServerMuxList
method Len (line 136) | func (r ServerMuxList) Len() int {
method Less (line 141) | func (r ServerMuxList) Less(i, j int) bool {
method Swap (line 146) | func (r ServerMuxList) Swap(i, j int) {
method Find (line 151) | func (r ServerMuxList) Find(path string) (interface{}, bool) {
function AddHTTPMux (line 162) | func AddHTTPMux(path string, callback interface{}) {
function handleInternal (line 167) | func handleInternal(ctx ServerContext) {
FILE: server.go
function RegisterServerEngine (line 28) | func RegisterServerEngine(name string, loader func() ServerEngine) {
function InitServer (line 37) | func InitServer() {
function Run (line 65) | func Run(port int) {
function InitServerEngine (line 96) | func InitServerEngine(port int, serverEngine string) {
function initControllerStack (line 137) | func initControllerStack() {
function StopServer (line 153) | func StopServer(value interface{}) EventResponse {
FILE: server_adapter_go.go
function init (line 24) | func init() {
type GoHttpServer (line 34) | type GoHttpServer struct
method Init (line 46) | func (g *GoHttpServer) Init(init *EngineInit) {
method Start (line 77) | func (g *GoHttpServer) Start() {
method Handle (line 100) | func (g *GoHttpServer) Handle(w http.ResponseWriter, r *http.Request) {
method handleAppMux (line 109) | func (g *GoHttpServer) handleAppMux(w http.ResponseWriter, r *http.Req...
method handleMux (line 134) | func (g *GoHttpServer) handleMux(w http.ResponseWriter, r *http.Reques...
method Name (line 199) | func (g *GoHttpServer) Name() string {
method Stats (line 204) | func (g *GoHttpServer) Stats() map[string]interface{} {
method Engine (line 212) | func (g *GoHttpServer) Engine() interface{} {
method Event (line 217) | func (g *GoHttpServer) Event(event Event, args interface{}) (r EventRe...
function HttpClientIP (line 171) | func HttpClientIP(r *http.Request) string {
constant GO_NATIVE_SERVER_ENGINE (line 196) | GO_NATIVE_SERVER_ENGINE = "go"
type GoContext (line 238) | type GoContext struct
method GetRequest (line 303) | func (c *GoContext) GetRequest() ServerRequest {
method GetResponse (line 308) | func (c *GoContext) GetResponse() ServerResponse {
method Destroy (line 316) | func (c *GoContext) Destroy() {
type GoRequest (line 245) | type GoRequest struct
method Get (line 326) | func (r *GoRequest) Get(key int) (value interface{}, err error) {
method Set (line 360) | func (r *GoRequest) Set(key int, value interface{}) bool {
method GetForm (line 365) | func (r *GoRequest) GetForm() (url.Values, error) {
method GetMultipartForm (line 377) | func (r *GoRequest) GetMultipartForm() (ServerMultipartForm, error) {
method GetHeader (line 390) | func (r *GoRequest) GetHeader() ServerHeader {
method GetRaw (line 395) | func (r *GoRequest) GetRaw() interface{} {
method SetRequest (line 400) | func (r *GoRequest) SetRequest(req *http.Request) {
method Destroy (line 407) | func (r *GoRequest) Destroy() {
type GoResponse (line 256) | type GoResponse struct
method Get (line 416) | func (r *GoResponse) Get(key int) (value interface{}, err error) {
method Set (line 431) | func (r *GoResponse) Set(key int, value interface{}) (set bool) {
method Header (line 444) | func (r *GoResponse) Header() ServerHeader {
method GetRaw (line 449) | func (r *GoResponse) GetRaw() interface{} {
method SetWriter (line 454) | func (r *GoResponse) SetWriter(writer io.Writer) {
method WriteStream (line 459) | func (r *GoResponse) WriteStream(name string, contentlen int64, modtim...
method Destroy (line 503) | func (r *GoResponse) Destroy() {
method SetResponse (line 513) | func (r *GoResponse) SetResponse(w http.ResponseWriter) {
type GoMultipartForm (line 265) | type GoMultipartForm struct
method GetFiles (line 601) | func (f *GoMultipartForm) GetFiles() map[string][]*multipart.FileHeader {
method GetValues (line 606) | func (f *GoMultipartForm) GetValues() url.Values {
method RemoveAll (line 611) | func (f *GoMultipartForm) RemoveAll() error {
type GoHeader (line 270) | type GoHeader struct
method SetCookie (line 521) | func (r *GoHeader) SetCookie(cookie string) {
method GetCookie (line 528) | func (r *GoHeader) GetCookie(key string) (value ServerCookie, err erro...
method Set (line 539) | func (r *GoHeader) Set(key string, value string) {
method Add (line 546) | func (r *GoHeader) Add(key string, value string) {
method Del (line 553) | func (r *GoHeader) Del(key string) {
method Get (line 560) | func (r *GoHeader) Get(key string) (value []string) {
method GetKeys (line 575) | func (r *GoHeader) GetKeys() (value []string) {
method SetStatus (line 589) | func (r *GoHeader) SetStatus(statusCode int) {
type GoWebSocket (line 276) | type GoWebSocket struct
method MessageSendJSON (line 618) | func (g *GoWebSocket) MessageSendJSON(v interface{}) error {
method MessageReceiveJSON (line 625) | func (g *GoWebSocket) MessageReceiveJSON(v interface{}) error {
method MessageSend (line 632) | func (g *GoWebSocket) MessageSend(v interface{}) error {
method MessageReceive (line 639) | func (g *GoWebSocket) MessageReceive(v interface{}) error {
type GoCookie (line 282) | type GoCookie
method GetValue (line 596) | func (r GoCookie) GetValue() string {
function NewGoContext (line 286) | func NewGoContext(instance *GoHttpServer) *GoContext {
FILE: server_test.go
function BenchmarkServeAction (line 31) | func BenchmarkServeAction(b *testing.B) {
function BenchmarkServeJson (line 35) | func BenchmarkServeJson(b *testing.B) {
function BenchmarkServePlaintext (line 39) | func BenchmarkServePlaintext(b *testing.B) {
function BenchmarkServeStatic (line 45) | func BenchmarkServeStatic(b *testing.B) {
function benchmarkRequest (line 49) | func benchmarkRequest(b *testing.B, req *http.Request) {
function TestFakeServer (line 59) | func TestFakeServer(t *testing.T) {
function getFileSize (line 96) | func getFileSize(t *testing.T, name string) int64 {
function TestOnAppStart (line 106) | func TestOnAppStart(t *testing.T) {
function TestOnAppStop (line 123) | func TestOnAppStop(t *testing.T) {
FILE: session/init.go
function InitSession (line 8) | func InitSession(coreLogger logger.MultiLogger) {
FILE: session/session.go
constant SessionIDKey (line 20) | SessionIDKey = "_ID"
constant TimestampKey (line 22) | TimestampKey = "_TS"
constant SessionValueName (line 25) | SessionValueName = "session"
constant SessionObjectKeyName (line 28) | SessionObjectKeyName = "_object_"
constant SessionMapKeyName (line 30) | SessionMapKeyName = "_map_"
constant SessionCookieSuffix (line 32) | SessionCookieSuffix = "_SESSION"
type Session (line 39) | type Session
method ID (line 47) | func (s Session) ID() string {
method GetExpiration (line 61) | func (s Session) GetExpiration(expireAfterDuration time.Duration) time...
method SetNoExpiration (line 70) | func (s Session) SetNoExpiration() {
method SetDefaultExpiration (line 75) | func (s Session) SetDefaultExpiration() {
method SessionTimeoutExpiredOrMissing (line 82) | func (s Session) SessionTimeoutExpiredOrMissing() bool {
method Get (line 98) | func (s Session) Get(key string) (newValue interface{}, err error) {
method GetInto (line 108) | func (s Session) GetInto(key string, target interface{}, force bool) (...
method GetDefault (line 146) | func (s Session) GetDefault(key string, value interface{}, defaultValu...
method GetProperty (line 155) | func (s Session) GetProperty(key string, value interface{}) (interface...
method Set (line 187) | func (s Session) Set(key string, value interface{}) error {
method Del (line 198) | func (s Session) Del(key string) {
method getSessionJsonMap (line 205) | func (s Session) getSessionJsonMap() map[string]string {
method Serialize (line 222) | func (s Session) Serialize() map[string]string {
method Load (line 256) | func (s Session) Load(data map[string]string) {
method Empty (line 272) | func (s Session) Empty() bool {
method reflectValue (line 283) | func (s *Session) reflectValue(obj interface{}) reflect.Value {
method getNestedProperty (line 296) | func (s Session) getNestedProperty(keys []string, newValue interface{}...
method sessionDataFromMap (line 308) | func (s Session) sessionDataFromMap(key string) (result interface{}, e...
method sessionDataFromObject (line 333) | func (s Session) sessionDataFromObject(key string, newValue interface{...
method convertSessionData (line 343) | func (s Session) convertSessionData(key string, target interface{}) (r...
function NewSession (line 41) | func NewSession() Session {
FILE: session/session_cookie_test.go
function TestCookieRestore (line 17) | func TestCookieRestore(t *testing.T) {
function TestCookieSessionExpire (line 38) | func TestCookieSessionExpire(t *testing.T) {
FILE: session/session_test.go
function TestSessionString (line 17) | func TestSessionString(t *testing.T) {
function TestSessionStruct (line 25) | func TestSessionStruct(t *testing.T) {
function setSharedDataTest (line 38) | func setSharedDataTest(s session.Session) {
function testSharedData (line 57) | func testSharedData(s, s1 session.Session, t *testing.T, a *assert.Asser...
FILE: session_adapter_cookie.go
type SessionCookieEngine (line 16) | type SessionCookieEngine struct
method Decode (line 52) | func (cse *SessionCookieEngine) Decode(c *Controller) {
method Encode (line 65) | func (cse *SessionCookieEngine) Encode(c *Controller) {
method DecodeCookie (line 70) | func (cse *SessionCookieEngine) DecodeCookie(cookie ServerCookie, s se...
method GetCookie (line 104) | func (cse *SessionCookieEngine) GetCookie(s session.Session) *http.Coo...
function init (line 25) | func init() {
function NewSessionCookieEngine (line 30) | func NewSessionCookieEngine() *SessionCookieEngine {
function initCookieEngine (line 36) | func initCookieEngine() SessionEngine {
FILE: session_engine.go
type SessionEngine (line 5) | type SessionEngine interface
function init (line 17) | func init() {
function RegisterSessionEngine (line 21) | func RegisterSessionEngine(f func() SessionEngine, name string) {
function initSessionEngine (line 26) | func initSessionEngine() {
FILE: session_filter.go
function SessionFilter (line 9) | func SessionFilter(c *Controller, fc []Filter) {
FILE: template.go
type TemplateLoader (line 27) | type TemplateLoader struct
method WatchDir (line 83) | func (loader *TemplateLoader) WatchDir(info os.FileInfo) bool {
method WatchFile (line 90) | func (loader *TemplateLoader) WatchFile(basename string) bool {
method Template (line 96) | func (loader *TemplateLoader) Template(name string) (tmpl Template, er...
method TemplateLang (line 101) | func (loader *TemplateLoader) TemplateLang(name, lang string) (tmpl Te...
method Refresh (line 109) | func (loader *TemplateLoader) Refresh() (err *Error) {
type Template (line 38) | type Template interface
function TemplateOutputArgs (line 56) | func TemplateOutputArgs(templatePath string, args map[string]interface{}...
function NewTemplateLoader (line 74) | func NewTemplateLoader(paths []string) *TemplateLoader {
type templateRuntime (line 244) | type templateRuntime struct
method findAndAddTemplate (line 262) | func (runtimeLoader *templateRuntime) findAndAddTemplate(path, fullSrc...
method loadIntoEngine (line 322) | func (runtimeLoader *templateRuntime) loadIntoEngine(engine TemplateEn...
method TemplateLang (line 379) | func (runtimeLoader *templateRuntime) TemplateLang(name, lang string) ...
method templateLoad (line 398) | func (runtimeLoader *templateRuntime) templateLoad(name, lang string) ...
function ParseTemplateError (line 352) | func ParseTemplateError(err error) (templateName string, line int, descr...
method Location (line 462) | func (i *TemplateView) Location() string {
method Content (line 466) | func (i *TemplateView) Content() (content []string) {
function NewBaseTemplate (line 479) | func NewBaseTemplate(templateName, filePath, basePath string, fileBytes ...
FILE: template_adapter_go.go
constant GO_TEMPLATE (line 10) | GO_TEMPLATE = "go"
function init (line 13) | func init() {
type GoTemplate (line 42) | type GoTemplate struct
method Render (line 49) | func (gotmpl GoTemplate) Render(wr io.Writer, arg interface{}) error {
type GoEngine (line 54) | type GoEngine struct
method ConvertPath (line 68) | func (i *GoEngine) ConvertPath(path string) string {
method Handles (line 76) | func (i *GoEngine) Handles(templateView *TemplateView) bool {
method ParseAndAdd (line 81) | func (engine *GoEngine) ParseAndAdd(baseTemplate *TemplateView) error {
method Lookup (line 107) | func (engine *GoEngine) Lookup(templateName string) Template {
method Name (line 116) | func (engine *GoEngine) Name() string {
method Event (line 121) | func (engine *GoEngine) Event(action Event, i interface{}) {
FILE: template_engine.go
type TemplateEngine (line 12) | type TemplateEngine interface
type TemplateView (line 30) | type TemplateView struct
function RegisterTemplateLoader (line 41) | func RegisterTemplateLoader(key string, loader func(loader *TemplateLoad...
method CreateTemplateEngine (line 52) | func (loader *TemplateLoader) CreateTemplateEngine(templateEngineName st...
method initializeEngines (line 71) | func (loader *TemplateLoader) initializeEngines(runtimeLoader *templateR...
function EngineHandles (line 94) | func EngineHandles(engine TemplateEngine, templateView *TemplateView) bo...
FILE: template_functions.go
function ReverseURL (line 215) | func ReverseURL(args ...interface{}) (template.URL, error) {
function Slug (line 259) | func Slug(text string) string {
function TimeAgo (line 270) | func TimeAgo(args ...interface{}) string {
FILE: testing/testsuite.go
type TestSuite (line 29) | type TestSuite struct
method NewTestRequest (line 74) | func (t *TestSuite) NewTestRequest(req *http.Request) *TestRequest {
method Host (line 83) | func (t *TestSuite) Host() string {
method BaseUrl (line 92) | func (t *TestSuite) BaseUrl() string {
method WebSocketUrl (line 100) | func (t *TestSuite) WebSocketUrl() string {
method Get (line 106) | func (t *TestSuite) Get(path string) {
method GetCustom (line 111) | func (t *TestSuite) GetCustom(uri string) *TestRequest {
method Delete (line 121) | func (t *TestSuite) Delete(path string) {
method DeleteCustom (line 127) | func (t *TestSuite) DeleteCustom(uri string) *TestRequest {
method Put (line 137) | func (t *TestSuite) Put(path string, contentType string, reader io.Rea...
method PutCustom (line 143) | func (t *TestSuite) PutCustom(uri string, contentType string, reader i...
method PutForm (line 154) | func (t *TestSuite) PutForm(path string, data url.Values) {
method PutFormCustom (line 160) | func (t *TestSuite) PutFormCustom(uri string, data url.Values) *TestRe...
method Patch (line 167) | func (t *TestSuite) Patch(path string, contentType string, reader io.R...
method PatchCustom (line 173) | func (t *TestSuite) PatchCustom(uri string, contentType string, reader...
method Post (line 184) | func (t *TestSuite) Post(path string, contentType string, reader io.Re...
method PostCustom (line 190) | func (t *TestSuite) PostCustom(uri string, contentType string, reader ...
method PostForm (line 201) | func (t *TestSuite) PostForm(path string, data url.Values) {
method PostFormCustom (line 207) | func (t *TestSuite) PostFormCustom(uri string, data url.Values) *TestR...
method PostFile (line 213) | func (t *TestSuite) PostFile(path string, params url.Values, filePaths...
method PostFileCustom (line 219) | func (t *TestSuite) PostFileCustom(uri string, params url.Values, file...
method WebSocket (line 290) | func (t *TestSuite) WebSocket(path string) *websocket.Conn {
method AssertOk (line 300) | func (t *TestSuite) AssertOk() {
method AssertNotFound (line 304) | func (t *TestSuite) AssertNotFound() {
method AssertStatus (line 308) | func (t *TestSuite) AssertStatus(status int) {
method AssertContentType (line 314) | func (t *TestSuite) AssertContentType(contentType string) {
method AssertHeader (line 318) | func (t *TestSuite) AssertHeader(name, value string) {
method AssertEqual (line 325) | func (t *TestSuite) AssertEqual(expected, actual interface{}) {
method AssertNotEqual (line 331) | func (t *TestSuite) AssertNotEqual(expected, actual interface{}) {
method Assert (line 337) | func (t *TestSuite) Assert(exp bool) {
method Assertf (line 341) | func (t *TestSuite) Assertf(exp bool, formatStr string, args ...interf...
method AssertContains (line 348) | func (t *TestSuite) AssertContains(s string) {
method AssertNotContains (line 355) | func (t *TestSuite) AssertNotContains(s string) {
method AssertContainsRegex (line 362) | func (t *TestSuite) AssertContainsRegex(regex string) {
type TestRequest (line 37) | type TestRequest struct
method Send (line 244) | func (r *TestRequest) Send() {
method MakeRequest (line 264) | func (r *TestRequest) MakeRequest() {
function NewTestSuite (line 47) | func NewTestSuite() TestSuite {
function NewTestSuiteEngine (line 52) | func NewTestSuiteEngine(engine revel.SessionEngine) TestSuite {
function createFormFile (line 370) | func createFormFile(writer *multipart.Writer, fieldname, filename string) {
function escapeQuotes (line 408) | func escapeQuotes(s string) string {
FILE: testing/testsuite_test.go
function TestMisc (line 23) | func TestMisc(t *testing.T) {
function TestGet (line 50) | func TestGet(t *testing.T) {
function TestGetNotFound (line 62) | func TestGetNotFound(t *testing.T) {
function TestGetCustom (line 75) | func TestGetCustom(t *testing.T) {
function TestDelete (line 91) | func TestDelete(t *testing.T) {
function TestPut (line 101) | func TestPut(t *testing.T) {
function TestPutForm (line 114) | func TestPutForm(t *testing.T) {
function TestPatch (line 128) | func TestPatch(t *testing.T) {
function TestPost (line 141) | func TestPost(t *testing.T) {
function TestPostForm (line 155) | func TestPostForm(t *testing.T) {
function TestPostFileUpload (line 170) | func TestPostFileUpload(t *testing.T) {
function createNewTestSuite (line 197) | func createNewTestSuite(t *testing.T) *TestSuite {
function testHandle (line 207) | func testHandle(w http.ResponseWriter, r *http.Request) {
function handleFileUpload (line 264) | func handleFileUpload(w http.ResponseWriter, r *http.Request) {
function createTestServer (line 288) | func createTestServer(fn func(w http.ResponseWriter, r *http.Request)) *...
function init (line 294) | func init() {
FILE: util.go
constant DefaultFileContentType (line 25) | DefaultFileContentType = "application/octet-stream"
type ExecutableTemplate (line 37) | type ExecutableTemplate interface
function ExecuteTemplate (line 42) | func ExecuteTemplate(tmpl ExecutableTemplate, data interface{}) string {
function MustReadLines (line 51) | func MustReadLines(filename string) []string {
function ReadLines (line 60) | func ReadLines(filename string) ([]string, error) {
function ContainsString (line 68) | func ContainsString(list []string, target string) bool {
function FindMethod (line 78) | func FindMethod(recvType reflect.Type, funcVal reflect.Value) *reflect.M...
function ParseKeyValueCookie (line 91) | func ParseKeyValueCookie(val string, cb func(key, val string)) {
function LoadMimeConfig (line 101) | func LoadMimeConfig() {
function ContentTypeByFilename (line 112) | func ContentTypeByFilename(filename string) string {
function DirExists (line 132) | func DirExists(filename string) bool {
function FirstNonEmpty (line 137) | func FirstNonEmpty(strs ...string) string {
function Equal (line 152) | func Equal(a, b interface{}) bool {
function ClientIP (line 194) | func ClientIP(r *Request) string {
function Walk (line 220) | func Walk(root string, walkFn filepath.WalkFunc) error {
function fsWalk (line 224) | func fsWalk(fname string, linkName string, walkFn filepath.WalkFunc) err...
function init (line 263) | func init() {
FILE: util_test.go
function TestContentTypeByFilename (line 13) | func TestContentTypeByFilename(t *testing.T) {
function TestEqual (line 36) | func TestEqual(t *testing.T) {
FILE: utils/simplestack.go
type SimpleLockStack (line 9) | type SimpleLockStack struct
method Pop (line 49) | func (s *SimpleLockStack) Pop() (value interface{}) {
method Push (line 69) | func (s *SimpleLockStack) Push(value interface{}) {
method Len (line 95) | func (s *SimpleLockStack) Len() int {
method Capacity (line 99) | func (s *SimpleLockStack) Capacity() int {
method Active (line 103) | func (s *SimpleLockStack) Active() int {
method String (line 107) | func (s *SimpleLockStack) String() string {
type SimpleLockStackElement (line 18) | type SimpleLockStackElement struct
type ObjectDestroy (line 23) | type ObjectDestroy interface
function NewStackLock (line 28) | func NewStackLock(startsize, maxsize int, creator func() interface{}) *S...
FILE: utils/simplestack_test.go
type SimpleStackTest (line 7) | type SimpleStackTest struct
function TestUnique (line 11) | func TestUnique(b *testing.T) {
function TestLimits (line 64) | func TestLimits(b *testing.T) {
function isDifferent (line 84) | func isDifferent(values ...*SimpleStackTest) bool {
function BenchmarkCreateWrite (line 96) | func BenchmarkCreateWrite(b *testing.B) {
function BenchmarkAllocWrite (line 103) | func BenchmarkAllocWrite(b *testing.B) {
function BenchmarkCreate (line 110) | func BenchmarkCreate(b *testing.B) {
function BenchmarkParrallel (line 114) | func BenchmarkParrallel(b *testing.B) {
FILE: validation.go
type ValidationError (line 16) | type ValidationError struct
method String (line 21) | func (e *ValidationError) String() string {
type Validation (line 29) | type Validation struct
method Keep (line 42) | func (v *Validation) Keep() {
method Clear (line 47) | func (v *Validation) Clear() {
method HasErrors (line 52) | func (v *Validation) HasErrors() bool {
method ErrorMap (line 59) | func (v *Validation) ErrorMap() map[string]*ValidationError {
method Error (line 70) | func (v *Validation) Error(message string, args ...interface{}) *Valid...
method ErrorKey (line 77) | func (v *Validation) ErrorKey(message string, args ...interface{}) *Va...
method ValidationResult (line 84) | func (v *Validation) ValidationResult(ok bool) *ValidationResult {
method Required (line 140) | func (v *Validation) Required(obj interface{}) *ValidationResult {
method Min (line 144) | func (v *Validation) Min(n int, min int) *ValidationResult {
method MinFloat (line 148) | func (v *Validation) MinFloat(n float64, min float64) *ValidationResult {
method Max (line 152) | func (v *Validation) Max(n int, max int) *ValidationResult {
method MaxFloat (line 156) | func (v *Validation) MaxFloat(n float64, max float64) *ValidationResult {
method Range (line 160) | func (v *Validation) Range(n, min, max int) *ValidationResult {
method RangeFloat (line 164) | func (v *Validation) RangeFloat(n, min, max float64) *ValidationResult {
method MinSize (line 168) | func (v *Validation) MinSize(obj interface{}, min int) *ValidationResu...
method MaxSize (line 172) | func (v *Validation) MaxSize(obj interface{}, max int) *ValidationResu...
method Length (line 176) | func (v *Validation) Length(obj interface{}, n int) *ValidationResult {
method Match (line 180) | func (v *Validation) Match(str string, regex *regexp.Regexp) *Validati...
method Email (line 184) | func (v *Validation) Email(str string) *ValidationResult {
method IPAddr (line 188) | func (v *Validation) IPAddr(str string, cktype ...int) *ValidationResu...
method MacAddr (line 192) | func (v *Validation) MacAddr(str string) *ValidationResult {
method Domain (line 196) | func (v *Validation) Domain(str string) *ValidationResult {
method URL (line 200) | func (v *Validation) URL(str string) *ValidationResult {
method PureText (line 204) | func (v *Validation) PureText(str string, m int) *ValidationResult {
method FilePath (line 208) | func (v *Validation) FilePath(str string, m int) *ValidationResult {
method apply (line 212) | func (v *Validation) apply(chk Validator, obj interface{}) *Validation...
method Check (line 244) | func (v *Validation) Check(obj interface{}, checks ...Validator) *Vali...
type ValidationResult (line 94) | type ValidationResult struct
method Key (line 102) | func (r *ValidationResult) Key(key string) *ValidationResult {
method Message (line 111) | func (r *ValidationResult) Message(message string, args ...interface{}...
method MessageKey (line 124) | func (r *ValidationResult) MessageKey(message string, args ...interfac...
function ValidationFilter (line 256) | func ValidationFilter(c *Controller, fc []Filter) {
function restoreValidationErrors (line 315) | func restoreValidationErrors(req *Request) ([]*ValidationError, error) {
FILE: validation_test.go
function getRecordedCookie (line 15) | func getRecordedCookie(recorder *httptest.ResponseRecorder, name string)...
function validationTester (line 26) | func validationTester(req *Request, fn func(c *Controller)) *httptest.Re...
function TestValidationWithError (line 38) | func TestValidationWithError(t *testing.T) {
function TestValidationNoKeep (line 55) | func TestValidationNoKeep(t *testing.T) {
function TestValidationNoKeepCookiePreviouslySet (line 69) | func TestValidationNoKeepCookiePreviouslySet(t *testing.T) {
function TestValidateMessageKey (line 85) | func TestValidateMessageKey(t *testing.T) {
FILE: validators.go
type Validator (line 20) | type Validator interface
type Required (line 25) | type Required struct
method IsSatisfied (line 31) | func (r Required) IsSatisfied(obj interface{}) bool {
method DefaultMessage (line 46) | func (r Required) DefaultMessage() string {
function ValidRequired (line 27) | func ValidRequired() Required {
type Min (line 50) | type Min struct
method IsSatisfied (line 62) | func (m Min) IsSatisfied(obj interface{}) bool {
method DefaultMessage (line 84) | func (m Min) DefaultMessage() string {
function ValidMin (line 54) | func ValidMin(min int) Min {
function ValidMinFloat (line 58) | func ValidMinFloat(min float64) Min {
type Max (line 88) | type Max struct
method IsSatisfied (line 100) | func (m Max) IsSatisfied(obj interface{}) bool {
method DefaultMessage (line 122) | func (m Max) DefaultMessage() string {
function ValidMax (line 92) | func ValidMax(max int) Max {
function ValidMaxFloat (line 96) | func ValidMaxFloat(max float64) Max {
type Range (line 127) | type Range struct
method IsSatisfied (line 140) | func (r Range) IsSatisfied(obj interface{}) bool {
method DefaultMessage (line 144) | func (r Range) DefaultMessage() string {
function ValidRange (line 132) | func ValidRange(min, max int) Range {
function ValidRangeFloat (line 136) | func ValidRangeFloat(min, max float64) Range {
type MinSize (line 149) | type MinSize struct
method IsSatisfied (line 157) | func (m MinSize) IsSatisfied(obj interface{}) bool {
method DefaultMessage (line 168) | func (m MinSize) DefaultMessage() string {
function ValidMinSize (line 153) | func ValidMinSize(min int) MinSize {
type MaxSize (line 173) | type MaxSize struct
method IsSatisfied (line 181) | func (m MaxSize) IsSatisfied(obj interface{}) bool {
method DefaultMessage (line 192) | func (m MaxSize) DefaultMessage() string {
function ValidMaxSize (line 177) | func ValidMaxSize(max int) MaxSize {
type Length (line 197) | type Length struct
method IsSatisfied (line 205) | func (s Length) IsSatisfied(obj interface{}) bool {
method DefaultMessage (line 216) | func (s Length) DefaultMessage() string {
function ValidLength (line 201) | func ValidLength(n int) Length {
type Match (line 221) | type Match struct
method IsSatisfied (line 229) | func (m Match) IsSatisfied(obj interface{}) bool {
method DefaultMessage (line 234) | func (m Match) DefaultMessage() string {
function ValidMatch (line 225) | func ValidMatch(regex *regexp.Regexp) Match {
type Email (line 240) | type Email struct
method DefaultMessage (line 248) | func (e Email) DefaultMessage() string {
function ValidEmail (line 244) | func ValidEmail() Email {
constant None (line 253) | None = 0
constant IPAny (line 254) | IPAny = 1
constant IPv4 (line 255) | IPv4 = 32
constant IPv6 (line 256) | IPv6 = 39
constant IPv4MappedIPv6 (line 257) | IPv4MappedIPv6 = 45
constant IPv4CIDR (line 258) | IPv4CIDR = IPv4 + 3
constant IPv6CIDR (line 259) | IPv6CIDR = IPv6 + 3
constant IPv4MappedIPv6CIDR (line 260) | IPv4MappedIPv6CIDR = IPv4MappedIPv6 + 3
type IPAddr (line 264) | type IPAddr struct
method IsSatisfied (line 327) | func (i IPAddr) IsSatisfied(obj interface{}) bool {
method DefaultMessage (line 355) | func (i IPAddr) DefaultMessage() string {
function ValidIPAddr (line 269) | func ValidIPAddr(cktypes ...int) IPAddr {
function isWithCIDR (line 279) | func isWithCIDR(str string, l int) bool {
function getIPType (line 294) | func getIPType(str string, l int) int {
type MacAddr (line 360) | type MacAddr struct
method IsSatisfied (line 366) | func (m MacAddr) IsSatisfied(obj interface{}) bool {
method DefaultMessage (line 376) | func (m MacAddr) DefaultMessage() string {
function ValidMacAddr (line 362) | func ValidMacAddr() MacAddr {
type Domain (line 383) | type Domain struct
method IsSatisfied (line 391) | func (d Domain) IsSatisfied(obj interface{}) bool {
method DefaultMessage (line 410) | func (d Domain) DefaultMessage() string {
function ValidDomain (line 387) | func ValidDomain() Domain {
type URL (line 416) | type URL struct
method IsSatisfied (line 424) | func (u URL) IsSatisfied(obj interface{}) bool {
method DefaultMessage (line 433) | func (u URL) DefaultMessage() string {
function ValidURL (line 420) | func ValidURL() URL {
constant NORMAL (line 442) | NORMAL = 0
constant STRICT (line 443) | STRICT = 4
type PureText (line 447) | type PureText struct
method IsSatisfied (line 553) | func (p PureText) IsSatisfied(obj interface{}) bool {
method DefaultMessage (line 568) | func (p PureText) DefaultMessage() string {
function ValidPureText (line 451) | func ValidPureText(m int) PureText {
function isPureTextStrict (line 458) | func isPureTextStrict(str string) (bool, error) {
function isPureTextNormal (line 529) | func isPureTextNormal(str string) (bool, error) {
constant ONLY_FILENAME (line 573) | ONLY_FILENAME = 0
constant ALLOW_RELATIVE_PATH (line 574) | ALLOW_RELATIVE_PATH = 1
constant regexDenyFileNameCharList (line 578) | regexDenyFileNameCharList = `[\x00-\x1f|\x21-\x2c|\x3b-\x40|\x5b-\x5e|\x...
constant regexDenyFileName (line 579) | regexDenyFileName = `|\x2e\x2e\x2f+`
type FilePath (line 588) | type FilePath struct
method IsSatisfied (line 599) | func (f FilePath) IsSatisfied(obj interface{}) bool {
method DefaultMessage (line 619) | func (f FilePath) DefaultMessage() string {
function ValidFilePath (line 592) | func ValidFilePath(m int) FilePath {
FILE: validators_test.go
constant errorsMessage (line 20) | errorsMessage = "validation for %s should not be satisfied with %s\n"
constant noErrorsMessage (line 21) | noErrorsMessage = "validation for %s should be satisfied with %s\n"
type Expect (line 24) | type Expect struct
function performTests (line 30) | func performTests(validator revel.Validator, tests []Expect, t *testing....
function TestRequired (line 42) | func TestRequired(t *testing.T) {
function TestMin (line 64) | func TestMin(t *testing.T) {
function TestMax (line 76) | func TestMax(t *testing.T) {
function TestRange (line 88) | func TestRange(t *testing.T) {
function TestMinSize (line 163) | func TestMinSize(t *testing.T) {
function TestMaxSize (line 181) | func TestMaxSize(t *testing.T) {
function TestLength (line 198) | func TestLength(t *testing.T) {
function TestMatch (line 214) | func TestMatch(t *testing.T) {
function TestEmail (line 226) | func TestEmail(t *testing.T) {
function runIPAddrTestfunc (line 293) | func runIPAddrTestfunc(t *testing.T, test_type int, ipaddr_list map[stri...
function TestIPAddr (line 305) | func TestIPAddr(t *testing.T) {
function TestMacAddr (line 384) | func TestMacAddr(t *testing.T) {
function TestDomain (line 410) | func TestDomain(t *testing.T) {
function TestURL (line 475) | func TestURL(t *testing.T) {
function TestPureTextNormal (line 498) | func TestPureTextNormal(t *testing.T) {
function TestPureTextStrict (line 540) | func TestPureTextStrict(t *testing.T) {
function TestFilePathOnlyFilePath (line 582) | func TestFilePathOnlyFilePath(t *testing.T) {
function TestFilePathAllowRelativePath (line 611) | func TestFilePathAllowRelativePath(t *testing.T) {
FILE: version.go
constant Version (line 9) | Version = "1.1.0"
constant BuildDate (line 12) | BuildDate = "2022-04-11"
constant MinimumGoVersion (line 15) | MinimumGoVersion = ">= go1.17"
FILE: watcher.go
type Listener (line 18) | type Listener interface
type DiscerningListener (line 25) | type DiscerningListener interface
type Watcher (line 33) | type Watcher struct
method Listen (line 59) | func (w *Watcher) Listen(listener Listener, roots ...string) {
method NotifyWhenUpdated (line 141) | func (w *Watcher) NotifyWhenUpdated(listener Listener, watcher *fsnoti...
method Notify (line 170) | func (w *Watcher) Notify() *Error {
method notifyInProcess (line 217) | func (w *Watcher) notifyInProcess(listener Listener) (err *Error) {
method eagerRebuildEnabled (line 269) | func (w *Watcher) eagerRebuildEnabled() bool {
method rebuildRequired (line 275) | func (w *Watcher) rebuildRequired(ev fsnotify.Event, listener Listener...
function NewWatcher (line 47) | func NewWatcher() *Watcher {
Condensed preview — 132 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (653K chars).
[
{
"path": ".codebeatsettings",
"chars": 266,
"preview": "{\n \"GOLANG\": {\n \"ABC\":[15, 25, 50, 70],\n \"BLOCK_NESTING\":[5, 6, 7, 8],\n \"CYCLO\":[20, 30, 45, 60],\n \"TOO_MANY_IVAR"
},
{
"path": ".gitignore",
"chars": 70,
"preview": "tmp/\nroutes/\ntest-results/\nrevel/revel\n\n# editor\n*.swp\n\n.idea/\n*.iml\n\n"
},
{
"path": ".travis.yml",
"chars": 1605,
"preview": "language: go\n\ngo:\n - \"1.13.x\"\n - \"1.14.x\"\n - \"1.15.x\"\n - \"tip\"\n\nos:\n - linux\n - osx\n - windows\n\nsudo: false\n\nbran"
},
{
"path": "AUTHORS",
"chars": 43,
"preview": "# TODO Revel Framework Authors Information\n"
},
{
"path": "CHANGELOG.md",
"chars": 44340,
"preview": "# CHANGELOG\n\n\n## v1.1.0\n\n[[revel/revel](https://github.com/revel/revel)]\n\n* bc0e27f Merge pull request #1552 from revel/"
},
{
"path": "CONTRIBUTING.md",
"chars": 6963,
"preview": "## Contributing to Revel\n\nThis describes how developers may contribute to Revel.\n\n## Mission\n\nRevel's mission is to prov"
},
{
"path": "LICENSE",
"chars": 1100,
"preview": "The MIT License (MIT)\n\nCopyright (C) 2012-2018 The Revel Framework Authors.\n\nPermission is hereby granted, free of charg"
},
{
"path": "README.md",
"chars": 2223,
"preview": "# Revel Framework\n\n[](https://travis-ci.org/r"
},
{
"path": "before_after_filter.go",
"chars": 1540,
"preview": "package revel\n\nimport (\n\t\"reflect\"\n)\n\n// Autocalls any defined before and after methods on the target controller\n// If e"
},
{
"path": "before_after_filter_test.go",
"chars": 1141,
"preview": "// Copyright (c) 2019 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is gove"
},
{
"path": "binder.go",
"chars": 16652,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "binder_test.go",
"chars": 13102,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "cache/cache.go",
"chars": 5520,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "cache/cache_test.go",
"chars": 6721,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "cache/init.go",
"chars": 1998,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "cache/inmemory.go",
"chars": 3712,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "cache/inmemory_test.go",
"chars": 1038,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "cache/memcached.go",
"chars": 3189,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "cache/memcached_test.go",
"chars": 1384,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "cache/redis.go",
"chars": 6887,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "cache/redis_test.go",
"chars": 1535,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "cache/serialization.go",
"chars": 2352,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "cache/serialization_test.go",
"chars": 2048,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "compress.go",
"chars": 10015,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "compress_test.go",
"chars": 1668,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "conf/mime-types.conf",
"chars": 13944,
"preview": "3dm=x-world/x-3dmf\n3dmf=x-world/x-3dmf\n7z=application/x-7z-compressed\na=application/octet-stream\naab=application/x-autho"
},
{
"path": "controller.go",
"chars": 17503,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "controller_type.go",
"chars": 5658,
"preview": "package revel\n\nimport (\n\t\"reflect\"\n\t\"strings\"\n)\n\n// Controller registry and types.\ntype ControllerType struct {\n\tNamespa"
},
{
"path": "errors.go",
"chars": 4850,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "event.go",
"chars": 2054,
"preview": "package revel\n\ntype (\n\t// The event type.\n\tEvent int\n\t// The event response.\n\tEventResponse int\n\t// The handler signatur"
},
{
"path": "event_test.go",
"chars": 746,
"preview": "package revel_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/revel/revel\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\n// Test that"
},
{
"path": "fakeapp_test.go",
"chars": 3653,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "field.go",
"chars": 2530,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "filter.go",
"chars": 1364,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "filterconfig.go",
"chars": 7028,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "filterconfig_test.go",
"chars": 3596,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "flash.go",
"chars": 2308,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "go.mod",
"chars": 1160,
"preview": "module github.com/revel/revel\n\ngo 1.17\n\nrequire (\n\tgithub.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d\n\tgi"
},
{
"path": "go.sum",
"chars": 5325,
"preview": "github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=\ngithub.com/BurntSushi/toml v1.1.0/go.m"
},
{
"path": "http.go",
"chars": 13388,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "i18n.go",
"chars": 8016,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "i18n_test.go",
"chars": 9880,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "intercept.go",
"chars": 7028,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "intercept_test.go",
"chars": 2821,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "invoker.go",
"chars": 1468,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "invoker_test.go",
"chars": 3498,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "libs.go",
"chars": 2371,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "libs_test.go",
"chars": 860,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "logger/composite_multihandler.go",
"chars": 4548,
"preview": "package logger\n\nimport (\n\t\"io\"\n\t\"os\"\n\n\t\"github.com/mattn/go-colorable\"\n\t\"gopkg.in/natefinch/lumberjack.v2\"\n)\n\ntype Compo"
},
{
"path": "logger/doc.go",
"chars": 570,
"preview": "/*\n Package logger contains filters and handles for the logging utilities in Revel.\n These facilities all currently us"
},
{
"path": "logger/handlers.go",
"chars": 5557,
"preview": "package logger\n\nimport (\n\t\"fmt\"\n\t\"io\"\n)\n\ntype LevelFilterHandler struct {\n\tLevel LogLevel\n\th LogHandler\n}\n\n// Filter"
},
{
"path": "logger/init.go",
"chars": 6443,
"preview": "package logger\n\n// Get all handlers based on the Config (if available).\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\""
},
{
"path": "logger/init_test.go",
"chars": 8093,
"preview": "// Copyright (c) 2012-2018 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "logger/log_function_map.go",
"chars": 1293,
"preview": "package logger\n\nimport (\n\t\"os\"\n)\n\n// The log function map can be added to, so that you can specify your own logging mech"
},
{
"path": "logger/logger.go",
"chars": 5867,
"preview": "package logger\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/revel/config\"\n)\n\n// The LogHandler defines the interface to handle"
},
{
"path": "logger/revel_logger.go",
"chars": 3695,
"preview": "package logger\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/revel/log15\"\n)\n\n// This type implements the MultiLogger.\ntype"
},
{
"path": "logger/terminal_format.go",
"chars": 5534,
"preview": "package logger\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n)\n\nconst (\n\ttimeFormat "
},
{
"path": "logger/utils.go",
"chars": 3196,
"preview": "package logger\n\nimport (\n\t\"log\"\n\n\t\"github.com/revel/log15\"\n\t\"gopkg.in/stack.v0\"\n)\n\n// Utility package to make existing l"
},
{
"path": "logger/wrap_handlers.go",
"chars": 2600,
"preview": "package logger\n\n// FuncHandler returns a Handler that logs records with the given\n// function.\nimport (\n\t\"fmt\"\n\t\"reflect"
},
{
"path": "logger.go",
"chars": 1938,
"preview": "package revel\n\nimport (\n\t\"github.com/revel/revel/logger\"\n)\n\n// Logger.\nvar (\n\t// The root log is what all other logs are"
},
{
"path": "model/revel_container.go",
"chars": 172,
"preview": "package model\n\n// The single instance object that has the config populated to it.\ntype (\n\tRevelContainer struct {\n\t\tCont"
},
{
"path": "model/revel_controller.go",
"chars": 693,
"preview": "package model\n\nimport \"github.com/revel/revel/utils\"\n\ntype RevelController struct {\n\tReuse bool "
},
{
"path": "model/revel_paths.go",
"chars": 345,
"preview": "package model\n\ntype RevelPaths struct {\n\tImport string\n\tSource string\n\tBase string\n\tCode []string "
},
{
"path": "model/revel_unit.go",
"chars": 609,
"preview": "package model\n\nconst (\n\tAPP RevelUnitType = 1 // App always overrides all\n\tMODULE RevelUnitType = 2 // Module is next"
},
{
"path": "module.go",
"chars": 6910,
"preview": "package revel\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/go-stack/stack\"\n\t\"github.com/revel/reve"
},
{
"path": "namespace.go",
"chars": 1238,
"preview": "package revel\n\nimport (\n\t\"bytes\"\n\t\"regexp\"\n)\n\n// Module matching template syntax allows for modules to replace this text"
},
{
"path": "panic.go",
"chars": 1444,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "params.go",
"chars": 5151,
"preview": "// Copyright (c) 2012-2017 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "params_test.go",
"chars": 4858,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "results.go",
"chars": 14777,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "results_test.go",
"chars": 2062,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "revel.go",
"chars": 10503,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "revel_hooks.go",
"chars": 2584,
"preview": "package revel\n\nimport (\n\t\"sort\"\n)\n\n// The list of startup hooks.\ntype (\n\t// The startup hooks structure.\n\tRevelHook stru"
},
{
"path": "revel_test.go",
"chars": 251,
"preview": "package revel\n\nimport (\n\t\"net/http\"\n)\n\nfunc NewTestController(w http.ResponseWriter, r *http.Request) *Controller {\n\tcon"
},
{
"path": "router.go",
"chars": 28997,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "router_test.go",
"chars": 16380,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "server-engine.go",
"chars": 6536,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "server.go",
"chars": 5072,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "server_adapter_go.go",
"chars": 16973,
"preview": "package revel\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"mime/multipart\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"os/signal\"\n\t\"path\"\n\t\"so"
},
{
"path": "server_test.go",
"chars": 3783,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "session/init.go",
"chars": 226,
"preview": "package session\n\n// The logger for the session.\nimport \"github.com/revel/revel/logger\"\n\nvar sessionLog logger.MultiLogge"
},
{
"path": "session/session.go",
"chars": 10504,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "session/session_cookie_test.go",
"chars": 2355,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "session/session_test.go",
"chars": 1544,
"preview": "// Copyright (c) 2012-2018 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "session_adapter_cookie.go",
"chars": 4024,
"preview": "package revel\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/revel/revel/session\"\n)"
},
{
"path": "session_engine.go",
"chars": 1045,
"preview": "package revel\n\n// The session engine provides an interface to allow for storage of session data.\ntype (\n\tSessionEngine i"
},
{
"path": "session_filter.go",
"chars": 768,
"preview": "package revel\n\n// SessionFilter is a Revel Filter that retrieves and sets the session cookie.\n// Within Revel, it is ava"
},
{
"path": "template.go",
"chars": 15674,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "template_adapter_go.go",
"chars": 4033,
"preview": "package revel\n\nimport (\n\t\"html/template\"\n\t\"io\"\n\t\"log\"\n\t\"strings\"\n)\n\nconst GO_TEMPLATE = \"go\"\n\n// Called on startup, init"
},
{
"path": "template_engine.go",
"chars": 4170,
"preview": "package revel\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\ntype TemplateEngine interface "
},
{
"path": "template_functions.go",
"chars": 10266,
"preview": "package revel\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"html\"\n\t\"html/template\"\n\t\"reflect\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/x"
},
{
"path": "templates/errors/403.html",
"chars": 184,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\t<head>\n\t\t<title>Forbidden</title>\n\t</head>\n\t<body>\n\t{{with .Error}}\n\t<h1>\n\t\t{{.Title}}"
},
{
"path": "templates/errors/403.json",
"chars": 87,
"preview": "{\n \"title\": \"{{js .Error.Title}}\",\n \"description\": \"{{js .Error.Description}}\"\n}\n"
},
{
"path": "templates/errors/403.txt",
"chars": 41,
"preview": "{{.Error.Title}}\n\n{{.Error.Description}}\n"
},
{
"path": "templates/errors/403.xml",
"chars": 46,
"preview": "<forbidden>{{.Error.Description}}</forbidden>\n"
},
{
"path": "templates/errors/404-dev.html",
"chars": 995,
"preview": "<style type=\"text/css\">\n\thtml, body {\n\t\tmargin: 0;\n\t\tpadding: 0;\n\t\tfont-family: Helvetica, Arial, Sans;\n\t\tbackground: #E"
},
{
"path": "templates/errors/404.html",
"chars": 260,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\t<head>\n\t\t<title>Not found</title>\n\t</head>\n\t<body>\n\n{{if .DevMode}}\n\n{{template \"error"
},
{
"path": "templates/errors/404.json",
"chars": 87,
"preview": "{\n \"title\": \"{{js .Error.Title}}\",\n \"description\": \"{{js .Error.Description}}\"\n}\n"
},
{
"path": "templates/errors/404.txt",
"chars": 41,
"preview": "{{.Error.Title}}\n\n{{.Error.Description}}\n"
},
{
"path": "templates/errors/404.xml",
"chars": 44,
"preview": "<notfound>{{.Error.Description}}</notfound>\n"
},
{
"path": "templates/errors/405.html",
"chars": 193,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\t<head>\n\t\t<title>Method not allowed</title>\n\t</head>\n\t<body>\n\t{{with .Error}}\n\t<h1>\n\t\t{"
},
{
"path": "templates/errors/405.json",
"chars": 87,
"preview": "{\n \"title\": \"{{js .Error.Title}}\",\n \"description\": \"{{js .Error.Description}}\"\n}\n"
},
{
"path": "templates/errors/405.txt",
"chars": 41,
"preview": "{{.Error.Title}}\n\n{{.Error.Description}}\n"
},
{
"path": "templates/errors/405.xml",
"chars": 64,
"preview": "<method-not-allowed>{{.Error.Description}}</method-not-allowed>\n"
},
{
"path": "templates/errors/500-dev.html",
"chars": 2506,
"preview": "\t\t<style type=\"text/css\">\n\t\thtml, body {\n\t\t\tmargin: 0;\n\t\t\tpadding: 0;\n\t\t\tfont-family: Helvetica, Arial, Sans;\n\t\t\tbackgro"
},
{
"path": "templates/errors/500.html",
"chars": 260,
"preview": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<title>Application error</title>\n\t</head>\n\t<body>\n\t\t{{if .DevMode}}\n\t\t{{template \"error"
},
{
"path": "templates/errors/500.json",
"chars": 87,
"preview": "{\n \"title\": \"{{js .Error.Title}}\",\n \"description\": \"{{js .Error.Description}}\"\n}\n"
},
{
"path": "templates/errors/500.txt",
"chars": 276,
"preview": "{{.Error.Title}}\n{{.Error.Description}}\n\n{{if eq .RunMode \"dev\"}}\n{{with .Error}}\n{{if .Path}}\n----------\nIn {{.Path}} {"
},
{
"path": "templates/errors/500.xml",
"chars": 101,
"preview": "<error>\n\t<title>{{.Error.Title}}</title>\n\t<description>{{.Error.Description}}</description>\n</error>\n"
},
{
"path": "testdata/app/views/footer.html",
"chars": 408,
"preview": " </div>\n\n <div id=\"footer\">\n Created with the <a href=\"http://github.com/revel/revel\">Revel framework</a> and"
},
{
"path": "testdata/app/views/header.html",
"chars": 1339,
"preview": "<!DOCTYPE html>\n\n<html>\n <head>\n <title>{{.title}}</title>\n <meta http-equiv=\"Content-Type\" content=\"text/html; c"
},
{
"path": "testdata/app/views/hotels/show.html",
"chars": 663,
"preview": "{{template \"header.html\" .}}\n\n<h1>View hotel</h1>\n\n{{with .hotel}}\n<form action=\"{{url \"Hotels.Book\" .HotelID}}\">\n\n <p>"
},
{
"path": "testdata/conf/app.conf",
"chars": 777,
"preview": "# Application\napp.name=Booking example\napp.secret=secret\n\n# Server\nhttp.addr=\nhttp.port=9000\nhttp.ssl=false\nhttp.sslcert"
},
{
"path": "testdata/conf/routes",
"chars": 604,
"preview": "# Routes\n# This file defines all application routes (Higher priority routes first)\n# ~~~~\n\nmodule:testrunner\n\nGET /h"
},
{
"path": "testdata/i18n/config/test_app.conf",
"chars": 609,
"preview": "app.name={{ .AppName }}\napp.secret={{ .Secret }}\nhttp.addr=\nhttp.port=9000\ncookie.prefix=REVEL\n\ni18n.default_language=en"
},
{
"path": "testdata/i18n/messages/dutch_messages.nl",
"chars": 118,
"preview": "greeting=Hallo \ngreeting.name=Rob\ngreeting.suffix=, welkom bij Revel!\n\n[NL]\ngreeting=Goeiedag\n\n[BE]\ngreeting=Hallokes\n"
},
{
"path": "testdata/i18n/messages/english_messages.en",
"chars": 387,
"preview": "greeting=Hello \ngreeting.name=Rob\ngreeting.suffix=, welcome to Revel!\n\nfolded=Greeting is '%(greeting)s'\nfolded.argument"
},
{
"path": "testdata/i18n/messages/english_messages2.en",
"chars": 14,
"preview": "greeting2=Yo!\n"
},
{
"path": "testdata/i18n/messages/invalid_message_file_name.txt",
"chars": 0,
"preview": ""
},
{
"path": "testdata/public/js/sessvars.js",
"chars": 26,
"preview": "console.log('Test file');\n"
},
{
"path": "testing/testsuite.go",
"chars": 12906,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "testing/testsuite_test.go",
"chars": 7169,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "util.go",
"chars": 7118,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "util_test.go",
"chars": 2752,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "utils/simplestack.go",
"chars": 2330,
"preview": "package utils\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n)\n\ntype (\n\tSimpleLockStack struct {\n\t\tCurrent *SimpleLockStackElement\n\t\tCreator "
},
{
"path": "utils/simplestack_test.go",
"chars": 2922,
"preview": "package utils\n\nimport (\n\t\"testing\"\n)\n\ntype SimpleStackTest struct {\n\tindex int\n}\n\nfunc TestUnique(b *testing.T) {\n\tstack"
},
{
"path": "validation.go",
"chars": 9753,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "validation_test.go",
"chars": 3213,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "validators.go",
"chars": 14055,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "validators_test.go",
"chars": 24561,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "version.go",
"chars": 437,
"preview": "// Copyright (c) 2012-2018 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
},
{
"path": "watcher.go",
"chars": 8662,
"preview": "// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.\n// Revel Framework source code and usage is"
}
]
About this extraction
This page contains the full source code of the revel/revel GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 132 files (583.7 KB), approximately 165.5k tokens, and a symbol index with 1135 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.