diff --git a/dev/API/assets.html b/dev/API/assets.html index e7831134f..5a708e041 100644 --- a/dev/API/assets.html +++ b/dev/API/assets.html @@ -9,18 +9,18 @@ script(src = "/stippleui.jl/master/assets/js/vuedraggable.umd.min.js") script(src = "/stippleui.jl/master/assets/js/qsortabletree.js") ] -Stipple.DEPS[:qdraggabletree] = draggabletree_depssource
Genie.Assets.AssetsConfigType
mutable struct AssetsConfig

Manages the assets configuration for the current package. Define your own instance of AssetsConfig if you want to add support for asset management for your package through Genie.Assets.

source
Genie.Assets.assets_config!Function
assets_config!(packages::Vector{Module}; config...) :: Nothing
-assets_config!(package::Module; config...) :: Nothing

Utility function which allows bulk configuration of the assets.

Example

Genie.Assets.assets_config!([Genie, Stipple, StippleUI], host = "https://cdn.statically.io/gh/GenieFramework")
source
assets_config!(; config...) :: Nothing

Updates the assets configuration for the current package.

source
Missing docstring.

Missing docstring for assets_endpoint. Check Documenter's build log for details.

Genie.Assets.asset_fileFunction
asset_file(; cwd = "", file::String, path::String = "", type::String = "", prefix::String = "assets",
-              ext::String = "", min::Bool = false, skip_ext::Bool = false) :: String

Generates the file system path to an asset file.

source
Genie.Assets.asset_pathFunction
asset_path(; file::String, host::String = Genie.config.base_path, package::String = "", version::String = "",
+Stipple.DEPS[:qdraggabletree] = draggabletree_deps
source
Genie.Assets.AssetsConfigType
mutable struct AssetsConfig

Manages the assets configuration for the current package. Define your own instance of AssetsConfig if you want to add support for asset management for your package through Genie.Assets.

source
Genie.Assets.assets_config!Function
assets_config!(packages::Vector{Module}; config...) :: Nothing
+assets_config!(package::Module; config...) :: Nothing

Utility function which allows bulk configuration of the assets.

Example

Genie.Assets.assets_config!([Genie, Stipple, StippleUI], host = "https://cdn.statically.io/gh/GenieFramework")
source
assets_config!(; config...) :: Nothing

Updates the assets configuration for the current package.

source
Missing docstring.

Missing docstring for assets_endpoint. Check Documenter's build log for details.

Genie.Assets.asset_fileFunction
asset_file(; cwd = "", file::String, path::String = "", type::String = "", prefix::String = "assets",
+              ext::String = "", min::Bool = false, skip_ext::Bool = false) :: String

Generates the file system path to an asset file.

source
Genie.Assets.asset_pathFunction
asset_path(; file::String, host::String = Genie.config.base_path, package::String = "", version::String = "",
               prefix::String = "assets", type::String = "", path::String = "", min::Bool = false,
               ext::String = "", skip_ext::Bool = false, query::String = "") :: String
 asset_path(file::String; kwargs...) :: String
 asset_path(ac::AssetsConfig, tp::Union{Symbol,String}; type::String = string(tp), path::String = "",
-                file::String = "", ext::String = "", skip_ext::Bool = false, query::String = "") :: String

Generates the path to an asset file.

source
Genie.Assets.asset_routeFunction
asset_route(; file::String, package::String = "", version::String = "", prefix::String = "assets",
+                file::String = "", ext::String = "", skip_ext::Bool = false, query::String = "") :: String

Generates the path to an asset file.

source
Genie.Assets.asset_routeFunction
asset_route(; file::String, package::String = "", version::String = "", prefix::String = "assets",
               type::String = "", path::String = "", min::Bool = false,
               ext::String = "", skip_ext::Bool = false, query::String = "") :: String
 asset_route(file::String; kwargs...) :: String
 asset_route(ac::AssetsConfig, tp::Union{Symbol,String}; type::String = string(tp), path::String = "",
-            file::String = "", ext::String = "", skip_ext::Bool = false, query::String = "") :: String

Generates the route to an asset file.

source
Genie.Assets.channelsFunction
channels(channel::AbstractString = Genie.config.webchannels_default_route) :: String

Outputs the channels.js file included with the Genie package.

source
Missing docstring.

Missing docstring for channels_route. Check Documenter's build log for details.

Genie.Assets.channels_scriptFunction
channels_script(channel::AbstractString = Genie.config.webchannels_default_route) :: String

Outputs the channels JavaScript content within <script>...</script> tags, for embedding into the page.

source
Missing docstring.

Missing docstring for channels_script_tag. Check Documenter's build log for details.

Genie.Assets.channels_subscribeFunction
channels_subscribe(channel::AbstractString = Genie.config.webchannels_default_route) :: Nothing

Registers subscription and unsubscription channels for channel.

source
Genie.Assets.channels_supportFunction
channels_support(channel = Genie.config.webchannels_default_route) :: String

Provides full web channels support, setting up routes for loading support JS files, web sockets subscription and returning the <script> tag for including the linked JS file into the web page.

source
Genie.Assets.css_assetFunction
css_asset(file_name::String) :: String

Path to a css asset. The file_name should not include the extension.

source
Genie.Assets.embeddedFunction
embeded(path::String) :: String

Reads and outputs the file at path.

source
Genie.Assets.embedded_pathFunction
embeded_path(path::String) :: String

Returns the path relative to Genie's root package dir.

source
Genie.Assets.external_assetsFunction
external_assets(host::String) :: Bool
+            file::String = "", ext::String = "", skip_ext::Bool = false, query::String = "") :: String

Generates the route to an asset file.

source
Genie.Assets.channelsFunction
channels(channel::AbstractString = Genie.config.webchannels_default_route) :: String

Outputs the channels.js file included with the Genie package.

source
Missing docstring.

Missing docstring for channels_route. Check Documenter's build log for details.

Genie.Assets.channels_scriptFunction
channels_script(channel::AbstractString = Genie.config.webchannels_default_route) :: String

Outputs the channels JavaScript content within <script>...</script> tags, for embedding into the page.

source
Missing docstring.

Missing docstring for channels_script_tag. Check Documenter's build log for details.

Genie.Assets.channels_subscribeFunction
channels_subscribe(channel::AbstractString = Genie.config.webchannels_default_route) :: Nothing

Registers subscription and unsubscription channels for channel.

source
Genie.Assets.channels_supportFunction
channels_support(channel = Genie.config.webchannels_default_route) :: String

Provides full web channels support, setting up routes for loading support JS files, web sockets subscription and returning the <script> tag for including the linked JS file into the web page.

source
Genie.Assets.css_assetFunction
css_asset(file_name::String) :: String

Path to a css asset. The file_name should not include the extension.

source
Genie.Assets.embeddedFunction
embeded(path::String) :: String

Reads and outputs the file at path.

source
Genie.Assets.embedded_pathFunction
embeded_path(path::String) :: String

Returns the path relative to Genie's root package dir.

source
Genie.Assets.external_assetsFunction
external_assets(host::String) :: Bool
 external_assets(ac::AssetsConfig) :: Bool
-external_assets() :: Bool

Returns true if the current package is using external assets.

source
Genie.Assets.favicon_supportFunction
favicon_support() :: String

Outputs the <link> tag for referencing the favicon file embedded with Genie.

source
Genie.Assets.include_assetFunction
include_asset(asset_type::Union{String,Symbol}, file_name::Union{String,Symbol}) :: String

Returns the path to an asset. asset_type can be one of :js, :css. The file_name should not include the extension.

source
Genie.Assets.js_assetFunction
js_asset(file_name::String) :: String

Path to a js asset. file_name should not include the extension.

source
Missing docstring.

Missing docstring for jsliteral. Check Documenter's build log for details.

Genie.Assets.js_settingsFunction
js_settings(channel::String = Genie.config.webchannels_default_route) :: String

Sets up a window.Genie.Settings JavaScript object which exposes relevant Genie app settings from Genie.config

source
Genie.Assets.webthreadsFunction
webthreads() :: String

Outputs the webthreads.js file included with the Genie package

source
Missing docstring.

Missing docstring for webthreads_endpoint. Check Documenter's build log for details.

Genie.Assets.webthreads_push_pullFunction
function webthreads_push_pull(channel) :: Nothing

Registers push and pull routes for channel.

source
Missing docstring.

Missing docstring for webthreads_route. Check Documenter's build log for details.

Genie.Assets.webthreads_scriptFunction
webthreads_script() :: String

Outputs the channels JavaScript content within <script>...</script> tags, for embedding into the page.

source
Missing docstring.

Missing docstring for webthreads_script_tag. Check Documenter's build log for details.

Genie.Assets.webthreads_subscribeFunction
function webthreads_subscribe(channel) :: Nothing

Registers subscription and unsubscription routes for channel.

source
Genie.Assets.webthreads_supportFunction
webthreads_support(channel = Genie.config.webthreads_default_route) :: String

Provides full web channels support, setting up routes for loading support JS files, web sockets subscription and returning the <script> tag for including the linked JS file into the web page.

source
+external_assets() :: Bool

Returns true if the current package is using external assets.

source
Genie.Assets.favicon_supportFunction
favicon_support() :: String

Outputs the <link> tag for referencing the favicon file embedded with Genie.

source
Genie.Assets.include_assetFunction
include_asset(asset_type::Union{String,Symbol}, file_name::Union{String,Symbol}) :: String

Returns the path to an asset. asset_type can be one of :js, :css. The file_name should not include the extension.

source
Genie.Assets.js_assetFunction
js_asset(file_name::String) :: String

Path to a js asset. file_name should not include the extension.

source
Missing docstring.

Missing docstring for jsliteral. Check Documenter's build log for details.

Genie.Assets.js_settingsFunction
js_settings(channel::String = Genie.config.webchannels_default_route) :: String

Sets up a window.Genie.Settings JavaScript object which exposes relevant Genie app settings from Genie.config

source
Genie.Assets.webthreadsFunction
webthreads() :: String

Outputs the webthreads.js file included with the Genie package

source
Missing docstring.

Missing docstring for webthreads_endpoint. Check Documenter's build log for details.

Genie.Assets.webthreads_push_pullFunction
function webthreads_push_pull(channel) :: Nothing

Registers push and pull routes for channel.

source
Missing docstring.

Missing docstring for webthreads_route. Check Documenter's build log for details.

Genie.Assets.webthreads_scriptFunction
webthreads_script() :: String

Outputs the channels JavaScript content within <script>...</script> tags, for embedding into the page.

source
Missing docstring.

Missing docstring for webthreads_script_tag. Check Documenter's build log for details.

Genie.Assets.webthreads_subscribeFunction
function webthreads_subscribe(channel) :: Nothing

Registers subscription and unsubscription routes for channel.

source
Genie.Assets.webthreads_supportFunction
webthreads_support(channel = Genie.config.webthreads_default_route) :: String

Provides full web channels support, setting up routes for loading support JS files, web sockets subscription and returning the <script> tag for including the linked JS file into the web page.

source
diff --git a/dev/API/commands.html b/dev/API/commands.html index ba48fce98..8d7270cca 100644 --- a/dev/API/commands.html +++ b/dev/API/commands.html @@ -1,2 +1,2 @@ -Commands · Genie - The Highly Productive Julia Web Framework
Genie.Commands.called_commandFunction
called_command(args::Dict, key::String) :: Bool

Checks whether or not a certain command was invoked by looking at the command line args.

source
Genie.Commands.executeFunction
execute(config::Settings) :: Nothing

Runs the requested Genie app command, based on the args passed to the script.

source
Genie.Commands.parse_commandline_argsFunction
parse_commandline_args() :: Dict{String,Any}

Extracts the command line args passed into the app and returns them as a Dict, possibly setting up defaults. Also, it is used by the ArgParse module to populate the command line help for the app -h.

source
+Commands · Genie - The Highly Productive Julia Web Framework
Genie.Commands.called_commandFunction
called_command(args::Dict, key::String) :: Bool

Checks whether or not a certain command was invoked by looking at the command line args.

source
Genie.Commands.executeFunction
execute(config::Settings) :: Nothing

Runs the requested Genie app command, based on the args passed to the script.

source
Genie.Commands.parse_commandline_argsFunction
parse_commandline_args() :: Dict{String,Any}

Extracts the command line args passed into the app and returns them as a Dict, possibly setting up defaults. Also, it is used by the ArgParse module to populate the command line help for the app -h.

source
diff --git a/dev/API/configuration.html b/dev/API/configuration.html index 25a75bcc6..7d54a0054 100644 --- a/dev/API/configuration.html +++ b/dev/API/configuration.html @@ -1,15 +1,15 @@ -Configuration · Genie - The Highly Productive Julia Web Framework
Genie.Configuration.isdevFunction
isdev()  :: Bool

Set of utility functions that return whether or not the current environment is development, production or testing.

Examples

julia> Configuration.isdev()
+Configuration · Genie - The Highly Productive Julia Web Framework
Genie.Configuration.isdevFunction
isdev()  :: Bool

Set of utility functions that return whether or not the current environment is development, production or testing.

Examples

julia> Configuration.isdev()
 true
 
 julia> Configuration.isprod()
-false
source
Genie.Configuration.isprodFunction
isprod() :: Bool

Set of utility functions that return whether or not the current environment is development, production or testing.

Examples

julia> Configuration.isdev()
+false
source
Genie.Configuration.isprodFunction
isprod() :: Bool

Set of utility functions that return whether or not the current environment is development, production or testing.

Examples

julia> Configuration.isdev()
 true
 
 julia> Configuration.isprod()
-false
source
Genie.Configuration.istestFunction
istest() :: Bool

Set of utility functions that return whether or not the current environment is development, production or testing.

Examples

julia> Configuration.isdev()
+false
source
Genie.Configuration.istestFunction
istest() :: Bool

Set of utility functions that return whether or not the current environment is development, production or testing.

Examples

julia> Configuration.isdev()
 true
 
 julia> Configuration.isprod()
-false
source
Genie.Configuration.SettingsType
mutable struct Settings

App configuration - sets up the app's defaults. Individual options are overwritten in the corresponding environment file.

Arguments

  • server_port::Int: the port for running the web server (default 8000)
  • server_host::String: the host for running the web server (default "127.0.0.1")
  • server_document_root::String: path to the document root (default "public/")
  • server_handle_static_files::Bool: if true, Genie will also serve static files. In production, it is recommended to serve static files with a web server like Nginx.
  • server_signature::String: Genie's signature used for tagging the HTTP responses. If empty, it will not be added.
  • app_env::String: the environment in which the app is running (dev, test, or prod)
  • cors_headers::Dict{String,String}: default Access-Control-* CORS settings
  • cors_allowed_origins::Vector{String}: allowed origin hosts for CORS settings
  • log_level::Logging.LogLevel: logging severity level
  • log_to_file::Bool: if true, information will be logged to file besides REPL
  • log_requests::Bool: if true, requests will be automatically logged
  • inflector_irregulars::Vector{Tuple{String,String}}: additional irregular singular-plural forms to be used by the Inflector
  • run_as_server::Bool: when true the server thread is launched synchronously to avoid that the script exits
  • websockets_server::Bool: if true, the websocket server is also started together with the web server
  • websockets_port::Int: the port for the websocket server (default server_port)
  • initializers_folder::String: the folder where the initializers are located (default "initializers/")
  • path_config::String: the path to the configurations folder (default "config/")
  • path_env::String: the path to the environment files (default "<path_config>/env/")
  • path_app::String: the path to the app files (default "app/")
  • html_parser_close_tag::String: default " /". Can be changed to an empty string "" so the single tags would not be closed.
  • webchannels_keepalive_frequency::Int: default 30000. Frequency in milliseconds to send keepalive messages to webchannel/websocket to keep the connection alive. Set to 0 to disable keepalive messages.
source
+false
source
Genie.Configuration.SettingsType
mutable struct Settings

App configuration - sets up the app's defaults. Individual options are overwritten in the corresponding environment file.

Arguments

  • server_port::Int: the port for running the web server (default 8000)
  • server_host::String: the host for running the web server (default "127.0.0.1")
  • server_document_root::String: path to the document root (default "public/")
  • server_handle_static_files::Bool: if true, Genie will also serve static files. In production, it is recommended to serve static files with a web server like Nginx.
  • server_signature::String: Genie's signature used for tagging the HTTP responses. If empty, it will not be added.
  • app_env::String: the environment in which the app is running (dev, test, or prod)
  • cors_headers::Dict{String,String}: default Access-Control-* CORS settings
  • cors_allowed_origins::Vector{String}: allowed origin hosts for CORS settings
  • log_level::Logging.LogLevel: logging severity level
  • log_to_file::Bool: if true, information will be logged to file besides REPL
  • log_requests::Bool: if true, requests will be automatically logged
  • inflector_irregulars::Vector{Tuple{String,String}}: additional irregular singular-plural forms to be used by the Inflector
  • run_as_server::Bool: when true the server thread is launched synchronously to avoid that the script exits
  • websockets_server::Bool: if true, the websocket server is also started together with the web server
  • websockets_port::Int: the port for the websocket server (default server_port)
  • initializers_folder::String: the folder where the initializers are located (default "initializers/")
  • path_config::String: the path to the configurations folder (default "config/")
  • path_env::String: the path to the environment files (default "<path_config>/env/")
  • path_app::String: the path to the app files (default "app/")
  • html_parser_close_tag::String: default " /". Can be changed to an empty string "" so the single tags would not be closed.
  • webchannels_keepalive_frequency::Int: default 30000. Frequency in milliseconds to send keepalive messages to webchannel/websocket to keep the connection alive. Set to 0 to disable keepalive messages.
source
diff --git a/dev/API/cookies.html b/dev/API/cookies.html index e44c61a73..2b22c7d1f 100644 --- a/dev/API/cookies.html +++ b/dev/API/cookies.html @@ -5,4 +5,4 @@ "A" => 1

Alternatively, a sequence of pair arguments may be passed.

julia> Dict("A"=>1, "B"=>2)
 Dict{String, Int64} with 2 entries:
   "B" => 2
-  "A" => 1
source
Genie.Cookies.getFunction
get(payload::Union{HTTP.Response,HTTP.Request}, key::Union{String,Symbol}, default::T; encrypted::Bool = true)::T where T

Attempts to get the Cookie value stored at key within payload. If the key is not set, the default value is returned.

Arguments

  • payload::Union{HTTP.Response,HTTP.Request}: the request or response object containing the Cookie headers
  • key::Union{String,Symbol}: the name of the cookie value
  • default::T: default value to be returned if no cookie value is set at key
  • encrypted::Bool: if true the value stored on the cookie is automatically decrypted
source
get(res::HTTP.Response, key::Union{String,Symbol}) :: Union{Nothing,String}

Retrieves a value stored on the cookie as key from the Respose object.

Arguments

  • payload::Union{HTTP.Response,HTTP.Request}: the request or response object containing the Cookie headers
  • key::Union{String,Symbol}: the name of the cookie value
  • encrypted::Bool: if true the value stored on the cookie is automatically decrypted
source
get(req::Request, key::Union{String,Symbol}) :: Union{Nothing,String}

Retrieves a value stored on the cookie as key from the Request object.

Arguments

  • req::HTTP.Request: the request or response object containing the Cookie headers
  • key::Union{String,Symbol}: the name of the cookie value
  • encrypted::Bool: if true the value stored on the cookie is automatically decrypted
source
Genie.Cookies.getcookiesFunction
getcookies(req::HTTP.Request) :: Vector{HTTP.Cookies.Cookie}

Extracts cookies from within req

source
getcookies(req::HTTP.Request) :: Vector{HTTP.Cookies.Cookie}

Extracts cookies from within req, filtering them by matching name.

source
Genie.Cookies.set!Function
set!(res::HTTP.Response, key::Union{String,Symbol}, value::Any, attributes::Dict; encrypted::Bool = true) :: HTTP.Response

Sets value under the key label on the Cookie.

Arguments

  • res::HTTP.Response: the HTTP.Response object
  • key::Union{String,Symbol}: the key for storing the cookie value
  • value::Any: the cookie value
  • attributes::Dict: additional cookie attributes, such as path, httponly, maxage
  • encrypted::Bool: if true the value is stored encoded
source
Genie.Cookies.nullablevalueFunction
nullablevalue(payload::Union{HTTP.Response,HTTP.Request}, key::Union{String,Symbol}; encrypted::Bool = true)

Attempts to retrieve a cookie value stored at key in the payload object and returns a Union{Nothing,String}

Arguments

  • payload::Union{HTTP.Response,HTTP.Request}: the request or response object containing the Cookie headers
  • key::Union{String,Symbol}: the name of the cookie value
  • encrypted::Bool: if true the value stored on the cookie is automatically decrypted
source
+ "A" => 1source
Genie.Cookies.getFunction
get(payload::Union{HTTP.Response,HTTP.Request}, key::Union{String,Symbol}, default::T; encrypted::Bool = true)::T where T

Attempts to get the Cookie value stored at key within payload. If the key is not set, the default value is returned.

Arguments

  • payload::Union{HTTP.Response,HTTP.Request}: the request or response object containing the Cookie headers
  • key::Union{String,Symbol}: the name of the cookie value
  • default::T: default value to be returned if no cookie value is set at key
  • encrypted::Bool: if true the value stored on the cookie is automatically decrypted
source
get(res::HTTP.Response, key::Union{String,Symbol}) :: Union{Nothing,String}

Retrieves a value stored on the cookie as key from the Respose object.

Arguments

  • payload::Union{HTTP.Response,HTTP.Request}: the request or response object containing the Cookie headers
  • key::Union{String,Symbol}: the name of the cookie value
  • encrypted::Bool: if true the value stored on the cookie is automatically decrypted
source
get(req::Request, key::Union{String,Symbol}) :: Union{Nothing,String}

Retrieves a value stored on the cookie as key from the Request object.

Arguments

  • req::HTTP.Request: the request or response object containing the Cookie headers
  • key::Union{String,Symbol}: the name of the cookie value
  • encrypted::Bool: if true the value stored on the cookie is automatically decrypted
source
Genie.Cookies.getcookiesFunction
getcookies(req::HTTP.Request) :: Vector{HTTP.Cookies.Cookie}

Extracts cookies from within req

source
getcookies(req::HTTP.Request) :: Vector{HTTP.Cookies.Cookie}

Extracts cookies from within req, filtering them by matching name.

source
Genie.Cookies.set!Function
set!(res::HTTP.Response, key::Union{String,Symbol}, value::Any, attributes::Dict; encrypted::Bool = true) :: HTTP.Response

Sets value under the key label on the Cookie.

Arguments

  • res::HTTP.Response: the HTTP.Response object
  • key::Union{String,Symbol}: the key for storing the cookie value
  • value::Any: the cookie value
  • attributes::Dict: additional cookie attributes, such as path, httponly, maxage
  • encrypted::Bool: if true the value is stored encoded
source
Genie.Cookies.nullablevalueFunction
nullablevalue(payload::Union{HTTP.Response,HTTP.Request}, key::Union{String,Symbol}; encrypted::Bool = true)

Attempts to retrieve a cookie value stored at key in the payload object and returns a Union{Nothing,String}

Arguments

  • payload::Union{HTTP.Response,HTTP.Request}: the request or response object containing the Cookie headers
  • key::Union{String,Symbol}: the name of the cookie value
  • encrypted::Bool: if true the value stored on the cookie is automatically decrypted
source
diff --git a/dev/API/encryption.html b/dev/API/encryption.html index 1fb9c8d4f..ab0dd7199 100644 --- a/dev/API/encryption.html +++ b/dev/API/encryption.html @@ -1,2 +1,2 @@ -Encryption · Genie - The Highly Productive Julia Web Framework
+Encryption · Genie - The Highly Productive Julia Web Framework
diff --git a/dev/API/exceptions.html b/dev/API/exceptions.html index 998f59d1e..34b515743 100644 --- a/dev/API/exceptions.html +++ b/dev/API/exceptions.html @@ -1,2 +1,2 @@ -Exceptions · Genie - The Highly Productive Julia Web Framework
Genie.Exceptions.ExceptionalResponseType
struct ExceptionalResponse <: Exception

A type of exception which wraps an HTTP Response object. The thrown exception will propagate until it is caught up the app stack or ultimately by Genie and the wrapped response is sent to the client.

Example

If the user is not authenticated, an ExceptionalResponse is thrown - if the exception is not caught in the app's stack, Genie will catch it and return the wrapped Response object, forcing an HTTP redirect to the login page.

isauthenticated() || throw(ExceptionalResponse(redirect(:show_login)))
source
Genie.Exceptions.InternalServerExceptionType
struct InternalServerException <: Exception

Dedicated exception type for server side exceptions. Results in a 500 error by default.

Arguments

  • message::String
  • info::String
  • code::Int
source
Genie.Exceptions.NotFoundExceptionType
struct NotFoundException <: Exception

Specialized exception representing a not found resources. Results in a 404 response being sent to the client.

Arguments

  • message::String
  • info::String
  • code::Int
  • resource::String
source
Genie.Exceptions.RuntimeExceptionType
RuntimeException

Represents an unexpected and unhandled runtime exceptions. An error event will be logged and the exception will be sent to the client, depending on the environment (the error stack is dumped by default in dev mode or an error message is displayed in production).

It allows defining custom error message and info, as well as an error code, in addition to the exception object.

Arguments

  • message::String
  • info::String
  • code::Int
  • ex::Union{Nothing,Exception}
source
+Exceptions · Genie - The Highly Productive Julia Web Framework
Genie.Exceptions.ExceptionalResponseType
struct ExceptionalResponse <: Exception

A type of exception which wraps an HTTP Response object. The thrown exception will propagate until it is caught up the app stack or ultimately by Genie and the wrapped response is sent to the client.

Example

If the user is not authenticated, an ExceptionalResponse is thrown - if the exception is not caught in the app's stack, Genie will catch it and return the wrapped Response object, forcing an HTTP redirect to the login page.

isauthenticated() || throw(ExceptionalResponse(redirect(:show_login)))
source
Genie.Exceptions.InternalServerExceptionType
struct InternalServerException <: Exception

Dedicated exception type for server side exceptions. Results in a 500 error by default.

Arguments

  • message::String
  • info::String
  • code::Int
source
Genie.Exceptions.NotFoundExceptionType
struct NotFoundException <: Exception

Specialized exception representing a not found resources. Results in a 404 response being sent to the client.

Arguments

  • message::String
  • info::String
  • code::Int
  • resource::String
source
Genie.Exceptions.RuntimeExceptionType
RuntimeException

Represents an unexpected and unhandled runtime exceptions. An error event will be logged and the exception will be sent to the client, depending on the environment (the error stack is dumped by default in dev mode or an error message is displayed in production).

It allows defining custom error message and info, as well as an error code, in addition to the exception object.

Arguments

  • message::String
  • info::String
  • code::Int
  • ex::Union{Nothing,Exception}
source
diff --git a/dev/API/filetemplates.html b/dev/API/filetemplates.html index 6fb9c0cc5..27c10505f 100644 --- a/dev/API/filetemplates.html +++ b/dev/API/filetemplates.html @@ -1,2 +1,2 @@ -FileTemplates · Genie - The Highly Productive Julia Web Framework
+FileTemplates · Genie - The Highly Productive Julia Web Framework
diff --git a/dev/API/generator.html b/dev/API/generator.html index c08e947ed..e35954c45 100644 --- a/dev/API/generator.html +++ b/dev/API/generator.html @@ -1,5 +1,5 @@ -Generator · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for autoconfdb. Check Documenter's build log for details.

Genie.Generator.autostart_appFunction
autostart_app(path::String = "."; autostart::Bool = true) :: Nothing

If autostart is true, the newly generated Genie app will be automatically started.

source
Missing docstring.

Missing docstring for binfolderpath. Check Documenter's build log for details.

Missing docstring.

Missing docstring for db_intializer. Check Documenter's build log for details.

Genie.Generator.generate_projectFunction
generate_project(name)

Generate the Project.toml with a name and a uuid. If this file already exists, generate Project_sample.toml as a reference instead.

source
Missing docstring.

Missing docstring for install_db_dependencies. Check Documenter's build log for details.

Missing docstring.

Missing docstring for install_searchlight_dependencies. Check Documenter's build log for details.

Genie.Generator.minimalFunction
minimal(app_name::String, app_path::String = abspath(app_name), autostart::Bool = true) :: Nothing

Creates a minimal Genie app.

source
Genie.Generator.mvc_supportFunction
mvc_support(app_path::String = ".") :: Nothing

Writes the files used for rendering resources using the MVC stack and the Genie templating system.

source
Genie.Generator.newappFunction
newapp(app_name::String; autostart::Bool = true, fullstack::Bool = false, dbsupport::Bool = false, mvcsupport::Bool = false) :: Nothing

Scaffolds a new Genie app, setting up the file structure indicated by the various arguments.

Arguments

  • app_name::String: the name of the app (can be the full path where the app should be created).
  • autostart::Bool: automatically start the app once the file structure is created
  • fullstack::Bool: the type of app to be bootstrapped. The fullstack app includes MVC structure, DB connection code, and asset pipeline files.
  • dbsupport::Bool: bootstrap the files needed for DB connection setup via the SearchLight ORM
  • mvcsupport::Bool: adds the files used for HTML+Julia view templates rendering and working with resources
  • dbadapter::Union{String,Symbol,Nothing} = nothing : pass the SearchLight database adapter to be used by default

(one of :MySQL, :SQLite, or :PostgreSQL). If dbadapter is nothing, an adapter will have to be selected interactivel at the REPL, during the app creation process.

Examples

julia> Genie.Generator.newapp("MyGenieApp")
+Generator · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for autoconfdb. Check Documenter's build log for details.

Genie.Generator.autostart_appFunction
autostart_app(path::String = "."; autostart::Bool = true) :: Nothing

If autostart is true, the newly generated Genie app will be automatically started.

source
Missing docstring.

Missing docstring for binfolderpath. Check Documenter's build log for details.

Missing docstring.

Missing docstring for db_intializer. Check Documenter's build log for details.

Genie.Generator.generate_projectFunction
generate_project(name)

Generate the Project.toml with a name and a uuid. If this file already exists, generate Project_sample.toml as a reference instead.

source
Missing docstring.

Missing docstring for install_db_dependencies. Check Documenter's build log for details.

Missing docstring.

Missing docstring for install_searchlight_dependencies. Check Documenter's build log for details.

Genie.Generator.minimalFunction
minimal(app_name::String, app_path::String = abspath(app_name), autostart::Bool = true) :: Nothing

Creates a minimal Genie app.

source
Genie.Generator.mvc_supportFunction
mvc_support(app_path::String = ".") :: Nothing

Writes the files used for rendering resources using the MVC stack and the Genie templating system.

source
Genie.Generator.newappFunction
newapp(app_name::String; autostart::Bool = true, fullstack::Bool = false, dbsupport::Bool = false, mvcsupport::Bool = false) :: Nothing

Scaffolds a new Genie app, setting up the file structure indicated by the various arguments.

Arguments

  • app_name::String: the name of the app (can be the full path where the app should be created).
  • autostart::Bool: automatically start the app once the file structure is created
  • fullstack::Bool: the type of app to be bootstrapped. The fullstack app includes MVC structure, DB connection code, and asset pipeline files.
  • dbsupport::Bool: bootstrap the files needed for DB connection setup via the SearchLight ORM
  • mvcsupport::Bool: adds the files used for HTML+Julia view templates rendering and working with resources
  • dbadapter::Union{String,Symbol,Nothing} = nothing : pass the SearchLight database adapter to be used by default

(one of :MySQL, :SQLite, or :PostgreSQL). If dbadapter is nothing, an adapter will have to be selected interactivel at the REPL, during the app creation process.

Examples

julia> Genie.Generator.newapp("MyGenieApp")
 2019-08-06 16:54:15:INFO:Main: Done! New app created at MyGenieApp
 2019-08-06 16:54:15:DEBUG:Main: Changing active directory to MyGenieApp
 2019-08-06 16:54:15:DEBUG:Main: Installing app dependencies
@@ -20,4 +20,4 @@
 [ Info: Logging to file at MyGenieApp/log/dev.log
 [ Info: Ready!
 2019-08-06 16:54:32:DEBUG:Main: Web Server starting at http://127.0.0.1:8000
-2019-08-06 16:54:32:DEBUG:Main: Web Server running at http://127.0.0.1:8000
source
Genie.Generator.newapp_fullstackFunction
newapp_fullstack(name::String; autostart::Bool = true) :: Nothing

Template for scaffolding a new Genie app suitable for full stack web applications (includes MVC structure, DB support, and frontend asset pipeline).

Arguments

  • name::String: the name of the app
  • autostart::Bool: automatically start the app once the file structure is created
  • dbadapter::Union{String,Symbol,Nothing} = nothing : pass the SearchLight database adapter to be used by default

(one of :MySQL, :SQLite, or :PostgreSQL). If dbadapter is nothing, an adapter will have to be selected interactivel at the REPL, during the app creation process.

source
Genie.Generator.newapp_mvcFunction
newapp_mvc(name::String; autostart::Bool = true) :: Nothing

Template for scaffolding a new Genie app suitable for MVC web applications (includes MVC structure and DB support).

Arguments

  • name::String: the name of the app
  • autostart::Bool: automatically start the app once the file structure is created
  • dbadapter::Union{String,Symbol,Nothing} = nothing : pass the SearchLight database adapter to be used by default

(one of :MySQL, :SQLite, or :PostgreSQL). If dbadapter is nothing, an adapter will have to be selected interactivel at the REPL, during the app creation process.

source
Genie.Generator.newapp_webserviceFunction
newapp_webservice(name::String; autostart::Bool = true, dbsupport::Bool = false) :: Nothing

Template for scaffolding a new Genie app suitable for nimble web services.

Arguments

  • name::String: the name of the app
  • autostart::Bool: automatically start the app once the file structure is created
  • dbsupport::Bool: bootstrap the files needed for DB connection setup via the SearchLight ORM
  • dbadapter::Union{String,Symbol,Nothing} = nothing : pass the SearchLight database adapter to be used by default

(one of :MySQL, :SQLite, or :PostgreSQL). If dbadapter is nothing, an adapter will have to be selected interactivel at the REPL, during the app creation process.

source
Genie.Generator.newcontrollerFunction
newcontroller(controller_name::Union{String,Symbol}) :: Nothing

Creates a new controller file. If pluralize is false, the name of the controller is not automatically pluralized.

source
newcontroller(resource_name::String) :: Nothing

Generates a new Genie controller file and persists it to the resources folder.

source
Genie.Generator.newresourceFunction
newresource(resource_name::Union{String,Symbol}; pluralize::Bool = true, context::Union{Module,Nothing} = nothing) :: Nothing

Creates all the files associated with a new resource. If pluralize is false, the name of the resource is not automatically pluralized.

source
newresource(resource_name::String, config::Settings) :: Nothing

Generates all the files associated with a new resource and persists them to the resources folder.

source
Missing docstring.

Missing docstring for pkggenfile. Check Documenter's build log for details.

Missing docstring.

Missing docstring for pkgproject. Check Documenter's build log for details.

Missing docstring.

Missing docstring for post_create. Check Documenter's build log for details.

Genie.Generator.scaffoldFunction
scaffold(app_name::String, app_path::String = "") :: Nothing

Writes the file necessary to scaffold a minimal Genie app.

source
Missing docstring.

Missing docstring for set_files_mod. Check Documenter's build log for details.

Missing docstring.

Missing docstring for validname. Check Documenter's build log for details.

Missing docstring.

Missing docstring for write_db_config. Check Documenter's build log for details.

+2019-08-06 16:54:32:DEBUG:Main: Web Server running at http://127.0.0.1:8000
source
Genie.Generator.newapp_fullstackFunction
newapp_fullstack(name::String; autostart::Bool = true) :: Nothing

Template for scaffolding a new Genie app suitable for full stack web applications (includes MVC structure, DB support, and frontend asset pipeline).

Arguments

  • name::String: the name of the app
  • autostart::Bool: automatically start the app once the file structure is created
  • dbadapter::Union{String,Symbol,Nothing} = nothing : pass the SearchLight database adapter to be used by default

(one of :MySQL, :SQLite, or :PostgreSQL). If dbadapter is nothing, an adapter will have to be selected interactivel at the REPL, during the app creation process.

source
Genie.Generator.newapp_mvcFunction
newapp_mvc(name::String; autostart::Bool = true) :: Nothing

Template for scaffolding a new Genie app suitable for MVC web applications (includes MVC structure and DB support).

Arguments

  • name::String: the name of the app
  • autostart::Bool: automatically start the app once the file structure is created
  • dbadapter::Union{String,Symbol,Nothing} = nothing : pass the SearchLight database adapter to be used by default

(one of :MySQL, :SQLite, or :PostgreSQL). If dbadapter is nothing, an adapter will have to be selected interactivel at the REPL, during the app creation process.

source
Genie.Generator.newapp_webserviceFunction
newapp_webservice(name::String; autostart::Bool = true, dbsupport::Bool = false) :: Nothing

Template for scaffolding a new Genie app suitable for nimble web services.

Arguments

  • name::String: the name of the app
  • autostart::Bool: automatically start the app once the file structure is created
  • dbsupport::Bool: bootstrap the files needed for DB connection setup via the SearchLight ORM
  • dbadapter::Union{String,Symbol,Nothing} = nothing : pass the SearchLight database adapter to be used by default

(one of :MySQL, :SQLite, or :PostgreSQL). If dbadapter is nothing, an adapter will have to be selected interactivel at the REPL, during the app creation process.

source
Genie.Generator.newcontrollerFunction
newcontroller(controller_name::Union{String,Symbol}) :: Nothing

Creates a new controller file. If pluralize is false, the name of the controller is not automatically pluralized.

source
newcontroller(resource_name::String) :: Nothing

Generates a new Genie controller file and persists it to the resources folder.

source
Genie.Generator.newresourceFunction
newresource(resource_name::Union{String,Symbol}; pluralize::Bool = true, context::Union{Module,Nothing} = nothing) :: Nothing

Creates all the files associated with a new resource. If pluralize is false, the name of the resource is not automatically pluralized.

source
newresource(resource_name::String, config::Settings) :: Nothing

Generates all the files associated with a new resource and persists them to the resources folder.

source
Missing docstring.

Missing docstring for pkggenfile. Check Documenter's build log for details.

Missing docstring.

Missing docstring for pkgproject. Check Documenter's build log for details.

Missing docstring.

Missing docstring for post_create. Check Documenter's build log for details.

Genie.Generator.scaffoldFunction
scaffold(app_name::String, app_path::String = "") :: Nothing

Writes the file necessary to scaffold a minimal Genie app.

source
Missing docstring.

Missing docstring for set_files_mod. Check Documenter's build log for details.

Missing docstring.

Missing docstring for validname. Check Documenter's build log for details.

Missing docstring.

Missing docstring for write_db_config. Check Documenter's build log for details.

diff --git a/dev/API/genie.html b/dev/API/genie.html index ceb8ce139..33550de34 100644 --- a/dev/API/genie.html +++ b/dev/API/genie.html @@ -1,5 +1,5 @@ -Genie · Genie - The Highly Productive Julia Web Framework
Genie.downFunction
down(; webserver::Bool = true, websockets::Bool = true) :: ServersCollection

Shuts down the servers optionally indicating which of the webserver and websockets servers to be stopped. It does not remove the servers from the SERVERS collection. Returns the collection.

source
Genie.down!Function
function down!(; webserver::Bool = true, websockets::Bool = true) :: Vector{ServersCollection}

Shuts down all the servers and empties the SERVERS collection. Returns the empty collection.

source
Genie.goFunction
loadapp(path::String = "."; autostart::Bool = false) :: Nothing

Loads an existing Genie app from the file system, within the current Julia REPL session.

Arguments

  • path::String: the path to the Genie app on the file system.
  • autostart::Bool: automatically start the app upon loading it.

Examples

shell> tree -L 1
+Genie · Genie - The Highly Productive Julia Web Framework
Genie.downFunction
down(; webserver::Bool = true, websockets::Bool = true) :: ServersCollection

Shuts down the servers optionally indicating which of the webserver and websockets servers to be stopped. It does not remove the servers from the SERVERS collection. Returns the collection.

source
Genie.down!Function
function down!(; webserver::Bool = true, websockets::Bool = true) :: Vector{ServersCollection}

Shuts down all the servers and empties the SERVERS collection. Returns the empty collection.

source
Genie.goFunction
loadapp(path::String = "."; autostart::Bool = false) :: Nothing

Loads an existing Genie app from the file system, within the current Julia REPL session.

Arguments

  • path::String: the path to the Genie app on the file system.
  • autostart::Bool: automatically start the app upon loading it.

Examples

shell> tree -L 1
 .
 ├── Manifest.toml
 ├── Project.toml
@@ -26,7 +26,7 @@
 ┌ Info:
 │ Starting Genie in >> DEV << mode
 └
-[ Info: Logging to file at MyGenieApp/log/dev.log
source
Missing docstring.

Missing docstring for isrunning. Check Documenter's build log for details.

Genie.loadappFunction
loadapp(path::String = "."; autostart::Bool = false) :: Nothing

Loads an existing Genie app from the file system, within the current Julia REPL session.

Arguments

  • path::String: the path to the Genie app on the file system.
  • autostart::Bool: automatically start the app upon loading it.

Examples

shell> tree -L 1
+[ Info: Logging to file at MyGenieApp/log/dev.log
source
Missing docstring.

Missing docstring for isrunning. Check Documenter's build log for details.

Genie.loadappFunction
loadapp(path::String = "."; autostart::Bool = false) :: Nothing

Loads an existing Genie app from the file system, within the current Julia REPL session.

Arguments

  • path::String: the path to the Genie app on the file system.
  • autostart::Bool: automatically start the app upon loading it.

Examples

shell> tree -L 1
 .
 ├── Manifest.toml
 ├── Project.toml
@@ -53,7 +53,7 @@
 ┌ Info:
 │ Starting Genie in >> DEV << mode
 └
-[ Info: Logging to file at MyGenieApp/log/dev.log
source
Genie.runFunction
run() :: Nothing

Runs the Genie app by parsing the command line args and invoking the corresponding actions. Used internally to parse command line arguments.

source
Genie.upFunction
up(port::Int = Genie.config.server_port, host::String = Genie.config.server_host;
+[ Info: Logging to file at MyGenieApp/log/dev.log
source
Genie.runFunction
run() :: Nothing

Runs the Genie app by parsing the command line args and invoking the corresponding actions. Used internally to parse command line arguments.

source
Genie.upFunction
up(port::Int = Genie.config.server_port, host::String = Genie.config.server_host;
     ws_port::Int = Genie.config.websockets_port, async::Bool = ! Genie.config.run_as_server) :: Nothing

Starts the web server. Alias for Server.up

Arguments

  • port::Int: the port used by the web server
  • host::String: the host used by the web server
  • ws_port::Int: the port used by the Web Sockets server
  • async::Bool: run the web server task asynchronously

Examples

julia> up(8000, "127.0.0.1", async = false)
 [ Info: Ready!
-Web Server starting at http://127.0.0.1:8000
source
+Web Server starting at http://127.0.0.1:8000
source
diff --git a/dev/API/headers.html b/dev/API/headers.html index c12e7b049..77ab94944 100644 --- a/dev/API/headers.html +++ b/dev/API/headers.html @@ -1,2 +1,2 @@ -Headers · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for set_access_control_allow_headers!. Check Documenter's build log for details.

Missing docstring.

Missing docstring for set_access_control_allow_origin!. Check Documenter's build log for details.

Genie.Headers.set_headers!Function
set_headers!(req::HTTP.Request, res::HTTP.Response, app_response::HTTP.Response) :: HTTP.Response

Configures the response headers.

source
+Headers · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for set_access_control_allow_headers!. Check Documenter's build log for details.

Missing docstring.

Missing docstring for set_access_control_allow_origin!. Check Documenter's build log for details.

Genie.Headers.set_headers!Function
set_headers!(req::HTTP.Request, res::HTTP.Response, app_response::HTTP.Response) :: HTTP.Response

Configures the response headers.

source
diff --git a/dev/API/httputils.html b/dev/API/httputils.html index 67e84b197..4acc8dcc8 100644 --- a/dev/API/httputils.html +++ b/dev/API/httputils.html @@ -1,2 +1,2 @@ -HttpUtils · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for HTTPUtils.Dict. Check Documenter's build log for details.

+HttpUtils · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for HTTPUtils.Dict. Check Documenter's build log for details.

diff --git a/dev/API/index.html b/dev/API/index.html index 7ec885e07..edce8203b 100644 --- a/dev/API/index.html +++ b/dev/API/index.html @@ -1,2 +1,2 @@ -Genie · Genie - The Highly Productive Julia Web Framework

Genie Logo

Genie

The highly productive Julia web framework

Genie is Julia web framework that provides a streamlined and efficient workflow for developing modern web applications. It builds on Julia's strengths (high-level, high-performance, dynamic, JIT compiled), exposing a rich API and a powerful toolset for productive web development.

Current status

Genie is compatible with Julia v1.3 and up.


Acknowledgements

  • Genie uses a multitude of packages that have been kindly contributed by the Julia community.
  • The awesome Genie logo was designed by Alvaro Casanova.
+Genie · Genie - The Highly Productive Julia Web Framework

Genie Logo

Genie

The highly productive Julia web framework

Genie is Julia web framework that provides a streamlined and efficient workflow for developing modern web applications. It builds on Julia's strengths (high-level, high-performance, dynamic, JIT compiled), exposing a rich API and a powerful toolset for productive web development.

Current status

Genie is compatible with Julia v1.3 and up.


Acknowledgements

  • Genie uses a multitude of packages that have been kindly contributed by the Julia community.
  • The awesome Genie logo was designed by Alvaro Casanova.
diff --git a/dev/API/input.html b/dev/API/input.html index 16b8d5e70..d45704567 100644 --- a/dev/API/input.html +++ b/dev/API/input.html @@ -1,2 +1,2 @@ -Input · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for HttpFormPart. Check Documenter's build log for details.

Missing docstring.

Missing docstring for HttpInput. Check Documenter's build log for details.

Missing docstring.

Missing docstring for all. Check Documenter's build log for details.

Missing docstring.

Missing docstring for files. Check Documenter's build log for details.

Missing docstring.

Missing docstring for get_multiform_parts!. Check Documenter's build log for details.

Missing docstring.

Missing docstring for parse_seicolon_fields. Check Documenter's build log for details.

Missing docstring.

Missing docstring for post. Check Documenter's build log for details.

Missing docstring.

Missing docstring for post_from_request!. Check Documenter's build log for details.

Missing docstring.

Missing docstring for parse_quoted_params. Check Documenter's build log for details.

Missing docstring.

Missing docstring for post_multipart!. Check Documenter's build log for details.

Missing docstring.

Missing docstring for post_url_encoded!. Check Documenter's build log for details.

+Input · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for HttpFormPart. Check Documenter's build log for details.

Missing docstring.

Missing docstring for HttpInput. Check Documenter's build log for details.

Missing docstring.

Missing docstring for all. Check Documenter's build log for details.

Missing docstring.

Missing docstring for files. Check Documenter's build log for details.

Missing docstring.

Missing docstring for get_multiform_parts!. Check Documenter's build log for details.

Missing docstring.

Missing docstring for parse_seicolon_fields. Check Documenter's build log for details.

Missing docstring.

Missing docstring for post. Check Documenter's build log for details.

Missing docstring.

Missing docstring for post_from_request!. Check Documenter's build log for details.

Missing docstring.

Missing docstring for parse_quoted_params. Check Documenter's build log for details.

Missing docstring.

Missing docstring for post_multipart!. Check Documenter's build log for details.

Missing docstring.

Missing docstring for post_url_encoded!. Check Documenter's build log for details.

diff --git a/dev/API/loader.html b/dev/API/loader.html index 74a3c3b4d..5a2d6f40a 100644 --- a/dev/API/loader.html +++ b/dev/API/loader.html @@ -1,2 +1,2 @@ -Loader · Genie - The Highly Productive Julia Web Framework
Genie.Loader.autoloadFunction
autoload

Automatically and recursively includes files from the indicated root_dir into the indicated context module, skipping directories from dir. The files are set up with Revise to be automatically reloaded when changed (in dev environment).

source
Genie.Loader.bootstrapFunction
bootstrap(context::Union{Module,Nothing} = nothing) :: Nothing

Kickstarts the loading of a Genie app by loading the environment settings.

source
Missing docstring.

Missing docstring for importenv. Check Documenter's build log for details.

Genie.Loader.loadFunction
load(; context::Union{Module,Nothing} = nothing) :: Nothing

Main entry point to loading a Genie app.

source
Genie.Loader.load_helpersFunction
load_helpers(root_dir::String = Genie.config.path_helpers) :: Nothing

Automatically recursively includes files from helpers/ and subfolders.

source
Genie.Loader.load_initializersFunction
load_initializers(root_dir::String = Genie.config.path_config; context::Union{Module,Nothing} = nothing) :: Nothing

Automatically recursively includes files from initializers/ and subfolders.

source
Genie.Loader.load_libsFunction
load_libs(root_dir::String = Genie.config.path_lib) :: Nothing

Recursively includes files from lib/ and subfolders. The lib/ folder, if present, is designed to host user code in the form of .jl files.

source
Genie.Loader.load_pluginsFunction
load_plugins(root_dir::String = Genie.config.path_plugins; context::Union{Module,Nothing} = nothing) :: Nothing

Automatically recursively includes files from plugins/ and subfolders.

source
Genie.Loader.load_resourcesFunction
load_resources(root_dir::String = Genie.config.path_resources) :: Nothing

Automatically recursively includes files from resources/ and subfolders.

source
Genie.Loader.load_routesFunction
load_routes(routes_file::String = Genie.ROUTES_FILE_NAME; context::Union{Module,Nothing} = nothing) :: Nothing

Loads the routes file.

source
+Loader · Genie - The Highly Productive Julia Web Framework
Genie.Loader.autoloadFunction
autoload

Automatically and recursively includes files from the indicated root_dir into the indicated context module, skipping directories from dir. The files are set up with Revise to be automatically reloaded when changed (in dev environment).

source
Genie.Loader.bootstrapFunction
bootstrap(context::Union{Module,Nothing} = nothing) :: Nothing

Kickstarts the loading of a Genie app by loading the environment settings.

source
Missing docstring.

Missing docstring for importenv. Check Documenter's build log for details.

Genie.Loader.loadFunction
load(; context::Union{Module,Nothing} = nothing) :: Nothing

Main entry point to loading a Genie app.

source
Genie.Loader.load_helpersFunction
load_helpers(root_dir::String = Genie.config.path_helpers) :: Nothing

Automatically recursively includes files from helpers/ and subfolders.

source
Genie.Loader.load_initializersFunction
load_initializers(root_dir::String = Genie.config.path_config; context::Union{Module,Nothing} = nothing) :: Nothing

Automatically recursively includes files from initializers/ and subfolders.

source
Genie.Loader.load_libsFunction
load_libs(root_dir::String = Genie.config.path_lib) :: Nothing

Recursively includes files from lib/ and subfolders. The lib/ folder, if present, is designed to host user code in the form of .jl files.

source
Genie.Loader.load_pluginsFunction
load_plugins(root_dir::String = Genie.config.path_plugins; context::Union{Module,Nothing} = nothing) :: Nothing

Automatically recursively includes files from plugins/ and subfolders.

source
Genie.Loader.load_resourcesFunction
load_resources(root_dir::String = Genie.config.path_resources) :: Nothing

Automatically recursively includes files from resources/ and subfolders.

source
Genie.Loader.load_routesFunction
load_routes(routes_file::String = Genie.ROUTES_FILE_NAME; context::Union{Module,Nothing} = nothing) :: Nothing

Loads the routes file.

source
diff --git a/dev/API/logger.html b/dev/API/logger.html index 3b47bc01e..8550e8468 100644 --- a/dev/API/logger.html +++ b/dev/API/logger.html @@ -1,2 +1,2 @@ -Logger · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for initialize_logging. Check Documenter's build log for details.

Missing docstring.

Missing docstring for timestamp_logger. Check Documenter's build log for details.

+Logger · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for initialize_logging. Check Documenter's build log for details.

Missing docstring.

Missing docstring for timestamp_logger. Check Documenter's build log for details.

diff --git a/dev/API/renderer-html.html b/dev/API/renderer-html.html index cc50fe451..e0df31b40 100644 --- a/dev/API/renderer-html.html +++ b/dev/API/renderer-html.html @@ -1,11 +1,11 @@ -HTML Renderer · Genie - The Highly Productive Julia Web Framework
Genie.Renderer.Html.normal_elementFunction
normal_element(f::Function, elem::String, attrs::Vector{Pair{Symbol,Any}} = Pair{Symbol,Any}[]) :: HTMLString

Generates a HTML element in the form <...></...>

source
Genie.Renderer.Html.void_elementFunction
void_element(elem::String, attrs::Vector{Pair{Symbol,String}} = Vector{Pair{Symbol,String}}()) :: HTMLString

Generates a void HTML element in the form <...>

source
Missing docstring.

Missing docstring for skip_element. Check Documenter's build log for details.

Missing docstring.

Missing docstring for include_markdown. Check Documenter's build log for details.

Genie.Renderer.Html.get_templateFunction
get_template(path::String; partial::Bool = true, context::Module = @__MODULE__, vars...) :: Function

Resolves the inclusion and rendering of a template file

source
Genie.Renderer.Html.parseviewFunction
parseview(data::String; partial = false, context::Module = @__MODULE__) :: Function

Parses a view file, returning a rendering function. If necessary, the function is JIT-compiled, persisted and loaded into memory.

source
Genie.Renderer.Html.renderFunction
render(data::String; context::Module = @__MODULE__, layout::Union{String,Nothing} = nothing, vars...) :: Function

Renders the string as an HTML view.

source
render(viewfile::Genie.Renderer.FilePath; layout::Union{Nothing,Genie.Renderer.FilePath} = nothing, context::Module = @__MODULE__, vars...) :: Function

Renders the template file as an HTML view.

source
Genie.Renderer.Html.parsehtmlFunction
parsehtml(input::String; partial::Bool = true) :: String
source
parsehtml(elem, output; partial = true) :: String

Parses a HTML tree structure into a string of Julia code.

source
Genie.Renderer.Html.htmlFunction
html(data::String; context::Module = @__MODULE__, status::Int = 200, headers::HTTPHeaders = HTTPHeaders(), layout::Union{String,Nothing} = nothing, vars...) :: HTTP.Response

Parses the data input as HTML, returning a HTML HTTP Response.

Arguments

  • data::String: the HTML string to be rendered
  • context::Module: the module in which the variables are evaluated (in order to provide the scope for vars). Usually the controller.
  • status::Int: status code of the response
  • headers::HTTPHeaders: HTTP response headers
  • layout::Union{String,Nothing}: layout file for rendering data

Example

julia> html("<h1>Welcome $(vars(:name))</h1>", layout = "<div><% @yield %></div>", name = "Adrian")
+HTML Renderer · Genie - The Highly Productive Julia Web Framework
Genie.Renderer.Html.normal_elementFunction
normal_element(f::Function, elem::String, attrs::Vector{Pair{Symbol,Any}} = Pair{Symbol,Any}[]) :: HTMLString

Generates a HTML element in the form <...></...>

source
Genie.Renderer.Html.void_elementFunction
void_element(elem::String, attrs::Vector{Pair{Symbol,String}} = Vector{Pair{Symbol,String}}()) :: HTMLString

Generates a void HTML element in the form <...>

source
Missing docstring.

Missing docstring for skip_element. Check Documenter's build log for details.

Missing docstring.

Missing docstring for include_markdown. Check Documenter's build log for details.

Genie.Renderer.Html.get_templateFunction
get_template(path::String; partial::Bool = true, context::Module = @__MODULE__, vars...) :: Function

Resolves the inclusion and rendering of a template file

source
Genie.Renderer.Html.parseviewFunction
parseview(data::String; partial = false, context::Module = @__MODULE__) :: Function

Parses a view file, returning a rendering function. If necessary, the function is JIT-compiled, persisted and loaded into memory.

source
Genie.Renderer.Html.renderFunction
render(data::String; context::Module = @__MODULE__, layout::Union{String,Nothing} = nothing, vars...) :: Function

Renders the string as an HTML view.

source
render(viewfile::Genie.Renderer.FilePath; layout::Union{Nothing,Genie.Renderer.FilePath} = nothing, context::Module = @__MODULE__, vars...) :: Function

Renders the template file as an HTML view.

source
Genie.Renderer.Html.parsehtmlFunction
parsehtml(input::String; partial::Bool = true) :: String
source
parsehtml(elem, output; partial = true) :: String

Parses a HTML tree structure into a string of Julia code.

source
Genie.Renderer.Html.htmlFunction
html(data::String; context::Module = @__MODULE__, status::Int = 200, headers::HTTPHeaders = HTTPHeaders(), layout::Union{String,Nothing} = nothing, vars...) :: HTTP.Response

Parses the data input as HTML, returning a HTML HTTP Response.

Arguments

  • data::String: the HTML string to be rendered
  • context::Module: the module in which the variables are evaluated (in order to provide the scope for vars). Usually the controller.
  • status::Int: status code of the response
  • headers::HTTPHeaders: HTTP response headers
  • layout::Union{String,Nothing}: layout file for rendering data

Example

julia> html("<h1>Welcome $(vars(:name))</h1>", layout = "<div><% @yield %></div>", name = "Adrian")
 HTTP.Messages.Response:
 "
 HTTP/1.1 200 OK
 Content-Type: text/html; charset=utf-8
 
 <html><head></head><body><div><h1>Welcome Adrian</h1>
-</div></body></html>"
source
html(md::Markdown.MD; context::Module = @__MODULE__, status::Int = 200, headers::Genie.Renderer.HTTPHeaders = Genie.Renderer.HTTPHeaders(), layout::Union{String,Nothing} = nothing, forceparse::Bool = false, vars...) :: Genie.Renderer.HTTP.Response

Markdown view rendering

source
html(viewfile::FilePath; layout::Union{Nothing,FilePath} = nothing,
-      context::Module = @__MODULE__, status::Int = 200, headers::HTTPHeaders = HTTPHeaders(), vars...) :: HTTP.Response

Parses and renders the HTML viewfile, optionally rendering it within the layout file. Valid file format is .html.jl.

Arguments

  • viewfile::FilePath: filesystem path to the view file as a Renderer.FilePath, ie Renderer.filepath("/path/to/file.html.jl") or path"/path/to/file.html.jl"
  • layout::FilePath: filesystem path to the layout file as a Renderer.FilePath, ie Renderer.FilePath("/path/to/file.html.jl") or path"/path/to/file.html.jl"
  • context::Module: the module in which the variables are evaluated (in order to provide the scope for vars). Usually the controller.
  • status::Int: status code of the response
  • headers::HTTPHeaders: HTTP response headers
source
Genie.Renderer.Html.safe_attrFunction
safe_attr(attr) :: String

Replaces illegal Julia characters from HTML attributes with safe ones, to be used as keyword arguments.

source
Missing docstring.

Missing docstring for parsehtml. Check Documenter's build log for details.

Genie.Renderer.Html.string_to_juliaFunction
string_to_julia(content::String; partial = true, f_name::Union{Symbol,Nothing} = nothing, prepend = "") :: String

Converts string view data to Julia code

source
Genie.Renderer.Html.to_juliaFunction
to_julia(input::String, f::Function; partial = true, f_name::Union{Symbol,Nothing} = nothing, prepend = "") :: String

Converts an input file to Julia code

source
Genie.Renderer.Html.partialFunction
partial(path::String; context::Module = @__MODULE__, vars...) :: String

Renders (includes) a view partial within a larger view or layout file.

source
Missing docstring.

Missing docstring for parse. Check Documenter's build log for details.

Missing docstring.

Missing docstring for parsetags. Check Documenter's build log for details.

Genie.Renderer.Html.register_elementFunction
register_element(elem::Union{Symbol,String}, elem_type::Union{Symbol,String} = :normal; context = @__MODULE__) :: Nothing

Generates a Julia function representing an HTML element.

source
Genie.Renderer.Html.register_normal_elementFunction
register_normal_element(elem::Union{Symbol,String}; context = @__MODULE__) :: Nothing

Generates a Julia function representing a "normal" HTML element: that is an element with a closing tag, <tag>...</tag>

source
Genie.Renderer.Html.register_void_elementFunction
register_void_element(elem::Union{Symbol,String}; context::Module = @__MODULE__) :: Nothing

Generates a Julia function representing a "void" HTML element: that is an element without a closing tag, <tag />

source
Missing docstring.

Missing docstring for attr. Check Documenter's build log for details.

Genie.Renderer.Html.for_eachFunction
for_each(f::Function, v)

Iterates over the v Vector and applies function f for each element. The results of each iteration are concatenated and the final string is returned.

source
Genie.Renderer.Html.collectionFunction
collection(template::Function, collection::Vector{T})::String where {T}

Creates a view fragment by repeateadly applying a function to each element of the collection.

source
Genie.Renderer.Html.serve_error_fileFunction
serve_error_file(error_code::Int, error_message::String = "", params::Dict{Symbol,Any} = Dict{Symbol,Any}()) :: Response

Serves the error file correspoding to error_code and current environment.

source
Missing docstring.

Missing docstring for el. Check Documenter's build log for details.

+</div></body></html>"
source
html(md::Markdown.MD; context::Module = @__MODULE__, status::Int = 200, headers::Genie.Renderer.HTTPHeaders = Genie.Renderer.HTTPHeaders(), layout::Union{String,Nothing} = nothing, forceparse::Bool = false, vars...) :: Genie.Renderer.HTTP.Response

Markdown view rendering

source
html(viewfile::FilePath; layout::Union{Nothing,FilePath} = nothing,
+      context::Module = @__MODULE__, status::Int = 200, headers::HTTPHeaders = HTTPHeaders(), vars...) :: HTTP.Response

Parses and renders the HTML viewfile, optionally rendering it within the layout file. Valid file format is .html.jl.

Arguments

  • viewfile::FilePath: filesystem path to the view file as a Renderer.FilePath, ie Renderer.filepath("/path/to/file.html.jl") or path"/path/to/file.html.jl"
  • layout::FilePath: filesystem path to the layout file as a Renderer.FilePath, ie Renderer.FilePath("/path/to/file.html.jl") or path"/path/to/file.html.jl"
  • context::Module: the module in which the variables are evaluated (in order to provide the scope for vars). Usually the controller.
  • status::Int: status code of the response
  • headers::HTTPHeaders: HTTP response headers
source
Genie.Renderer.Html.safe_attrFunction
safe_attr(attr) :: String

Replaces illegal Julia characters from HTML attributes with safe ones, to be used as keyword arguments.

source
Missing docstring.

Missing docstring for parsehtml. Check Documenter's build log for details.

Genie.Renderer.Html.string_to_juliaFunction
string_to_julia(content::String; partial = true, f_name::Union{Symbol,Nothing} = nothing, prepend = "") :: String

Converts string view data to Julia code

source
Genie.Renderer.Html.to_juliaFunction
to_julia(input::String, f::Function; partial = true, f_name::Union{Symbol,Nothing} = nothing, prepend = "") :: String

Converts an input file to Julia code

source
Genie.Renderer.Html.partialFunction
partial(path::String; context::Module = @__MODULE__, vars...) :: String

Renders (includes) a view partial within a larger view or layout file.

source
Missing docstring.

Missing docstring for parse. Check Documenter's build log for details.

Missing docstring.

Missing docstring for parsetags. Check Documenter's build log for details.

Genie.Renderer.Html.register_elementFunction
register_element(elem::Union{Symbol,String}, elem_type::Union{Symbol,String} = :normal; context = @__MODULE__) :: Nothing

Generates a Julia function representing an HTML element.

source
Genie.Renderer.Html.register_normal_elementFunction
register_normal_element(elem::Union{Symbol,String}; context = @__MODULE__) :: Nothing

Generates a Julia function representing a "normal" HTML element: that is an element with a closing tag, <tag>...</tag>

source
Genie.Renderer.Html.register_void_elementFunction
register_void_element(elem::Union{Symbol,String}; context::Module = @__MODULE__) :: Nothing

Generates a Julia function representing a "void" HTML element: that is an element without a closing tag, <tag />

source
Missing docstring.

Missing docstring for attr. Check Documenter's build log for details.

Genie.Renderer.Html.for_eachFunction
for_each(f::Function, v)

Iterates over the v Vector and applies function f for each element. The results of each iteration are concatenated and the final string is returned.

source
Genie.Renderer.Html.collectionFunction
collection(template::Function, collection::Vector{T})::String where {T}

Creates a view fragment by repeateadly applying a function to each element of the collection.

source
Genie.Renderer.Html.serve_error_fileFunction
serve_error_file(error_code::Int, error_message::String = "", params::Dict{Symbol,Any} = Dict{Symbol,Any}()) :: Response

Serves the error file correspoding to error_code and current environment.

source
Missing docstring.

Missing docstring for el. Check Documenter's build log for details.

diff --git a/dev/API/renderer-js.html b/dev/API/renderer-js.html index e72d560df..b5c729328 100644 --- a/dev/API/renderer-js.html +++ b/dev/API/renderer-js.html @@ -1,2 +1,2 @@ -JS Renderer · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for get_template. Check Documenter's build log for details.

Missing docstring.

Missing docstring for to_js. Check Documenter's build log for details.

Missing docstring.

Missing docstring for render. Check Documenter's build log for details.

Missing docstring.

Missing docstring for js. Check Documenter's build log for details.

Missing docstring.

Missing docstring for Genie.Router.error. Check Documenter's build log for details.

+JS Renderer · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for get_template. Check Documenter's build log for details.

Missing docstring.

Missing docstring for to_js. Check Documenter's build log for details.

Missing docstring.

Missing docstring for render. Check Documenter's build log for details.

Missing docstring.

Missing docstring for js. Check Documenter's build log for details.

Missing docstring.

Missing docstring for Genie.Router.error. Check Documenter's build log for details.

diff --git a/dev/API/renderer-json.html b/dev/API/renderer-json.html index 62df3a2ec..64c2a7e2b 100644 --- a/dev/API/renderer-json.html +++ b/dev/API/renderer-json.html @@ -1,2 +1,2 @@ -JSON Renderer · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for render. Check Documenter's build log for details.

Missing docstring.

Missing docstring for Genie.Renderer.render. Check Documenter's build log for details.

Missing docstring.

Missing docstring for json. Check Documenter's build log for details.

Missing docstring.

Missing docstring for Genie.Router.error. Check Documenter's build log for details.

+JSON Renderer · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for render. Check Documenter's build log for details.

Missing docstring.

Missing docstring for Genie.Renderer.render. Check Documenter's build log for details.

Missing docstring.

Missing docstring for json. Check Documenter's build log for details.

Missing docstring.

Missing docstring for Genie.Router.error. Check Documenter's build log for details.

diff --git a/dev/API/renderer.html b/dev/API/renderer.html index dfcc4c2a7..64a458590 100644 --- a/dev/API/renderer.html +++ b/dev/API/renderer.html @@ -1,2 +1,2 @@ -Renderer · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for render. Check Documenter's build log for details.

Genie.Renderer.redirectFunction

Sets redirect headers and prepares the Response. It accepts 3 parameters: 1 - Label of a Route (to learn more, see the advanced routes section) 2 - Default HTTP 302 Found Status: indicates that the provided resource will be changed to a URL provided 3 - Tuples (key, value) to define the HTTP request header

Example: julia> Genie.Renderer.redirect(:index, 302, Dict("Content-Type" => "application/json; charset=UTF-8"))

HTTP.Messages.Response: HTTP/1.1 302 Moved Temporarily Content-Type: application/json; charset=UTF-8 Location: /index

Redirecting you to /index

source
Missing docstring.

Missing docstring for injectvars. Check Documenter's build log for details.

Genie.Renderer.view_file_infoFunction
view_file_info(path::String, supported_extensions::Vector{String}) :: Tuple{String,String}

Extracts path and extension info about a file

source
Genie.Renderer.vars_signatureFunction
vars_signature() :: String

Collects the names of the view vars in order to create a unique hash/salt to identify compiled views with different vars.

source
Genie.Renderer.build_is_staleFunction
build_is_stale(file_path::String, build_path::String) :: Bool

Checks if the view template has been changed since the last time the template was compiled.

source
Genie.Renderer.build_moduleFunction
build_module(content::String, path::String, mod_name::String) :: String

Persists compiled Julia view data to file and returns the path

source
Genie.Renderer.set_negotiated_contentFunction
set_negotiated_content(req::HTTP.Request, res::HTTP.Response, params::Dict{Symbol,Any})

Configures the request, response, and params response content type based on the request and defaults.

source
Genie.Renderer.negotiate_contentFunction
negotiate_content(req::Request, res::Response, params::Params) :: Response

Computes the content-type of the Response, based on the information in the Request.

source
+Renderer · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for render. Check Documenter's build log for details.

Genie.Renderer.redirectFunction

Sets redirect headers and prepares the Response. It accepts 3 parameters: 1 - Label of a Route (to learn more, see the advanced routes section) 2 - Default HTTP 302 Found Status: indicates that the provided resource will be changed to a URL provided 3 - Tuples (key, value) to define the HTTP request header

Example: julia> Genie.Renderer.redirect(:index, 302, Dict("Content-Type" => "application/json; charset=UTF-8"))

HTTP.Messages.Response: HTTP/1.1 302 Moved Temporarily Content-Type: application/json; charset=UTF-8 Location: /index

Redirecting you to /index

source
Missing docstring.

Missing docstring for injectvars. Check Documenter's build log for details.

Genie.Renderer.view_file_infoFunction
view_file_info(path::String, supported_extensions::Vector{String}) :: Tuple{String,String}

Extracts path and extension info about a file

source
Genie.Renderer.vars_signatureFunction
vars_signature() :: String

Collects the names of the view vars in order to create a unique hash/salt to identify compiled views with different vars.

source
Genie.Renderer.build_is_staleFunction
build_is_stale(file_path::String, build_path::String) :: Bool

Checks if the view template has been changed since the last time the template was compiled.

source
Genie.Renderer.build_moduleFunction
build_module(content::String, path::String, mod_name::String) :: String

Persists compiled Julia view data to file and returns the path

source
Genie.Renderer.set_negotiated_contentFunction
set_negotiated_content(req::HTTP.Request, res::HTTP.Response, params::Dict{Symbol,Any})

Configures the request, response, and params response content type based on the request and defaults.

source
Genie.Renderer.negotiate_contentFunction
negotiate_content(req::Request, res::Response, params::Params) :: Response

Computes the content-type of the Response, based on the information in the Request.

source
diff --git a/dev/API/requests.html b/dev/API/requests.html index 3dbb6d5c5..d92d51a0a 100644 --- a/dev/API/requests.html +++ b/dev/API/requests.html @@ -1,5 +1,5 @@ -Requests · Genie - The Highly Productive Julia Web Framework
Genie.Requests.jsonpayloadFunction
jsonpayload()

Processes an application/json POST request. If it fails to successfully parse the JSON data it returns nothing. The original payload can still be accessed invoking rawpayload()

source
jsonpayload(v)

Processes an application/json POST request attempting to return value corresponding to key v.

source
Genie.Requests.filespayloadFunction
filespayload() :: Dict{String,HttpFile}

Collection of form uploaded files.

source
filespayload(filename::Union{String,Symbol}) :: HttpFile

Returns the HttpFile uploaded through the key input name.

source
Genie.Requests.infilespayloadFunction
infilespayload(key::Union{String,Symbol}) :: Bool

Checks if the collection of uploaded files contains a file stored under the key name.

source
Base.writeFunction
write(io::IO, x)
+Requests · Genie - The Highly Productive Julia Web Framework
Genie.Requests.jsonpayloadFunction
jsonpayload()

Processes an application/json POST request. If it fails to successfully parse the JSON data it returns nothing. The original payload can still be accessed invoking rawpayload()

source
jsonpayload(v)

Processes an application/json POST request attempting to return value corresponding to key v.

source
Genie.Requests.filespayloadFunction
filespayload() :: Dict{String,HttpFile}

Collection of form uploaded files.

source
filespayload(filename::Union{String,Symbol}) :: HttpFile

Returns the HttpFile uploaded through the key input name.

source
Genie.Requests.infilespayloadFunction
infilespayload(key::Union{String,Symbol}) :: Bool

Checks if the collection of uploaded files contains a file stored under the key name.

source
Base.writeFunction
write(io::IO, x)
 write(filename::AbstractString, x)

Write the canonical binary representation of a value to the given I/O stream or file. Return the number of bytes written into the stream. See also print to write a text representation (with an encoding that may depend upon io).

The endianness of the written value depends on the endianness of the host system. Convert to/from a fixed endianness when writing/reading (e.g. using htol and ltoh) to get results that are consistent across platforms.

You can write multiple values with the same write call. i.e. the following are equivalent:

write(io, x, y...)
 write(io, x) + write(io, y...)

Examples

Consistent serialization:

julia> fname = tempname(); # random temporary filename
 
@@ -45,4 +45,4 @@
 "JuliaLang is a GitHub organization"
source
read(filename::AbstractString, args...)

Open a file and read its contents. args is passed to read: this is equivalent to open(io->read(io, args...), filename).

read(filename::AbstractString, String)

Read the entire contents of a file as a string.

source
read(s::IO, nb=typemax(Int))

Read at most nb bytes from s, returning a Vector{UInt8} of the bytes read.

source
read(s::IOStream, nb::Integer; all=true)

Read at most nb bytes from s, returning a Vector{UInt8} of the bytes read.

If all is true (the default), this function will block repeatedly trying to read all requested bytes, until an error or end-of-file occurs. If all is false, at most one read call is performed, and the amount of data returned is device-dependent. Note that not all stream types support the all option.

source
read(command::Cmd)

Run command and return the resulting output as an array of bytes.

source
read(command::Cmd, String)

Run command and return the resulting output as a String.

source
read(stream::IO, [nb::Integer,] enc::Encoding)
 read(filename::AbstractString, [nb::Integer,] enc::Encoding)
 read(stream::IO, ::Type{String}, enc::Encoding)
-read(filename::AbstractString, ::Type{String}, enc::Encoding)

Methods to read text in character encoding enc. See documentation for corresponding methods without the enc argument for details.

read(file::HttpFile)

Returns the content of file as string.

source
Genie.Requests.postpayloadFunction
postpayload() :: Dict{Symbol,Any}

A dict representing the POST variables payload of the request (corresponding to a form-data request)

source
postpayload(key::Symbol) :: Any

Returns the value of the POST variables key.

source
postpayload(key::Symbol, default::Any)

Returns the value of the POST variables key or the default value if key is not defined.

source
Genie.Requests.getpayloadFunction
getpayload() :: Dict{Symbol,Any}

A dict representing the GET/query variables payload of the request (the part corresponding to ?foo=bar&baz=moo)

source
getpayload(key::Symbol) :: Any

The value of the GET/query variable key, as in ?key=value

source
getpayload(key::Symbol, default::Any) :: Any

The value of the GET/query variable key, as in ?key=value. If key is not defined, default is returned.

source
Genie.Requests.requestFunction
request() :: HTTP.Request

Returns the raw HTTP.Request object associated with the request. If no request is available (not within a request/response cycle) returns nothing.

source
Genie.Requests.payloadFunction
payload() :: Any

Utility function for accessing the params collection, which holds the request variables.

source
payload(key::Symbol) :: Any

Utility function for accessing the key value within the params collection of request variables.

source
payload(key::Symbol, default_value::T) :: Any

Utility function for accessing the key value within the params collection of request variables. If key is not defined, default_value is returned.

source
Genie.Requests.matchedrouteFunction
matchedroute() :: Route

Returns the Route object which was matched for the current request or noting if no route is available.

source
Genie.Requests.matchedchannelFunction
matchedchannel() :: Channel

Returns the Channel object which was matched for the current request or nothing if no channel is available.

source
Genie.Requests.wsclientFunction
wsclient() :: HTTP.WebSockets.WebSocket

The web sockets client for the current request or nothing if not available.

source
+read(filename::AbstractString, ::Type{String}, enc::Encoding)

Methods to read text in character encoding enc. See documentation for corresponding methods without the enc argument for details.

read(file::HttpFile)

Returns the content of file as string.

source
Genie.Requests.postpayloadFunction
postpayload() :: Dict{Symbol,Any}

A dict representing the POST variables payload of the request (corresponding to a form-data request)

source
postpayload(key::Symbol) :: Any

Returns the value of the POST variables key.

source
postpayload(key::Symbol, default::Any)

Returns the value of the POST variables key or the default value if key is not defined.

source
Genie.Requests.getpayloadFunction
getpayload() :: Dict{Symbol,Any}

A dict representing the GET/query variables payload of the request (the part corresponding to ?foo=bar&baz=moo)

source
getpayload(key::Symbol) :: Any

The value of the GET/query variable key, as in ?key=value

source
getpayload(key::Symbol, default::Any) :: Any

The value of the GET/query variable key, as in ?key=value. If key is not defined, default is returned.

source
Genie.Requests.requestFunction
request() :: HTTP.Request

Returns the raw HTTP.Request object associated with the request. If no request is available (not within a request/response cycle) returns nothing.

source
Genie.Requests.payloadFunction
payload() :: Any

Utility function for accessing the params collection, which holds the request variables.

source
payload(key::Symbol) :: Any

Utility function for accessing the key value within the params collection of request variables.

source
payload(key::Symbol, default_value::T) :: Any

Utility function for accessing the key value within the params collection of request variables. If key is not defined, default_value is returned.

source
Genie.Requests.matchedrouteFunction
matchedroute() :: Route

Returns the Route object which was matched for the current request or noting if no route is available.

source
Genie.Requests.matchedchannelFunction
matchedchannel() :: Channel

Returns the Channel object which was matched for the current request or nothing if no channel is available.

source
Genie.Requests.wsclientFunction
wsclient() :: HTTP.WebSockets.WebSocket

The web sockets client for the current request or nothing if not available.

source
diff --git a/dev/API/responses.html b/dev/API/responses.html index 0b71ab200..efef101dd 100644 --- a/dev/API/responses.html +++ b/dev/API/responses.html @@ -1,2 +1,2 @@ -Responses · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for getresponse. Check Documenter's build log for details.

Missing docstring.

Missing docstring for getheaders. Check Documenter's build log for details.

Missing docstring.

Missing docstring for setheaders!. Check Documenter's build log for details.

Missing docstring.

Missing docstring for setheaders. Check Documenter's build log for details.

Missing docstring.

Missing docstring for getstatus. Check Documenter's build log for details.

Missing docstring.

Missing docstring for setstatus!. Check Documenter's build log for details.

Missing docstring.

Missing docstring for setstatus. Check Documenter's build log for details.

Missing docstring.

Missing docstring for getbody. Check Documenter's build log for details.

Missing docstring.

Missing docstring for setbody!. Check Documenter's build log for details.

Missing docstring.

Missing docstring for setbody. Check Documenter's build log for details.

+Responses · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for getresponse. Check Documenter's build log for details.

Missing docstring.

Missing docstring for getheaders. Check Documenter's build log for details.

Missing docstring.

Missing docstring for setheaders!. Check Documenter's build log for details.

Missing docstring.

Missing docstring for setheaders. Check Documenter's build log for details.

Missing docstring.

Missing docstring for getstatus. Check Documenter's build log for details.

Missing docstring.

Missing docstring for setstatus!. Check Documenter's build log for details.

Missing docstring.

Missing docstring for setstatus. Check Documenter's build log for details.

Missing docstring.

Missing docstring for getbody. Check Documenter's build log for details.

Missing docstring.

Missing docstring for setbody!. Check Documenter's build log for details.

Missing docstring.

Missing docstring for setbody. Check Documenter's build log for details.

diff --git a/dev/API/router.html b/dev/API/router.html index e12a35224..cb68613f3 100644 --- a/dev/API/router.html +++ b/dev/API/router.html @@ -1,5 +1,5 @@ -Router · Genie - The Highly Productive Julia Web Framework
Base.showFunction
show([io::IO = stdout], x)

Write a text representation of a value x to the output stream io. New types T should overload show(io::IO, x::T). The representation used by show generally includes Julia-specific formatting and type information, and should be parseable Julia code when possible.

repr returns the output of show as a string.

For a more verbose human-readable text output for objects of type T, define show(io::IO, ::MIME"text/plain", ::T) in addition. Checking the :compact IOContext key (often checked as get(io, :compact, false)::Bool) of io in such methods is recommended, since some containers show their elements by calling this method with :compact => true.

See also print, which writes un-decorated representations.

Examples

julia> show("Hello World!")
+Router · Genie - The Highly Productive Julia Web Framework
Base.showFunction
show([io::IO = stdout], x)

Write a text representation of a value x to the output stream io. New types T should overload show(io::IO, x::T). The representation used by show generally includes Julia-specific formatting and type information, and should be parseable Julia code when possible.

repr returns the output of show as a string.

For a more verbose human-readable text output for objects of type T, define show(io::IO, ::MIME"text/plain", ::T) in addition. Checking the :compact IOContext key (often checked as get(io, :compact, false)::Bool) of io in such methods is recommended, since some containers show their elements by calling this method with :compact => true.

See also print, which writes un-decorated representations.

Examples

julia> show("Hello World!")
 "Hello World!"
 julia> print("Hello World!")
 Hello World!
source
show(io::IO, mime, x)

The display functions ultimately call show in order to write an object x as a given mime type to a given I/O stream io (usually a memory buffer), if possible. In order to provide a rich multimedia representation of a user-defined type T, it is only necessary to define a new show method for T, via: show(io, ::MIME"mime", x::T) = ..., where mime is a MIME-type string and the function body calls write (or similar) to write that representation of x to io. (Note that the MIME"" notation only supports literal strings; to construct MIME types in a more flexible manner use MIME{Symbol("")}.)

For example, if you define a MyImage type and know how to write it to a PNG file, you could define a function show(io, ::MIME"image/png", x::MyImage) = ... to allow your images to be displayed on any PNG-capable AbstractDisplay (such as IJulia). As usual, be sure to import Base.show in order to add new methods to the built-in Julia function show.

Technically, the MIME"mime" macro defines a singleton type for the given mime string, which allows us to exploit Julia's dispatch mechanisms in determining how to display objects of any given type.

The default MIME type is MIME"text/plain". There is a fallback definition for text/plain output that calls show with 2 arguments, so it is not always necessary to add a method for that case. If a type benefits from custom human-readable output though, show(::IO, ::MIME"text/plain", ::T) should be defined. For example, the Day type uses 1 day as the output for the text/plain MIME type, and Day(1) as the output of 2-argument show.

Examples

julia> struct Day
@@ -9,12 +9,12 @@
 julia> Base.show(io::IO, ::MIME"text/plain", d::Day) = print(io, d.n, " day")
 
 julia> Day(1)
-1 day

Container types generally implement 3-argument show by calling show(io, MIME"text/plain"(), x) for elements x, with :compact => true set in an IOContext passed as the first argument.

source
Base.show(io::IO, ex::RuntimeException)

Custom printing of RuntimeException

source
Base.show(io::IO, ex::FileExistsException)

Custom printing for FileExistsException

source
Genie.Router.ParamsType
mutable struct Params{T}

Collection of key value pairs representing the parameters of the current request - response cycle.

source
Genie.Router.ispayloadFunction
ispayload(req::HTTP.Request)

True if the request can carry a payload - that is, it's a POST, PUT, or PATCH request

source
ispayload()

True if the request can carry a payload - that is, it's a POST, PUT, or PATCH request

source
Genie.Router.route_requestFunction
route_request(req::Request, res::Response) :: Response

First step in handling a request: sets up params collection, handles query vars, negotiates content.

source
Genie.Router.route_ws_requestFunction
route_ws_request(req::Request, msg::String, ws_client::HTTP.WebSockets.WebSocket) :: String

First step in handling a web socket request: sets up params collection, handles query vars.

source
Base.push!Function
push!(collection, items...) -> collection

Insert one or more items in collection. If collection is an ordered container, the items are inserted at the end (in the given order).

Examples

julia> push!([1, 2, 3], 4, 5, 6)
+1 day

Container types generally implement 3-argument show by calling show(io, MIME"text/plain"(), x) for elements x, with :compact => true set in an IOContext passed as the first argument.

source
Base.show(io::IO, ex::RuntimeException)

Custom printing of RuntimeException

source
Base.show(io::IO, ex::FileExistsException)

Custom printing for FileExistsException

source
Genie.Router.ParamsType
mutable struct Params{T}

Collection of key value pairs representing the parameters of the current request - response cycle.

source
Genie.Router.ispayloadFunction
ispayload(req::HTTP.Request)

True if the request can carry a payload - that is, it's a POST, PUT, or PATCH request

source
ispayload()

True if the request can carry a payload - that is, it's a POST, PUT, or PATCH request

source
Genie.Router.route_requestFunction
route_request(req::Request, res::Response) :: Response

First step in handling a request: sets up params collection, handles query vars, negotiates content.

source
Genie.Router.route_ws_requestFunction
route_ws_request(req::Request, msg::String, ws_client::HTTP.WebSockets.WebSocket) :: String

First step in handling a web socket request: sets up params collection, handles query vars.

source
Base.push!Function
push!(collection, items...) -> collection

Insert one or more items in collection. If collection is an ordered container, the items are inserted at the end (in the given order).

Examples

julia> push!([1, 2, 3], 4, 5, 6)
 6-element Vector{Int64}:
  1
  2
  3
  4
  5
- 6

If collection is ordered, use append! to add all the elements of another collection to it. The result of the preceding example is equivalent to append!([1, 2, 3], [4, 5, 6]). For AbstractSet objects, union! can be used instead.

See sizehint! for notes about the performance model.

See also pushfirst!.

source
Genie.Router.baptizerFunction
baptizer(params::Union{Route,Channel}, parts::Vector{String}) :: Symbol

Generates default names for routes and channels.

source
Missing docstring.

Missing docstring for routes. Check Documenter's build log for details.

Missing docstring.

Missing docstring for channels. Check Documenter's build log for details.

Genie.Router.delete!Function
delete!(route_name::Symbol)

Removes the route with the corresponding name from the routes collection and returns the collection of remaining routes.

source
Genie.Router.to_linkFunction

Generates the HTTP link corresponding to route_name using the parameters in d.

source

Generates the HTTP link corresponding to route_name using the parameters in route_params.

source
Genie.Router.tolinkFunction

Generates the HTTP link corresponding to route_name using the parameters in d.

source

Generates the HTTP link corresponding to route_name using the parameters in route_params.

source
Genie.Router.link_toFunction

Generates the HTTP link corresponding to route_name using the parameters in d.

source

Generates the HTTP link corresponding to route_name using the parameters in route_params.

source
Genie.Router.linktoFunction

Generates the HTTP link corresponding to route_name using the parameters in d.

source

Generates the HTTP link corresponding to route_name using the parameters in route_params.

source
Genie.Router.torouteFunction

Generates the HTTP link corresponding to route_name using the parameters in d.

source

Generates the HTTP link corresponding to route_name using the parameters in route_params.

source
Genie.Router.action_controller_paramsFunction
action_controller_params(action::Function, params::Params) :: Nothing

Sets up the :action_controller, :action, and :controller key - value pairs of the params collection.

source
Missing docstring.

Missing docstring for run_hook. Check Documenter's build log for details.

Genie.Router.match_routesFunction
match_routes(req::Request, res::Response, params::Params) :: Union{Route,Nothing}

Matches the invoked URL to the corresponding route, sets up the execution environment and invokes the controller method.

source
Genie.Router.match_channelsFunction
match_channels(req::Request, msg::String, ws_client::HTTP.WebSockets.WebSocket, params::Params) :: String

Matches the invoked URL to the corresponding channel, sets up the execution environment and invokes the channel controller method.

source
Genie.Router.parse_routeFunction
parse_route(route::String, context::Module = @__MODULE__) :: Tuple{String,Vector{String},Vector{Any}}

Parses a route and extracts its named params and types. context is used to access optional route parts types.

source
Genie.Router.parse_channelFunction
parse_channel(channel::String) :: Tuple{String,Vector{String},Vector{Any}}

Parses a channel and extracts its named parms and types.

source
Genie.Router.extract_uri_paramsFunction
extract_uri_params(uri::String, regex_route::Regex, param_names::Vector{String}, param_types::Vector{Any}, params::Params) :: Bool

Extracts params from request URI and sets up the params Dict.

source
Genie.Router.nested_keysFunction
nested_keys(k::String, v, params::Params) :: Nothing

Utility function to process nested keys and set them up in params.

source
Genie.Router.setup_base_paramsFunction
setup_base_params(req::Request, res::Response, params::Dict{Symbol,Any}) :: Dict{Symbol,Any}

Populates params with default environment vars.

source
Missing docstring.

Missing docstring for _params_. Check Documenter's build log for details.

Genie.Router.response_typeFunction
response_type{T}(params::Dict{Symbol,T}) :: Symbol
-response_type(params::Params) :: Symbol

Returns the content-type of the current request-response cycle.

source
response_type{T}(check::Symbol, params::Dict{Symbol,T}) :: Bool

Checks if the content-type of the current request-response cycle matches check.

source
Missing docstring.

Missing docstring for to_uri. Check Documenter's build log for details.

Genie.Router.file_pathFunction
file_path(resource::String; within_doc_root = true, root = Genie.config.server_document_root) :: String

Returns the path to a resource file. If within_doc_root it will automatically prepend the document root to resource.

source
Genie.Router.filepathFunction
file_path(resource::String; within_doc_root = true, root = Genie.config.server_document_root) :: String

Returns the path to a resource file. If within_doc_root it will automatically prepend the document root to resource.

source
Missing docstring.

Missing docstring for ormatch. Check Documenter's build log for details.

+ 6

If collection is ordered, use append! to add all the elements of another collection to it. The result of the preceding example is equivalent to append!([1, 2, 3], [4, 5, 6]). For AbstractSet objects, union! can be used instead.

See sizehint! for notes about the performance model.

See also pushfirst!.

source
Genie.Router.baptizerFunction
baptizer(params::Union{Route,Channel}, parts::Vector{String}) :: Symbol

Generates default names for routes and channels.

source
Missing docstring.

Missing docstring for routes. Check Documenter's build log for details.

Missing docstring.

Missing docstring for channels. Check Documenter's build log for details.

Genie.Router.delete!Function
delete!(route_name::Symbol)

Removes the route with the corresponding name from the routes collection and returns the collection of remaining routes.

source
Genie.Router.to_linkFunction

Generates the HTTP link corresponding to route_name using the parameters in d.

source

Generates the HTTP link corresponding to route_name using the parameters in route_params.

source
Genie.Router.tolinkFunction

Generates the HTTP link corresponding to route_name using the parameters in d.

source

Generates the HTTP link corresponding to route_name using the parameters in route_params.

source
Genie.Router.link_toFunction

Generates the HTTP link corresponding to route_name using the parameters in d.

source

Generates the HTTP link corresponding to route_name using the parameters in route_params.

source
Genie.Router.linktoFunction

Generates the HTTP link corresponding to route_name using the parameters in d.

source

Generates the HTTP link corresponding to route_name using the parameters in route_params.

source
Genie.Router.torouteFunction

Generates the HTTP link corresponding to route_name using the parameters in d.

source

Generates the HTTP link corresponding to route_name using the parameters in route_params.

source
Genie.Router.action_controller_paramsFunction
action_controller_params(action::Function, params::Params) :: Nothing

Sets up the :action_controller, :action, and :controller key - value pairs of the params collection.

source
Missing docstring.

Missing docstring for run_hook. Check Documenter's build log for details.

Genie.Router.match_routesFunction
match_routes(req::Request, res::Response, params::Params) :: Union{Route,Nothing}

Matches the invoked URL to the corresponding route, sets up the execution environment and invokes the controller method.

source
Genie.Router.match_channelsFunction
match_channels(req::Request, msg::String, ws_client::HTTP.WebSockets.WebSocket, params::Params) :: String

Matches the invoked URL to the corresponding channel, sets up the execution environment and invokes the channel controller method.

source
Genie.Router.parse_routeFunction
parse_route(route::String, context::Module = @__MODULE__) :: Tuple{String,Vector{String},Vector{Any}}

Parses a route and extracts its named params and types. context is used to access optional route parts types.

source
Genie.Router.parse_channelFunction
parse_channel(channel::String) :: Tuple{String,Vector{String},Vector{Any}}

Parses a channel and extracts its named parms and types.

source
Genie.Router.extract_uri_paramsFunction
extract_uri_params(uri::String, regex_route::Regex, param_names::Vector{String}, param_types::Vector{Any}, params::Params) :: Bool

Extracts params from request URI and sets up the params Dict.

source
Genie.Router.nested_keysFunction
nested_keys(k::String, v, params::Params) :: Nothing

Utility function to process nested keys and set them up in params.

source
Genie.Router.setup_base_paramsFunction
setup_base_params(req::Request, res::Response, params::Dict{Symbol,Any}) :: Dict{Symbol,Any}

Populates params with default environment vars.

source
Missing docstring.

Missing docstring for _params_. Check Documenter's build log for details.

Genie.Router.response_typeFunction
response_type{T}(params::Dict{Symbol,T}) :: Symbol
+response_type(params::Params) :: Symbol

Returns the content-type of the current request-response cycle.

source
response_type{T}(check::Symbol, params::Dict{Symbol,T}) :: Bool

Checks if the content-type of the current request-response cycle matches check.

source
Missing docstring.

Missing docstring for to_uri. Check Documenter's build log for details.

Genie.Router.file_pathFunction
file_path(resource::String; within_doc_root = true, root = Genie.config.server_document_root) :: String

Returns the path to a resource file. If within_doc_root it will automatically prepend the document root to resource.

source
Genie.Router.filepathFunction
file_path(resource::String; within_doc_root = true, root = Genie.config.server_document_root) :: String

Returns the path to a resource file. If within_doc_root it will automatically prepend the document root to resource.

source
Missing docstring.

Missing docstring for ormatch. Check Documenter's build log for details.

diff --git a/dev/API/secrets.html b/dev/API/secrets.html index c762459f3..3605d8175 100644 --- a/dev/API/secrets.html +++ b/dev/API/secrets.html @@ -1,2 +1,2 @@ -Secrets · Genie - The Highly Productive Julia Web Framework
Genie.Secrets.loadFunction
load(root_dir::String = Genie.config.path_config; context::Union{Module,Nothing} = nothing) :: Nothing

Loads (includes) the framework's secrets.jl file into the app's module context. The files are set up with Revise to be automatically reloaded.

source
Genie.Secrets.secretFunction
secret() :: String

Generates a random secret token to be used for configuring the call to Genie.Secrets.secret_token!.

source
Genie.Secrets.secret_tokenFunction
secret_token(generate_if_missing=true) :: String

Return the secret token used in the app for encryption and salting.

Usually, this token is defined through Genie.Secrets.secret_token! in the config/secrets.jl file. Here, a temporary one is generated for the current session if no other token is defined and generate_if_missing is true.

source
+Secrets · Genie - The Highly Productive Julia Web Framework
Genie.Secrets.loadFunction
load(root_dir::String = Genie.config.path_config; context::Union{Module,Nothing} = nothing) :: Nothing

Loads (includes) the framework's secrets.jl file into the app's module context. The files are set up with Revise to be automatically reloaded.

source
Genie.Secrets.secretFunction
secret() :: String

Generates a random secret token to be used for configuring the call to Genie.Secrets.secret_token!.

source
Genie.Secrets.secret_tokenFunction
secret_token(generate_if_missing=true) :: String

Return the secret token used in the app for encryption and salting.

Usually, this token is defined through Genie.Secrets.secret_token! in the config/secrets.jl file. Here, a temporary one is generated for the current session if no other token is defined and generate_if_missing is true.

source
diff --git a/dev/API/server.html b/dev/API/server.html index 1e6093d7d..148748b13 100644 --- a/dev/API/server.html +++ b/dev/API/server.html @@ -1,9 +1,9 @@ -Server · Genie - The Highly Productive Julia Web Framework
Genie.Server.SERVERSConstant
SERVERS

ServersCollection constant containing references to the current app's web and websockets servers.

source
Genie.Server.ServersCollectionType
ServersCollection(webserver::Union{Task,Nothing}, websockets::Union{Task,Nothing})

Represents a object containing references to Genie's web and websockets servers.

source
Genie.Server.downFunction
down(; webserver::Bool = true, websockets::Bool = true) :: ServersCollection

Shuts down the servers optionally indicating which of the webserver and websockets servers to be stopped. It does not remove the servers from the SERVERS collection. Returns the collection.

source
Genie.Server.down!Function
function down!(; webserver::Bool = true, websockets::Bool = true) :: Vector{ServersCollection}

Shuts down all the servers and empties the SERVERS collection. Returns the empty collection.

source
Genie.Server.handle_requestFunction
handle_request(req::HTTP.Request, res::HTTP.Response) :: HTTP.Response

Http server handler function - invoked when the server gets a request.

source
Genie.Server.handle_ws_requestFunction
handle_ws_request(req::HTTP.Request, msg::String, ws_client) :: String

Http server handler function - invoked when the server gets a request.

source
Missing docstring.

Missing docstring for isrunning. Check Documenter's build log for details.

Missing docstring.

Missing docstring for openbrowser. Check Documenter's build log for details.

Missing docstring.

Missing docstring for print_server_status. Check Documenter's build log for details.

Genie.Server.serveFunction
serve(path::String = pwd(), params...; kwparams...)

Serves a folder of static files located at path. Allows Genie to be used as a static files web server. The params and kwparams arguments are forwarded to Genie.up().

Arguments

  • path::String: the folder of static files to be served by the server
  • params: additional arguments which are passed to Genie.up to control the web server
  • kwparams: additional keyword arguments which are passed to Genie.up to control the web server

Examples

julia> Genie.serve("public", 8888, async = false, verbose = true)
+Server · Genie - The Highly Productive Julia Web Framework
Genie.Server.SERVERSConstant
SERVERS

ServersCollection constant containing references to the current app's web and websockets servers.

source
Genie.Server.ServersCollectionType
ServersCollection(webserver::Union{Task,Nothing}, websockets::Union{Task,Nothing})

Represents a object containing references to Genie's web and websockets servers.

source
Genie.Server.downFunction
down(; webserver::Bool = true, websockets::Bool = true) :: ServersCollection

Shuts down the servers optionally indicating which of the webserver and websockets servers to be stopped. It does not remove the servers from the SERVERS collection. Returns the collection.

source
Genie.Server.down!Function
function down!(; webserver::Bool = true, websockets::Bool = true) :: Vector{ServersCollection}

Shuts down all the servers and empties the SERVERS collection. Returns the empty collection.

source
Genie.Server.handle_requestFunction
handle_request(req::HTTP.Request, res::HTTP.Response) :: HTTP.Response

Http server handler function - invoked when the server gets a request.

source
Genie.Server.handle_ws_requestFunction
handle_ws_request(req::HTTP.Request, msg::String, ws_client) :: String

Http server handler function - invoked when the server gets a request.

source
Missing docstring.

Missing docstring for isrunning. Check Documenter's build log for details.

Missing docstring.

Missing docstring for openbrowser. Check Documenter's build log for details.

Missing docstring.

Missing docstring for print_server_status. Check Documenter's build log for details.

Genie.Server.serveFunction
serve(path::String = pwd(), params...; kwparams...)

Serves a folder of static files located at path. Allows Genie to be used as a static files web server. The params and kwparams arguments are forwarded to Genie.up().

Arguments

  • path::String: the folder of static files to be served by the server
  • params: additional arguments which are passed to Genie.up to control the web server
  • kwparams: additional keyword arguments which are passed to Genie.up to control the web server

Examples

julia> Genie.serve("public", 8888, async = false, verbose = true)
 [ Info: Ready!
 2019-08-06 16:39:20:DEBUG:Main: Web Server starting at http://127.0.0.1:8888
 [ Info: Listening on: 127.0.0.1:8888
-[ Info: Accept (1):  🔗    0↑     0↓    1s 127.0.0.1:8888:8888 ≣16
source
Missing docstring.

Missing docstring for server_status. Check Documenter's build log for details.

Genie.Server.setup_http_listenerFunction
setup_http_listener(req::HTTP.Request, res::HTTP.Response = HTTP.Response()) :: HTTP.Response

Configures the handler for the HTTP Request and handles errors.

source
Missing docstring.

Missing docstring for setup_http_streamer. Check Documenter's build log for details.

Genie.Server.upFunction
up(port::Int = Genie.config.server_port, host::String = Genie.config.server_host;
+[ Info: Accept (1):  🔗    0↑     0↓    1s 127.0.0.1:8888:8888 ≣16
source
Missing docstring.

Missing docstring for server_status. Check Documenter's build log for details.

Genie.Server.setup_http_listenerFunction
setup_http_listener(req::HTTP.Request, res::HTTP.Response = HTTP.Response()) :: HTTP.Response

Configures the handler for the HTTP Request and handles errors.

source
Missing docstring.

Missing docstring for setup_http_streamer. Check Documenter's build log for details.

Genie.Server.upFunction
up(port::Int = Genie.config.server_port, host::String = Genie.config.server_host;
     ws_port::Int = Genie.config.websockets_port, async::Bool = ! Genie.config.run_as_server) :: ServersCollection

Starts the web server.

Arguments

  • port::Int: the port used by the web server
  • host::String: the host used by the web server
  • ws_port::Int: the port used by the Web Sockets server
  • async::Bool: run the web server task asynchronously

Examples

julia> up(8000, "127.0.0.1", async = false)
 [ Info: Ready!
-Web Server starting at http://127.0.0.1:8000
source
Genie.Server.update_configFunction
update_config(port::Int, host::String, ws_port::Int) :: Nothing

Updates the corresponding Genie configurations to the corresponding values for port, host, and ws_port, if these are passed as arguments when starting up the server.

source
+Web Server starting at http://127.0.0.1:8000
source
Genie.Server.update_configFunction
update_config(port::Int, host::String, ws_port::Int) :: Nothing

Updates the corresponding Genie configurations to the corresponding values for port, host, and ws_port, if these are passed as arguments when starting up the server.

source
diff --git a/dev/API/sessions.html b/dev/API/sessions.html index 5b6720c7a..164e7550d 100644 --- a/dev/API/sessions.html +++ b/dev/API/sessions.html @@ -41,4 +41,4 @@ 3
get(f::Function, collection, key)

Return the value stored for the given key, or if no mapping for the key is present, return f(). Use get! to also store the default value in the dictionary.

This is intended to be called using do block syntax

get(dict, key) do
     # default value calculated here
     time()
-end
Missing docstring.

Missing docstring for unset!. Check Documenter's build log for details.

Missing docstring.

Missing docstring for isset. Check Documenter's build log for details.

Missing docstring.

Missing docstring for persist. Check Documenter's build log for details.

Missing docstring.

Missing docstring for load. Check Documenter's build log for details.

Missing docstring.

Missing docstring for session. Check Documenter's build log for details.

Missing docstring.

Missing docstring for init. Check Documenter's build log for details.

+end
Missing docstring.

Missing docstring for unset!. Check Documenter's build log for details.

Missing docstring.

Missing docstring for isset. Check Documenter's build log for details.

Missing docstring.

Missing docstring for persist. Check Documenter's build log for details.

Missing docstring.

Missing docstring for load. Check Documenter's build log for details.

Missing docstring.

Missing docstring for session. Check Documenter's build log for details.

Missing docstring.

Missing docstring for init. Check Documenter's build log for details.

diff --git a/dev/API/toolbox.html b/dev/API/toolbox.html index 23b739ee0..a804ca3c9 100644 --- a/dev/API/toolbox.html +++ b/dev/API/toolbox.html @@ -1,2 +1,2 @@ -Toolbox · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for TaskInfo. Check Documenter's build log for details.

Missing docstring.

Missing docstring for TaskResult. Check Documenter's build log for details.

Missing docstring.

Missing docstring for tasks. Check Documenter's build log for details.

Missing docstring.

Missing docstring for VoidTaskResult. Check Documenter's build log for details.

Genie.Toolbox.validtasknameFunction
validtaskname(task_name::String) :: String

Attempts to convert a potentially invalid (partial) task_name into a valid one.

source
Genie.Toolbox.taskdocsFunction
task_docs(module_name::Module) :: String

Retrieves the docstring of the runtask method and returns it as a string.

source
Genie.Toolbox.loadtasksFunction
loadtasks(; filter_type_name = Symbol()) :: Vector{TaskInfo}

Returns a vector of all registered Genie tasks.

source
newKeyword
new, or new{A,B,...}

Special function available to inner constructors which creates a new object of the type. The form new{A,B,...} explicitly specifies values of parameters for parametric types. See the manual section on Inner Constructor Methods for more information.

source
Genie.Toolbox.taskfilenameFunction
task_file_name(cmd_args::Dict{String,Any}, config::Settings) :: String

Computes the name of a Genie task based on the command line input.

source
Genie.Toolbox.taskmodulenameFunction
task_module_name(underscored_task_name::String) :: String

Computes the name of a Genie task based on the command line input.

source
Genie.Toolbox.isvalidtask!Function
isvalidtask!(parsed_args::Dict{String,Any}) :: Dict{String,Any}

Checks if the name of the task passed as the command line arg is valid task identifier – if not, attempts to address it, by appending the TASKSUFFIX suffix. Returns the potentially modified `parsedargsDict`.

source
+Toolbox · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for TaskInfo. Check Documenter's build log for details.

Missing docstring.

Missing docstring for TaskResult. Check Documenter's build log for details.

Missing docstring.

Missing docstring for tasks. Check Documenter's build log for details.

Missing docstring.

Missing docstring for VoidTaskResult. Check Documenter's build log for details.

Genie.Toolbox.validtasknameFunction
validtaskname(task_name::String) :: String

Attempts to convert a potentially invalid (partial) task_name into a valid one.

source
Genie.Toolbox.taskdocsFunction
task_docs(module_name::Module) :: String

Retrieves the docstring of the runtask method and returns it as a string.

source
Genie.Toolbox.loadtasksFunction
loadtasks(; filter_type_name = Symbol()) :: Vector{TaskInfo}

Returns a vector of all registered Genie tasks.

source
newKeyword
new, or new{A,B,...}

Special function available to inner constructors which creates a new object of the type. The form new{A,B,...} explicitly specifies values of parameters for parametric types. See the manual section on Inner Constructor Methods for more information.

source
Genie.Toolbox.taskfilenameFunction
task_file_name(cmd_args::Dict{String,Any}, config::Settings) :: String

Computes the name of a Genie task based on the command line input.

source
Genie.Toolbox.taskmodulenameFunction
task_module_name(underscored_task_name::String) :: String

Computes the name of a Genie task based on the command line input.

source
Genie.Toolbox.isvalidtask!Function
isvalidtask!(parsed_args::Dict{String,Any}) :: Dict{String,Any}

Checks if the name of the task passed as the command line arg is valid task identifier – if not, attempts to address it, by appending the TASKSUFFIX suffix. Returns the potentially modified `parsedargsDict`.

source
diff --git a/dev/API/util.html b/dev/API/util.html index 5a88efb82..841e70486 100644 --- a/dev/API/util.html +++ b/dev/API/util.html @@ -1,2 +1,2 @@ -Util · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for expand_nullable. Check Documenter's build log for details.

Genie.Util.walk_dirFunction
function walk_dir(dir, paths = String[]; only_extensions = ["jl"], only_files = true, only_dirs = false) :: Vector{String}

Recursively walks dir and produces non directories. If only_files, directories will be skipped. If only_dirs, files will be skipped.

source
Missing docstring.

Missing docstring for time_to_unixtimestamp. Check Documenter's build log for details.

+Util · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for expand_nullable. Check Documenter's build log for details.

Genie.Util.walk_dirFunction
function walk_dir(dir, paths = String[]; only_extensions = ["jl"], only_files = true, only_dirs = false) :: Vector{String}

Recursively walks dir and produces non directories. If only_files, directories will be skipped. If only_dirs, files will be skipped.

source
Missing docstring.

Missing docstring for time_to_unixtimestamp. Check Documenter's build log for details.

diff --git a/dev/API/watch.html b/dev/API/watch.html index 5f936ad01..4bc22d3ff 100644 --- a/dev/API/watch.html +++ b/dev/API/watch.html @@ -1,2 +1,2 @@ -Watch · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for WATCHED_FOLDERS. Check Documenter's build log for details.

Missing docstring.

Missing docstring for WATCHING. Check Documenter's build log for details.

Missing docstring.

Missing docstring for collect_watched_files. Check Documenter's build log for details.

Missing docstring.

Missing docstring for handlers. Check Documenter's build log for details.

Missing docstring.

Missing docstring for unwatch. Check Documenter's build log for details.

Missing docstring.

Missing docstring for watch. Check Documenter's build log for details.

Missing docstring.

Missing docstring for watchpath. Check Documenter's build log for details.

+Watch · Genie - The Highly Productive Julia Web Framework
Missing docstring.

Missing docstring for WATCHED_FOLDERS. Check Documenter's build log for details.

Missing docstring.

Missing docstring for WATCHING. Check Documenter's build log for details.

Missing docstring.

Missing docstring for collect_watched_files. Check Documenter's build log for details.

Missing docstring.

Missing docstring for handlers. Check Documenter's build log for details.

Missing docstring.

Missing docstring for unwatch. Check Documenter's build log for details.

Missing docstring.

Missing docstring for watch. Check Documenter's build log for details.

Missing docstring.

Missing docstring for watchpath. Check Documenter's build log for details.

diff --git a/dev/API/webchannels.html b/dev/API/webchannels.html index 96baaba99..21c374574 100644 --- a/dev/API/webchannels.html +++ b/dev/API/webchannels.html @@ -11,4 +11,4 @@ "A" => 1

Alternatively, a sequence of pair arguments may be passed.

julia> Dict("A"=>1, "B"=>2)
 Dict{String, Int64} with 2 entries:
   "B" => 2
-  "A" => 1
source
Missing docstring.

Missing docstring for MessagePayload. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ChannelMessage. Check Documenter's build log for details.

Missing docstring.

Missing docstring for CLIENTS. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SUBSCRIPTIONS. Check Documenter's build log for details.

Missing docstring.

Missing docstring for clients. Check Documenter's build log for details.

Missing docstring.

Missing docstring for subscriptions. Check Documenter's build log for details.

Missing docstring.

Missing docstring for websockets. Check Documenter's build log for details.

Missing docstring.

Missing docstring for channels. Check Documenter's build log for details.

Missing docstring.

Missing docstring for connected_clients. Check Documenter's build log for details.

Missing docstring.

Missing docstring for disconnected_clients. Check Documenter's build log for details.

Genie.WebChannels.subscribeFunction

Subscribes a web socket client ws to channel.

source
Genie.WebChannels.unsubscribeFunction

Unsubscribes a web socket client ws from channel.

source
Genie.WebChannels.unsubscribe_clientFunction

Unsubscribes a web socket client ws from all the channels.

source
Genie.WebChannels.unsubscribe_disconnected_clientsFunction

unsubscribedisconnectedclients() :: ChannelClientsCollection

Unsubscribes clients which are no longer connected.

source
Genie.WebChannels.push_subscriptionFunction

Adds a new subscription for client to channel.

source
Genie.WebChannels.pop_subscriptionFunction

Removes the subscription of client to channel.

source

Removes all subscriptions of client.

source
Genie.WebChannels.broadcastFunction

Pushes msg (and payload) to all the clients subscribed to the channels in channels, with the exception of except.

source

Pushes msg (and payload) to all the clients subscribed to the channels in channels, with the exception of except.

source
Genie.WebChannels.messageFunction

Writes msg to web socket for client.

source
+ "A" => 1source
Missing docstring.

Missing docstring for MessagePayload. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ChannelMessage. Check Documenter's build log for details.

Missing docstring.

Missing docstring for CLIENTS. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SUBSCRIPTIONS. Check Documenter's build log for details.

Missing docstring.

Missing docstring for clients. Check Documenter's build log for details.

Missing docstring.

Missing docstring for subscriptions. Check Documenter's build log for details.

Missing docstring.

Missing docstring for websockets. Check Documenter's build log for details.

Missing docstring.

Missing docstring for channels. Check Documenter's build log for details.

Missing docstring.

Missing docstring for connected_clients. Check Documenter's build log for details.

Missing docstring.

Missing docstring for disconnected_clients. Check Documenter's build log for details.

Genie.WebChannels.subscribeFunction

Subscribes a web socket client ws to channel.

source
Genie.WebChannels.unsubscribeFunction

Unsubscribes a web socket client ws from channel.

source
Genie.WebChannels.unsubscribe_clientFunction

Unsubscribes a web socket client ws from all the channels.

source
Genie.WebChannels.unsubscribe_disconnected_clientsFunction

unsubscribedisconnectedclients() :: ChannelClientsCollection

Unsubscribes clients which are no longer connected.

source
Genie.WebChannels.push_subscriptionFunction

Adds a new subscription for client to channel.

source
Genie.WebChannels.pop_subscriptionFunction

Removes the subscription of client to channel.

source

Removes all subscriptions of client.

source
Genie.WebChannels.broadcastFunction

Pushes msg (and payload) to all the clients subscribed to the channels in channels, with the exception of except.

source

Pushes msg (and payload) to all the clients subscribed to the channels in channels, with the exception of except.

source
Genie.WebChannels.messageFunction

Writes msg to web socket for client.

source
diff --git a/dev/API/webthreads.html b/dev/API/webthreads.html index 79152dd37..2f0bc17b2 100644 --- a/dev/API/webthreads.html +++ b/dev/API/webthreads.html @@ -11,4 +11,4 @@ "A" => 1

Alternatively, a sequence of pair arguments may be passed.

julia> Dict("A"=>1, "B"=>2)
 Dict{String, Int64} with 2 entries:
   "B" => 2
-  "A" => 1
source
Genie.WebThreads.ClientIdType
UInt64 <: Unsigned

64-bit unsigned integer type.

source
Genie.WebThreads.ChannelNameType
String <: AbstractString

The default string type in Julia, used by e.g. string literals.

Strings are immutable sequences of Chars. A String is stored internally as a contiguous byte array, and while they are interpreted as being UTF-8 encoded, they can be composed of any byte sequence. Use isvalid to validate that the underlying byte sequence is valid as UTF-8.

source
Missing docstring.

Missing docstring for MessagePayload. Check Documenter's build log for details.

Genie.WebThreads.broadcastFunction

Pushes msg (and payload) to all the clients subscribed to the channels in channels.

source

Pushes msg (and payload) to all the clients subscribed to all the channels.

source
Missing docstring.

Missing docstring for channels. Check Documenter's build log for details.

Missing docstring.

Missing docstring for clients. Check Documenter's build log for details.

Missing docstring.

Missing docstring for connected_clients. Check Documenter's build log for details.

Missing docstring.

Missing docstring for disconnected_clients. Check Documenter's build log for details.

Genie.WebThreads.messageFunction

Pushes msg (and payload) to channel.

source

Writes msg to message queue for client.

source
Genie.WebThreads.pop_subscriptionFunction

Removes the subscription of client to channel.

source

Removes all subscriptions of client.

source
Missing docstring.

Missing docstring for pull. Check Documenter's build log for details.

Missing docstring.

Missing docstring for push. Check Documenter's build log for details.

Genie.WebThreads.push_subscriptionFunction

Adds a new subscription for client to channel.

source
Genie.WebThreads.subscribeFunction

Subscribes a web thread client wt to channel.

source
Missing docstring.

Missing docstring for subscriptions. Check Documenter's build log for details.

Missing docstring.

Missing docstring for timestamp_client. Check Documenter's build log for details.

Genie.WebThreads.unsubscribeFunction

Unsubscribes a web socket client wt from channel.

source
Genie.WebThreads.unsubscribe_clientFunction

Unsubscribes a web socket client wt from all the channels.

source
Missing docstring.

Missing docstring for unsubscribe_clients. Check Documenter's build log for details.

Genie.WebThreads.unsubscribe_disconnected_clientsFunction

unsubscribedisconnectedclients() :: ChannelClientsCollection

Unsubscribes clients which are no longer connected.

source
Missing docstring.

Missing docstring for webthreads. Check Documenter's build log for details.

+ "A" => 1source
Genie.WebThreads.ClientIdType
UInt64 <: Unsigned

64-bit unsigned integer type.

source
Genie.WebThreads.ChannelNameType
String <: AbstractString

The default string type in Julia, used by e.g. string literals.

Strings are immutable sequences of Chars. A String is stored internally as a contiguous byte array, and while they are interpreted as being UTF-8 encoded, they can be composed of any byte sequence. Use isvalid to validate that the underlying byte sequence is valid as UTF-8.

source
Missing docstring.

Missing docstring for MessagePayload. Check Documenter's build log for details.

Genie.WebThreads.broadcastFunction

Pushes msg (and payload) to all the clients subscribed to the channels in channels.

source

Pushes msg (and payload) to all the clients subscribed to all the channels.

source
Missing docstring.

Missing docstring for channels. Check Documenter's build log for details.

Missing docstring.

Missing docstring for clients. Check Documenter's build log for details.

Missing docstring.

Missing docstring for connected_clients. Check Documenter's build log for details.

Missing docstring.

Missing docstring for disconnected_clients. Check Documenter's build log for details.

Genie.WebThreads.messageFunction

Pushes msg (and payload) to channel.

source

Writes msg to message queue for client.

source
Genie.WebThreads.pop_subscriptionFunction

Removes the subscription of client to channel.

source

Removes all subscriptions of client.

source
Missing docstring.

Missing docstring for pull. Check Documenter's build log for details.

Missing docstring.

Missing docstring for push. Check Documenter's build log for details.

Genie.WebThreads.push_subscriptionFunction

Adds a new subscription for client to channel.

source
Genie.WebThreads.subscribeFunction

Subscribes a web thread client wt to channel.

source
Missing docstring.

Missing docstring for subscriptions. Check Documenter's build log for details.

Missing docstring.

Missing docstring for timestamp_client. Check Documenter's build log for details.

Genie.WebThreads.unsubscribeFunction

Unsubscribes a web socket client wt from channel.

source
Genie.WebThreads.unsubscribe_clientFunction

Unsubscribes a web socket client wt from all the channels.

source
Missing docstring.

Missing docstring for unsubscribe_clients. Check Documenter's build log for details.

Genie.WebThreads.unsubscribe_disconnected_clientsFunction

unsubscribedisconnectedclients() :: ChannelClientsCollection

Unsubscribes clients which are no longer connected.

source
Missing docstring.

Missing docstring for webthreads. Check Documenter's build log for details.

diff --git a/dev/guides/Controlling_Load_Order_Of_Genie_Apps.html b/dev/guides/Controlling_Load_Order_Of_Genie_Apps.html index bea869636..291981345 100644 --- a/dev/guides/Controlling_Load_Order_Of_Genie_Apps.html +++ b/dev/guides/Controlling_Load_Order_Of_Genie_Apps.html @@ -13,4 +13,4 @@ -Foo.jl

where (-) means exclude the file from Genie's autoload sequence and (–) means remove file starting with (-) from Genie Load sequence. Now the load order of our directory is going to be

def.jl
 Abc.jl
 Aaa.jl
-Abb.jl
+Abb.jl diff --git a/dev/guides/Deploying_Genie_Apps_On_AWS.html b/dev/guides/Deploying_Genie_Apps_On_AWS.html index 3560a25a6..b80b9d217 100644 --- a/dev/guides/Deploying_Genie_Apps_On_AWS.html +++ b/dev/guides/Deploying_Genie_Apps_On_AWS.html @@ -9,4 +9,4 @@ # And clone your git repo git clone <your-repo.git> cd <your-repo>

Building a Genie Application

First of all let’s build our app using docker cli

docker build -t my-demo-app .
-docker images 

If build was successful your should be able to see docker image. Now let’s just start it. In our case application works on port 8000, so we did a port-mapping to port 80.

docker run -p 80:8000 my-demo-app

Then just grep a public IP from AWS console and open it in your browser.

deployed-app

That’s it, your application was successfully deployed. Thanks.

+docker images

If build was successful your should be able to see docker image. Now let’s just start it. In our case application works on port 8000, so we did a port-mapping to port 80.

docker run -p 80:8000 my-demo-app

Then just grep a public IP from AWS console and open it in your browser.

deployed-app

That’s it, your application was successfully deployed. Thanks.

diff --git a/dev/guides/Genie_Plugins.html b/dev/guides/Genie_Plugins.html index 383055876..283dd421c 100644 --- a/dev/guides/Genie_Plugins.html +++ b/dev/guides/Genie_Plugins.html @@ -65,4 +65,4 @@ isdir(f) || continue GeniePlugins.install(joinpath(src, f), dest, force = force) end -end

You can use it as a starting point - and add any other specific extra logic to it.

+end

You can use it as a starting point - and add any other specific extra logic to it.

diff --git a/dev/guides/Interactive_environment.html b/dev/guides/Interactive_environment.html index 5ebbec953..b2f11a87d 100644 --- a/dev/guides/Interactive_environment.html +++ b/dev/guides/Interactive_environment.html @@ -12,4 +12,4 @@ params(:x) + params(:y) end

By default, route params are extracted as SubString (more exactly, SubString{String}). If type constraints are added, Genie will attempt to convert the SubString to the indicated type.

For the above to work, we also need to tell Genie how to perform the conversion:

julia> Base.convert(::Type{Int}, s::AbstractString) = parse(Int, s)

Now if we access http://localhost:8000/sum/2/3 we should see 5

Handling query params

Query params, which look like ...?foo=bar&baz=2 are automatically unpacked by Genie and placed into the params collection. For example:

julia> route("/sum/:x::Int/:y::Int") do
          params(:x) + params(:y) + parse(Int, get(params, :initial_value, "0"))
-       end

Accessing http://localhost:8000/sum/2/3?initial_value=10 will now output 15.

+ end

Accessing http://localhost:8000/sum/2/3?initial_value=10 will now output 15.

diff --git a/dev/guides/Migrating_from_v4_to_v5.html b/dev/guides/Migrating_from_v4_to_v5.html index 9f3c08d32..4889e09d8 100644 --- a/dev/guides/Migrating_from_v4_to_v5.html +++ b/dev/guides/Migrating_from_v4_to_v5.html @@ -11,4 +11,4 @@ end

If your app does not use output_flash then you can just remove the ViewHelper file.

julia> rm("app/helpers/ViewHelper.jl")

4. All references to app resources (controllers and models)

In Genie v5, the app resources such as controllers and models were accessible directly in routes.jl and in any other resource. For instance, let's say that we have:

In v4 we could access them directly in routes.jl (or in other controllers and models) as using HomeController, Home.

However, in Genie v5 all the app's resources are scoped to the app's main module. So we need to update all references to the app resources to use the app's main module, meaning that in v5 we'll need using WelcomeHome.HomeController, WelcomeHome.Home.

We can also dynamically reference the app's main module by using ..Main.UserApp (so ..Main.UserApp is the same as WelcomeHome in our example).

5. Genie.Cache

In Genie v5, the Genie.Cache module has been moved to a dedicated plugin called GenieCache.jl. This means that all references to Genie.Cache need to be updated to use GenieCache.

This change was made to provide a leaner Genie core, making caching features opt-in. But also to allow the independent development of the caching features, independent from Genie itself.

6. Genie.Cache.FileCache

Starting with Genie 5, the file-based caching functionality provided by Genie.Cache.FileCache has been moved to a dedicated plugin called GenieCacheFileCache.jl. This means that all references to Genie.Cache.FileCache need to be updated to use GenieCacheFileCache. The GenieCacheFileCache plugin is dependent on the GenieCache package and it extends the functionality of GenieCache.

In the future, additional cache backends will be released.

7. Genie.Session

As mentioned above, the Genie.Session module has been moved to a dedicated plugin called GenieSession.jl. This means that all references to Genie.Session need to be updated to use GenieSession.

This change was made to provide a leaner Genie core, making session related features opt-in. But also to allow the independent development of the session features, independent from Genie itself.

8. Genie.Session.FileSession

Starting with Genie 5, the file-based session storage provided by Genie.Session.FileSession has been moved to a dedicated plugin called GenieSessionFileSession.jl. This means that all references to Genie.Session.FileSession need to be updated to use GenieSessionFileSession. The GenieSessionFileSession plugin is dependent on the GenieSession package and it extends the functionality of GenieSession.

In the future, additional session storage backends will be released.

9. Genie.Deploy

Similar to Genie.Session and Genie.Cache, the Genie.Deploy module has been moved to a dedicated plugin called GenieDeploy.jl. This means that all references to Genie.Deploy need to be updated to use GenieDeploy.

10. Genie.Deploy.Docker

Starting with Genie 5, the Docker deployment functionality provided by Genie.Deploy.Docker has been moved to a dedicated plugin called GenieDeployDocker.jl. This means that all references to Genie.Deploy.Docker need to be updated to use GenieDeployDocker. The GenieDeployDocker plugin is dependent on the GenieDeploy package and it extends the functionality of GenieDeploy.

11. Genie.Deploy.Heroku

Starting with Genie 5, the Heroku deployment functionality provided by Genie.Deploy.Heroku has been moved to a dedicated plugin called GenieDeployHeroku.jl. This means that all references to Genie.Deploy.Heroku need to be updated to use GenieDeployHeroku. The GenieDeployHeroku plugin is dependent on the GenieDeploy package and it extends the functionality of GenieDeploy.

12. Genie.Deploy.JuliaHub

Starting with Genie 5, the JuliaHub deployment functionality provided by Genie.Deploy.JuliaHub has been moved to a dedicated plugin called GenieDeployJuliaHub.jl. This means that all references to Genie.Deploy.JuliaHub need to be updated to use GenieDeployJuliaHub. The GenieDeployJuliaHub plugin is dependent on the GenieDeploy package and it extends the functionality of GenieDeploy.

13. Genie.App

The Genie.App module has been removed in v5 and its API has been moved to the Genie module.

14. Genie.AppServer

The Genie.AppServer module has been renamed to Genie.Server in v5.

15. Genie.Plugins

Starting with Genie 5, the Genie.Plugins functionality has been moved to a dedicated plugin called GeniePlugins.jl. This means that all references to Genie.Plugins need to be updated to use GeniePlugins. The GeniePlugins plugin is dependent on the Genie package and it extends the functionality of Genie.

16. Genie.new family of functions

All the Genie.new functions have been moved to Genie.Generator in v5.

17. No automatic import of Genie in Main (at REPL)

Genie v4 apps would automatically import Genie in Main, so that Genie would be readily available at the REPL. Starting with Genie 5, this is no longer the case and at the app's REPL it's now necessary to first run julia> using Genie.

18. Modification of bin/server and bin/server.bat

The scripts responsible for starting the app in non-interactive/serving mode need to be updated by replacing the end of the command from s "$@" to -s=true "$@".

19. Update config/initializers/logging.jl

Update the content of the logging.jl initializer to this:

import Genie
 
-Genie.Logger.initialize_logging()

20. Other

Genie 5 also changes or removes other APIs which can be generally be considered as internal. If you find other important breaking changes that have been missed, please open an issue on the Genie GitHub repository or just edit this file and submit a PR.

+Genie.Logger.initialize_logging()

20. Other

Genie 5 also changes or removes other APIs which can be generally be considered as internal. If you find other important breaking changes that have been missed, please open an issue on the Genie GitHub repository or just edit this file and submit a PR.

diff --git a/dev/guides/Simple_API_backend.html b/dev/guides/Simple_API_backend.html index c987f8fc7..decd5acc9 100644 --- a/dev/guides/Simple_API_backend.html +++ b/dev/guides/Simple_API_backend.html @@ -25,4 +25,4 @@ up(async = false)

Here we define two routes, /send and /echo. The send route makes a HTTP request over POST to /echo, sending a JSON payload with two values, message and repeat. In the /echo route, we grab the JSON payload using the Requests.jsonpayload() function, extract the values from the JSON object, and output the message value repeated for a number of times equal to the repeat value.

If you run the code, the output should be

{
   echo: "hello hello hello "
-}

If the payload contains invalid JSON, the jsonpayload will be set to nothing. You can still access the raw payload by using the Requests.rawpayload() function. You can also use rawpayload if for example the type of request/payload is not JSON.

+}

If the payload contains invalid JSON, the jsonpayload will be set to nothing. You can still access the raw payload by using the Requests.rawpayload() function. You can also use rawpayload if for example the type of request/payload is not JSON.

diff --git a/dev/guides/Working_With_Genie_Apps.html b/dev/guides/Working_With_Genie_Apps.html index 4a26d17a6..bc41487c9 100644 --- a/dev/guides/Working_With_Genie_Apps.html +++ b/dev/guides/Working_With_Genie_Apps.html @@ -370,4 +370,4 @@ "title": "Leonardo da Vinci" } ] -}

PRO TIP

SearchLight exposes two similar data persistence methods: save! and save. They both perform the same action (persisting the object to the database), but save will return a Bool of value true to indicate that the operation was successful or a Bool of value false to indicate that the operation has failed. While the save! variant will return the persisted object upon success or will throw an exception on failure.


Congratulations

You have successfully finished the first part of the step by step walkthrough - you have mastered the Genie basics, allowing you to set up a new app, register routes, add resources (controllers, models, and views), add database support, version the database schema with migrations, and execute basic queries with SearchLight!

In the next part we'll look at more advanced topics like handling forms and file uploads, templates rendering, interactivity and more.

+}

PRO TIP

SearchLight exposes two similar data persistence methods: save! and save. They both perform the same action (persisting the object to the database), but save will return a Bool of value true to indicate that the operation was successful or a Bool of value false to indicate that the operation has failed. While the save! variant will return the persisted object upon success or will throw an exception on failure.


Congratulations

You have successfully finished the first part of the step by step walkthrough - you have mastered the Genie basics, allowing you to set up a new app, register routes, add resources (controllers, models, and views), add database support, version the database schema with migrations, and execute basic queries with SearchLight!

In the next part we'll look at more advanced topics like handling forms and file uploads, templates rendering, interactivity and more.

diff --git a/dev/guides/Working_With_Genie_Apps_Intermediary_Topics.html b/dev/guides/Working_With_Genie_Apps_Intermediary_Topics.html index 030d00c60..7fa539a05 100644 --- a/dev/guides/Working_With_Genie_Apps_Intermediary_Topics.html +++ b/dev/guides/Working_With_Genie_Apps_Intermediary_Topics.html @@ -107,4 +107,4 @@ %> </ul>

Here we check if the cover property is not empty, and display the actual cover. Otherwise we show a placeholder image. You can check the result at http://localhost:8000/bgbooks

As for the JSON view, it already does what we want - you can check that the cover property is now outputted, as stored in the database: http://localhost:8000/api/v1/bgbooks

Success, we're done here!

Heads up!

In production you will have to make the upload code more robust - the big problem here is that we store the cover file as it comes from the user which can lead to name clashes and files being overwritten - not to mention security vulnerabilities. A more robust way would be to compute a hash based on author and title and rename the cover to that.

One more thing

So far so good, but what if we want to update the books we have already uploaded? It would be nice to add those missing covers. We need to add a bit of functionality to include editing features.

First things first - let's add the routes. Please add these two new route definitions to the routes.jl file:

route("/bgbooks/:id::Int/edit", BooksController.edit)
 route("/bgbooks/:id::Int/update", BooksController.update, method = POST, named = :update_book)

We defined two new routes. The first will display the book object in the form, for editing. While the second will take care of actually updating the database, server side. For both routes we need to pass the id of the book that we want to edit - and we want to constrain it to an Int. We express this as the /:id::Int/ part of the route.

We also want to:

OK, that's quite a list and this is where things become interesting. This is an important design pattern for CRUD web apps. In order to simplify the rendering of the form, we will always pass a book object into it. When editing a book it will be the book corresponding to the id passed into the route. And when creating a new book, it will be just an empty book object we'll create and then dispose of.

Using view partials

First, let's set up the views. In app/resources/books/views/ please create a new file called form.jl.html. Then, from app/resources/books/views/new.jl.html cut the <form> code. That is, everything between the opening and closing <form>...</form> tags. Paste it into the newly created form.jl.html file. Now, back to new.jl.html, instead of the previous <form>...</form> code add:

<% partial("app/resources/books/views/form.jl.html", context = @__MODULE__) %>

This line, as the partial function suggests, includes a view partial, which is a part of a view file, effectively including a view within another view. Notice that we're explicitly passing the context so Genie can set the correct variable scope when including the partial.

You can reload the new page to make sure that everything still works: http://localhost:8000/bgbooks/new

Now, let's add an Edit option to our list of books. Please go back to our list view file, billgatesbooks.jl.html. Here, for each iteration, within the for_each block we'll want to dynamically link to the edit page for the corresponding book.

for_each with view partials

However, this for_each which renders a Julia string is very ugly - and we now know how to refactor it, by using a view partial. Let's do it. First, replace the body of the for_each block:

<!-- app/resources/books/views/billgatesbooks.jl.html -->
-"""<li><img src='$( isempty(book.cover) ? "img/docs.png" : book.cover )' width="100px" /> $(book.title) by $(book.author)"""

with:

partial("app/resources/books/views/book.jl.html", book = book, context = @__MODULE__)

Notice that we are using the partial function and we pass the book object into our view, under the name book (will be accessible in book inside the view partial). Again, we're passing the scope's context (our controller object).

Next, create the book.jl.html in app/resources/books/views/, for example with

julia> touch("app/resources/books/views/book.jl.html")

Add this content to it: TO BE CONTINUED

+"""<li><img src='$( isempty(book.cover) ? "img/docs.png" : book.cover )' width="100px" /> $(book.title) by $(book.author)"""

with:

partial("app/resources/books/views/book.jl.html", book = book, context = @__MODULE__)

Notice that we are using the partial function and we pass the book object into our view, under the name book (will be accessible in book inside the view partial). Again, we're passing the scope's context (our controller object).

Next, create the book.jl.html in app/resources/books/views/, for example with

julia> touch("app/resources/books/views/book.jl.html")

Add this content to it: TO BE CONTINUED

diff --git a/dev/index.html b/dev/index.html index ace9a2c5f..437d77c92 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,2 +1,2 @@ -Home · Genie - The Highly Productive Julia Web Framework

Genie

Genie Logo

Genie

The highly productive Julia web framework

Genie is a full-stack MVC web framework that provides a streamlined and efficient workflow for developing modern web applications. It builds on Julia's strengths (high-level, high-performance, dynamic, JIT compiled), exposing a rich API and a powerful toolset for productive web development.

Current status

Genie is compatible with Julia v1.6 and up.


Documentation

https://genieframework.github.io/Genie.jl/dev/


Acknowledgements

  • Genie uses a multitude of packages that have been kindly contributed by the Julia community.
  • The awesome Genie logo was designed by Alvaro Casanova.
+Home · Genie - The Highly Productive Julia Web Framework

Genie

Genie Logo

Genie

The highly productive Julia web framework

Genie is a full-stack MVC web framework that provides a streamlined and efficient workflow for developing modern web applications. It builds on Julia's strengths (high-level, high-performance, dynamic, JIT compiled), exposing a rich API and a powerful toolset for productive web development.

Current status

Genie is compatible with Julia v1.6 and up.


Documentation

https://genieframework.github.io/Genie.jl/dev/


Acknowledgements

  • Genie uses a multitude of packages that have been kindly contributed by the Julia community.
  • The awesome Genie logo was designed by Alvaro Casanova.
diff --git a/dev/search.html b/dev/search.html index f1a5137b0..1eeff4e51 100644 --- a/dev/search.html +++ b/dev/search.html @@ -1,2 +1,2 @@ -Search · Genie - The Highly Productive Julia Web Framework

Loading search...

    +Search · Genie - The Highly Productive Julia Web Framework

    Loading search...

      diff --git a/dev/search_index.js b/dev/search_index.js index 6b4812c1c..dc03cabf2 100644 --- a/dev/search_index.js +++ b/dev/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#Working-With-Genie-Apps:-Intermediate-Topics","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"WARNING: THIS PAGE IS UNDER CONSTRUCTION – THE CONTENT IS USABLE BUT INCOMPLETE","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#Handling-forms","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Handling forms","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Now, the problem is that Bill Gates reads – a lot! It would be much easier if we would allow our users to add a few books themselves, to give us a hand. But since, obviously, we're not going to give them access to our Julia REPL, we should setup a web page with a form. Let's do it.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"We'll start by adding the new routes:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"# routes.jl\nroute(\"/bgbooks/new\", BooksController.new)\nroute(\"/bgbooks/create\", BooksController.create, method = POST, named = :create_book)","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"The first route will be used to display the page with the new book form. The second will be the target page for submitting our form - this page will accept the form's payload. Please note that it's configured to match POST requests and that we gave it a name. We'll use the name in our form so that Genie will dynamically generate the correct links to the corresponding URL (to avoid hard coding URLs). This way we'll make sure that our form will always submit to the right URL, even if we change the route (as long as we don't change the name).","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Now, to add the methods in BooksController. Add these definition under the billgatesbooks function (make sure you add them in BooksController, not in BooksController.API):","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"# BooksController.jl\nfunction new()\n html(:books, :new)\nend\n\nfunction create()\n # code here\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"The new method should be clear: we'll just render a view file called new. As for create, for now it's just a placeholder.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Next, to add our view. Add a blank file called new.jl.html in app/resources/books/views. Using Julia:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"julia> touch(\"app/resources/books/views/new.jl.html\")","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Make sure that it has this content:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"\n

      Add a new book recommended by Bill Gates

      \n

      \n For inspiration you can visit Bill Gates' website\n

      \n
      \n
      \n
      \n \n
      ","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Notice that the form's action calls the linkto method, passing in the name of the route to generate the URL, resulting in the following HTML:
      .","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"We should also update the BooksController.create method to do something useful with the form data. Let's make it create a new book, persist it to the database and redirect to the list of books. Here is the code:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"# BooksController.jl\nusing Genie.Router, Genie.Renderer\n\nfunction create()\n Book(title = params(:book_title), author = params(:book_author)) |> save && redirect(:get_bgbooks)\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"A few things are worth pointing out in this snippet:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"again, we're accessing the params collection to extract the request data, in this case passing in the names of our","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"form's inputs as parameters.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"We need to bring Genie.Router into scope in order to access params;","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"we're using the redirect method to perform a HTTP redirect. As the argument we're passing in the name of the route,","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"just like we did with the form's action. However, we didn't set any route to use this name. It turns out that Genie gives default names to all the routes.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"We can use these – but a word of notice: these names are generated using the properties of the route, so if the route changes it's possible that the name will change too. So either make sure your route stays unchanged – or explicitly name your routes. The autogenerated name, get_bgbooks corresponds to the method (GET) and the route (bgbooks).","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"In order to get info about the defined routes you can use the Router.named_routes function:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"julia> Router.named_routes()\nOrderedCollections.OrderedDict{Symbol, Genie.Router.Route} with 6 entries:\n :get_bgbooks => [GET] /bgbooks => billgatesbooks | :get_bgbooks\n :get_bgbooks_new => [GET] /bgbooks/new => new | :get_bgbooks_new\n :get => [GET] / => () | :get\n :get_api_v1_bgbooks => [GET] /api/v1/bgbooks | :get_api_v1_bgbooks\n :create_book => [POST] /bgbooks/create | :create_book","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Let's try it out. Input something and submit the form. If everything goes well a new book will be persisted to the database – and it will be added at the bottom of the list of books.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#Uploading-files","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Uploading files","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Our app looks great – but the list of books would be so much better if we'd display the covers as well. Let's do it!","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#Modify-the-database","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Modify the database","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"The first thing we need to do is to modify our table to add a new column, for storing a reference to the name of the cover image. Per best practices, we'll use database migrations to modify the structure of our table:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"julia> SearchLight.Generator.newmigration(\"add cover column\")\n[debug] New table migration created at db/migrations/2019030813344258_add_cover_column.jl","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Now we need to edit the migration file - please make it look like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"# db/migrations/*_add_cover_column.jl\nmodule AddCoverColumn\n\nimport SearchLight.Migrations: add_column, add_index\n\n# SQLite does not support column removal so the `remove_column` method is not implemented in the SearchLightSQLite adapter\n# If using SQLite leave the next line commented -- otherwise uncomment it\n# import SearchLight.Migrations: remove_column\n\nfunction up()\n add_column(:books, :cover, :string)\nend\n\nfunction down()\n # if using the SQLite backend, leave the next line commented -- otherwise uncomment it\n # remove_column(:books, :cover)\nend\n\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Looking good - lets ask SearchLight to run it:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"julia> SearchLight.Migration.lastup()\n[debug] Executed migration AddCoverColumn up","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"If you want to double check, ask SearchLight for the migrations status:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"julia> SearchLight.Migration.status()\n\n| | Module name & status |\n| | File name |\n|---|----------------------------------------|\n| | CreateTableBooks: UP |\n| 1 | 2018100120160530_create_table_books.jl |\n| | AddCoverColumn: UP |\n| 2 | 2019030813344258_add_cover_column.jl |","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Perfect! Now we need to add the new column as a field to the Books.Book model:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"module Books\n\nusing SearchLight, SearchLight.Validation, BooksValidator\n\nexport Book\n\nBase.@kwdef mutable struct Book <: AbstractModel\n id::DbId = DbId()\n title::String = \"\"\n author::String = \"\"\n cover::String = \"\"\nend\n\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"As a quick test we can extend our JSON view and see that all goes well - make it look like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"# app/resources/books/views/billgatesbooks.json.jl\n\"Bill's Gates list of recommended books\" => [Dict(\"author\" => b.author,\n \"title\" => b.title,\n \"cover\" => b.cover) for b in books]","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"If we navigate http://localhost:8000/api/v1/bgbooks you should see the newly added \"cover\" property (empty, but present).","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#Heads-up!","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Heads up!","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Sometimes Julia/Genie/Revise fails to update structs on changes. If you get an error saying that Book does not have a cover field or that it can not be changed, you'll need to restart the Genie app.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#File-uploading","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"File uploading","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Next step, extending our form to upload images (book covers). Please edit the new.jl.html view file as follows:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"

      Add a new book recommended by Bill Gates

      \n

      \n For inspiration you can visit Bill Gates' website\n

      \n\n
      \n
      \n
      \n \n
      ","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"The new bits are:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"we added a new attribute to our
      tag: enctype=\"multipart/form-data\". This is required in order to support files payloads.\nthere's a new input of type file: ","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"You can see the updated form by visiting http://localhost:8000/bgbooks/new","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Now, time to add a new book, with the cover! How about \"Identity\" by Francis Fukuyama? Sounds good. You can use whatever image you want for the cover, or maybe borrow the one from Bill Gates, I hope he won't mind https://www.gatesnotes.com/-/media/Images/GoodReadsBookCovers/Identity.png. Just download the file to your computer so you can upload it through our form.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Almost there - now to add the logic for handling the uploaded file server side. Please update the BooksController.create method to look like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"# BooksController\nfunction create()\n cover_path = if haskey(filespayload(), \"book_cover\")\n path = joinpath(\"img\", \"covers\", filespayload(\"book_cover\").name)\n write(joinpath(\"public\", path), IOBuffer(filespayload(\"book_cover\").data))\n\n path\n else\n \"\"\n end\n\n Book( title = params(:book_title),\n author = params(:book_author),\n cover = cover_path) |> save && redirect(:get_bgbooks)\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Also, very important, you need to make sure that BooksController is using Genie.Requests.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Regarding the code, there's nothing very fancy about it. First we check if the files payload contains an entry for our book_cover input. If yes, we compute the path where we want to store the file, write the file, and store the path in the database.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Please make sure that you create the folder covers/ within public/img/.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Great, now let's display the images. Let's start with the HTML view - please edit app/resources/books/views/billgatesbooks.jl.html and make sure it has the following content:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"\n

      Bill's Gates top $( length(books) ) recommended books

      \n","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Here we check if the cover property is not empty, and display the actual cover. Otherwise we show a placeholder image. You can check the result at http://localhost:8000/bgbooks","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"As for the JSON view, it already does what we want - you can check that the cover property is now outputted, as stored in the database: http://localhost:8000/api/v1/bgbooks","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Success, we're done here!","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#Heads-up!-2","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Heads up!","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"In production you will have to make the upload code more robust - the big problem here is that we store the cover file as it comes from the user which can lead to name clashes and files being overwritten - not to mention security vulnerabilities. A more robust way would be to compute a hash based on author and title and rename the cover to that.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#One-more-thing","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"One more thing","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"So far so good, but what if we want to update the books we have already uploaded? It would be nice to add those missing covers. We need to add a bit of functionality to include editing features.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"First things first - let's add the routes. Please add these two new route definitions to the routes.jl file:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"route(\"/bgbooks/:id::Int/edit\", BooksController.edit)\nroute(\"/bgbooks/:id::Int/update\", BooksController.update, method = POST, named = :update_book)","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"We defined two new routes. The first will display the book object in the form, for editing. While the second will take care of actually updating the database, server side. For both routes we need to pass the id of the book that we want to edit - and we want to constrain it to an Int. We express this as the /:id::Int/ part of the route.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"We also want to:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"reuse the form which we have defined in app/resources/books/views/new.jl.html\nmake the form aware of whether it's used to create a new book, or for editing an existing one respond accordingly by setting the correct action\npre-fill the inputs with the book's info when editing a book.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"OK, that's quite a list and this is where things become interesting. This is an important design pattern for CRUD web apps. In order to simplify the rendering of the form, we will always pass a book object into it. When editing a book it will be the book corresponding to the id passed into the route. And when creating a new book, it will be just an empty book object we'll create and then dispose of.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#Using-view-partials","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Using view partials","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"First, let's set up the views. In app/resources/books/views/ please create a new file called form.jl.html. Then, from app/resources/books/views/new.jl.html cut the code. That is, everything between the opening and closing ...
      tags. Paste it into the newly created form.jl.html file. Now, back to new.jl.html, instead of the previous
      ...
      code add:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"<% partial(\"app/resources/books/views/form.jl.html\", context = @__MODULE__) %>","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"This line, as the partial function suggests, includes a view partial, which is a part of a view file, effectively including a view within another view. Notice that we're explicitly passing the context so Genie can set the correct variable scope when including the partial.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"You can reload the new page to make sure that everything still works: http://localhost:8000/bgbooks/new","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Now, let's add an Edit option to our list of books. Please go back to our list view file, billgatesbooks.jl.html. Here, for each iteration, within the for_each block we'll want to dynamically link to the edit page for the corresponding book.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#for_each-with-view-partials","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"for_each with view partials","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"However, this for_each which renders a Julia string is very ugly - and we now know how to refactor it, by using a view partial. Let's do it. First, replace the body of the for_each block:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"\n\"\"\"
    • $(book.title) by $(book.author)\"\"\"","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"with:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"partial(\"app/resources/books/views/book.jl.html\", book = book, context = @__MODULE__)","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Notice that we are using the partial function and we pass the book object into our view, under the name book (will be accessible in book inside the view partial). Again, we're passing the scope's context (our controller object).","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Next, create the book.jl.html in app/resources/books/views/, for example with","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"julia> touch(\"app/resources/books/views/book.jl.html\")","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Add this content to it: TO BE CONTINUED","category":"page"},{"location":"guides/Interactive_environment.html#Using-Genie-in-an-interactive-environment-(Jupyter/IJulia,-REPL,-etc)","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment (Jupyter/IJulia, REPL, etc)","text":"","category":"section"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"Genie can be used for ad-hoc exploratory programming, to quickly whip up a web server and expose your Julia functions.","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"Once you have Genie into scope, you can define a new route. A route maps a URL to a function.","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"julia> using Genie\n\njulia> route(\"/\") do\n \"Hi there!\"\n end","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"You can now start the web server using","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"julia> up()","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"Finally, now navigate to http://localhost:8000 – you should see the message \"Hi there!\".","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"We can define more complex URIs which can also map to previously defined functions:","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"julia> function hello_world()\n \"Hello World!\"\n end\njulia> route(\"/hello/world\", hello_world)","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"The route handler functions can be defined anywhere (in any other file or module) as long as they are accessible in the current scope.","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"You can now visit http://localhost:8000/hello/world in the browser.","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"We can access route params that are defined as part of the URL, like :message in the following example:","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"julia> route(\"/echo/:message\") do\n params(:message)\n end","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"Accessing http://localhost:8000/echo/ciao should echo \"ciao\".","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"And we can even match route params by types (and automatically convert them to the correct type):","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"julia> route(\"/sum/:x::Int/:y::Int\") do\n params(:x) + params(:y)\n end","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"By default, route params are extracted as SubString (more exactly, SubString{String}). If type constraints are added, Genie will attempt to convert the SubString to the indicated type.","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"For the above to work, we also need to tell Genie how to perform the conversion:","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"julia> Base.convert(::Type{Int}, s::AbstractString) = parse(Int, s)","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"Now if we access http://localhost:8000/sum/2/3 we should see 5","category":"page"},{"location":"guides/Interactive_environment.html#Handling-query-params","page":"Using Genie in an interactive environment","title":"Handling query params","text":"","category":"section"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"Query params, which look like ...?foo=bar&baz=2 are automatically unpacked by Genie and placed into the params collection. For example:","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"julia> route(\"/sum/:x::Int/:y::Int\") do\n params(:x) + params(:y) + parse(Int, get(params, :initial_value, \"0\"))\n end","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"Accessing http://localhost:8000/sum/2/3?initial_value=10 will now output 15.","category":"page"},{"location":"API/server.html","page":"Server","title":"Server","text":"CurrentModule = Server","category":"page"},{"location":"API/server.html","page":"Server","title":"Server","text":"SERVERS\nServersCollection\ndown\ndown!\nhandle_request\nhandle_ws_request\nisrunning\nopenbrowser\nprint_server_status\nserve\nserver_status\nsetup_http_listener\nsetup_http_streamer\nsetup_ws_handler\nup\nupdate_config","category":"page"},{"location":"API/server.html#Genie.Server.SERVERS","page":"Server","title":"Genie.Server.SERVERS","text":"SERVERS\n\nServersCollection constant containing references to the current app's web and websockets servers.\n\n\n\n\n\n","category":"constant"},{"location":"API/server.html#Genie.Server.ServersCollection","page":"Server","title":"Genie.Server.ServersCollection","text":"ServersCollection(webserver::Union{Task,Nothing}, websockets::Union{Task,Nothing})\n\nRepresents a object containing references to Genie's web and websockets servers.\n\n\n\n\n\n","category":"type"},{"location":"API/server.html#Genie.Server.down","page":"Server","title":"Genie.Server.down","text":"down(; webserver::Bool = true, websockets::Bool = true) :: ServersCollection\n\nShuts down the servers optionally indicating which of the webserver and websockets servers to be stopped. It does not remove the servers from the SERVERS collection. Returns the collection.\n\n\n\n\n\n","category":"function"},{"location":"API/server.html#Genie.Server.down!","page":"Server","title":"Genie.Server.down!","text":"function down!(; webserver::Bool = true, websockets::Bool = true) :: Vector{ServersCollection}\n\nShuts down all the servers and empties the SERVERS collection. Returns the empty collection.\n\n\n\n\n\n","category":"function"},{"location":"API/server.html#Genie.Server.handle_request","page":"Server","title":"Genie.Server.handle_request","text":"handle_request(req::HTTP.Request, res::HTTP.Response) :: HTTP.Response\n\nHttp server handler function - invoked when the server gets a request.\n\n\n\n\n\n","category":"function"},{"location":"API/server.html#Genie.Server.handle_ws_request","page":"Server","title":"Genie.Server.handle_ws_request","text":"handle_ws_request(req::HTTP.Request, msg::String, ws_client) :: String\n\nHttp server handler function - invoked when the server gets a request.\n\n\n\n\n\n","category":"function"},{"location":"API/server.html#Genie.Server.serve","page":"Server","title":"Genie.Server.serve","text":"serve(path::String = pwd(), params...; kwparams...)\n\nServes a folder of static files located at path. Allows Genie to be used as a static files web server. The params and kwparams arguments are forwarded to Genie.up().\n\nArguments\n\npath::String: the folder of static files to be served by the server\nparams: additional arguments which are passed to Genie.up to control the web server\nkwparams: additional keyword arguments which are passed to Genie.up to control the web server\n\nExamples\n\njulia> Genie.serve(\"public\", 8888, async = false, verbose = true)\n[ Info: Ready!\n2019-08-06 16:39:20:DEBUG:Main: Web Server starting at http://127.0.0.1:8888\n[ Info: Listening on: 127.0.0.1:8888\n[ Info: Accept (1): 🔗 0↑ 0↓ 1s 127.0.0.1:8888:8888 ≣16\n\n\n\n\n\n","category":"function"},{"location":"API/server.html#Genie.Server.setup_http_listener","page":"Server","title":"Genie.Server.setup_http_listener","text":"setup_http_listener(req::HTTP.Request, res::HTTP.Response = HTTP.Response()) :: HTTP.Response\n\nConfigures the handler for the HTTP Request and handles errors.\n\n\n\n\n\n","category":"function"},{"location":"API/server.html#Genie.Server.setup_ws_handler","page":"Server","title":"Genie.Server.setup_ws_handler","text":"setup_ws_handler(stream::HTTP.Stream, ws_client) :: Nothing\n\nConfigures the handler for WebSockets requests.\n\n\n\n\n\n","category":"function"},{"location":"API/server.html#Genie.Server.up","page":"Server","title":"Genie.Server.up","text":"up(port::Int = Genie.config.server_port, host::String = Genie.config.server_host;\n ws_port::Int = Genie.config.websockets_port, async::Bool = ! Genie.config.run_as_server) :: ServersCollection\n\nStarts the web server.\n\nArguments\n\nport::Int: the port used by the web server\nhost::String: the host used by the web server\nws_port::Int: the port used by the Web Sockets server\nasync::Bool: run the web server task asynchronously\n\nExamples\n\njulia> up(8000, \"127.0.0.1\", async = false)\n[ Info: Ready!\nWeb Server starting at http://127.0.0.1:8000\n\n\n\n\n\n","category":"function"},{"location":"API/server.html#Genie.Server.update_config","page":"Server","title":"Genie.Server.update_config","text":"update_config(port::Int, host::String, ws_port::Int) :: Nothing\n\nUpdates the corresponding Genie configurations to the corresponding values for port, host, and ws_port, if these are passed as arguments when starting up the server.\n\n\n\n\n\n","category":"function"},{"location":"tutorials/14--The_Secrets_File.html#The-secrets-(config/secrets.jl)-file","page":"The secrets file","title":"The secrets (config/secrets.jl) file","text":"","category":"section"},{"location":"tutorials/14--The_Secrets_File.html","page":"The secrets file","title":"The secrets file","text":"Confidential configuration data (like API keys, usernames, passwords, etc) should be added to the config/secrets.jl file. This file is by default added to .gitignore when creating a Genie app, so it won't be added to source control – to avoid that it is accidentally exposed.","category":"page"},{"location":"tutorials/14--The_Secrets_File.html#Scope","page":"The secrets file","title":"Scope","text":"","category":"section"},{"location":"tutorials/14--The_Secrets_File.html","page":"The secrets file","title":"The secrets file","text":"All the definitions (variables, constants, functions, modules, etc) added to the secrets.jl file are loaded into your app's module. So if your app (and its main module) is called MyGenieApp, the definitions will be available under the MyGenieApp namespace.","category":"page"},{"location":"tutorials/14--The_Secrets_File.html","page":"The secrets file","title":"The secrets file","text":"","category":"page"},{"location":"tutorials/14--The_Secrets_File.html","page":"The secrets file","title":"The secrets file","text":"HEADS UP","category":"page"},{"location":"tutorials/14--The_Secrets_File.html","page":"The secrets file","title":"The secrets file","text":"Given the your app's name is variable, you can also access your app's module through the Main.UserApp constant. So all the definitions added to secrets.jl can also be accessed through the Mani.UserApp module.","category":"page"},{"location":"tutorials/14--The_Secrets_File.html","page":"The secrets file","title":"The secrets file","text":"","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html#How-to-deploy-Genie-applications","page":"Deploying Genie Apps On AWS","title":"How to deploy Genie applications","text":"","category":"section"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"In this guide we will walk through how to deploy a Genie App to Amazon Cloud. This article will be added with more information in future.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html#Prerequisite:","page":"Deploying Genie Apps On AWS","title":"Prerequisite:","text":"","category":"section"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Genie Application with ready Dockerfile(Refer to Genie Docker Tutorial)\nOpened account in main cloud providers","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"That’s it, from this point we can start with deployment.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html#AWS","page":"Deploying Genie Apps On AWS","title":"AWS","text":"","category":"section"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html#Networking","page":"Deploying Genie Apps On AWS","title":"Networking","text":"","category":"section"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"The easiest and quickest to deploy and expose Genie applications is to create a EC2 instance and run docker container inside it.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"After opening an AWS console you should be able to see your network under VPC tab. This is default one, and we’ll use it for hosting our EC2 instance.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"(Image: network-vpc)","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"This VPC already contains a predefined subnets. We need a public subnet as we should access our Application via Internet.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"(Image: subnet)","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Great, let’s move on.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html#Setup-EC2-instance","page":"Deploying Genie Apps On AWS","title":"Setup EC2 instance","text":"","category":"section"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Navigate to EC2 tab in AWS Console and then click on “Launch instances“. ","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"(Image: launch-instance)","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"We will use a Free Tier Amazon Linux 2 AMI image. It can fit our needs for this demo","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"(Image: free-tier)","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"For an Instance type lets choose t2.small, in that case we’ll get 1vCPU and 2Gb Memory. We should have enough memory with this instance to meet our application requirements. Also we should allow HTTP & HTTPS access. For SSH let’s specify your IP or let 0.0.0.0/0 with is allows connect to this instance from everywhere (not secure). Also double check that “Auto-assign public IP“ is enabled.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"(Image: instance-conf)","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Click on \"Launch instance\". It’ll take around 2min and we can proceed.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"(Image: launch-instance)","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Ok, now our Instance is Running and have a public IP.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html#Setup-Docker-on-EC2","page":"Deploying Genie Apps On AWS","title":"Setup Docker on EC2","text":"","category":"section"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Now let’s connect to is and install a docker. Here is the command to connect via SSH. Please change this IP by yours.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"ssh -i my-demo-app.pem ec2-user@13.xx.xxx.x63","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Let’s Install Docker on it, it’s pretty easy, just execute next commands:","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"sudo yum update\nsudo yum install docker -y\nsudo usermod -a -G docker ec2-user\nid ec2-user\nnewgrp docker\nsudo systemctl enable docker.service\nsudo systemctl start docker.service","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Now we can verify that our docker was installed and runs successfully","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"sudo systemctl status docker.service","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"For more information you can check this guide. Amazing, basically that’s it, now we can deploy our application.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Let’s clone repository with our applications and Dockerfile to EC2. If git is not installed on EC2 just execute","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"sudo yum git install -y\n# And clone your git repo\ngit clone \ncd ","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html#Building-a-Genie-Application","page":"Deploying Genie Apps On AWS","title":"Building a Genie Application","text":"","category":"section"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"First of all let’s build our app using docker cli","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"docker build -t my-demo-app .\ndocker images ","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"If build was successful your should be able to see docker image. Now let’s just start it. In our case application works on port 8000, so we did a port-mapping to port 80.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"docker run -p 80:8000 my-demo-app","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Then just grep a public IP from AWS console and open it in your browser.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"(Image: deployed-app)","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"That’s it, your application was successfully deployed. Thanks.","category":"page"},{"location":"API/logger.html","page":"Logger","title":"Logger","text":"CurrentModule = Logger","category":"page"},{"location":"API/logger.html","page":"Logger","title":"Logger","text":"initialize_logging\ntimestamp_logger","category":"page"},{"location":"API/webthreads.html","page":"WebThreads","title":"WebThreads","text":"CurrentModule = WebThreads","category":"page"},{"location":"API/webthreads.html","page":"WebThreads","title":"WebThreads","text":"CLIENTS\nMESSAGE_QUEUE\nSUBSCRIPTIONS\nChannelClient\nChannelClientsCollection\nChannelMessage\nChannelSubscriptionsCollection\nClientId\nChannelName\nMessagePayload\nbroadcast\nchannels\nclients\nconnected_clients\ndisconnected_clients\nmessage\npop_subscription\npull\npush\npush_subscription\nsubscribe\nsubscriptions\ntimestamp_client\nunsubscribe\nunsubscribe_client\nunsubscribe_clients\nunsubscribe_disconnected_clients\nwebthreads","category":"page"},{"location":"API/webthreads.html#Genie.WebThreads.ChannelClientsCollection","page":"WebThreads","title":"Genie.WebThreads.ChannelClientsCollection","text":"Dict([itr])\n\nDict{K,V}() constructs a hash table with keys of type K and values of type V. Keys are compared with isequal and hashed with hash.\n\nGiven a single iterable argument, constructs a Dict whose key-value pairs are taken from 2-tuples (key,value) generated by the argument.\n\nExamples\n\njulia> Dict([(\"A\", 1), (\"B\", 2)])\nDict{String, Int64} with 2 entries:\n \"B\" => 2\n \"A\" => 1\n\nAlternatively, a sequence of pair arguments may be passed.\n\njulia> Dict(\"A\"=>1, \"B\"=>2)\nDict{String, Int64} with 2 entries:\n \"B\" => 2\n \"A\" => 1\n\n\n\n\n\n","category":"type"},{"location":"API/webthreads.html#Genie.WebThreads.ChannelSubscriptionsCollection","page":"WebThreads","title":"Genie.WebThreads.ChannelSubscriptionsCollection","text":"Dict([itr])\n\nDict{K,V}() constructs a hash table with keys of type K and values of type V. Keys are compared with isequal and hashed with hash.\n\nGiven a single iterable argument, constructs a Dict whose key-value pairs are taken from 2-tuples (key,value) generated by the argument.\n\nExamples\n\njulia> Dict([(\"A\", 1), (\"B\", 2)])\nDict{String, Int64} with 2 entries:\n \"B\" => 2\n \"A\" => 1\n\nAlternatively, a sequence of pair arguments may be passed.\n\njulia> Dict(\"A\"=>1, \"B\"=>2)\nDict{String, Int64} with 2 entries:\n \"B\" => 2\n \"A\" => 1\n\n\n\n\n\n","category":"type"},{"location":"API/webthreads.html#Genie.WebThreads.ClientId","page":"WebThreads","title":"Genie.WebThreads.ClientId","text":"UInt64 <: Unsigned\n\n64-bit unsigned integer type.\n\n\n\n\n\n","category":"type"},{"location":"API/webthreads.html#Genie.WebThreads.ChannelName","page":"WebThreads","title":"Genie.WebThreads.ChannelName","text":"String <: AbstractString\n\nThe default string type in Julia, used by e.g. string literals.\n\nStrings are immutable sequences of Chars. A String is stored internally as a contiguous byte array, and while they are interpreted as being UTF-8 encoded, they can be composed of any byte sequence. Use isvalid to validate that the underlying byte sequence is valid as UTF-8.\n\n\n\n\n\n","category":"type"},{"location":"API/webthreads.html#Genie.WebThreads.broadcast","page":"WebThreads","title":"Genie.WebThreads.broadcast","text":"Pushes msg (and payload) to all the clients subscribed to the channels in channels.\n\n\n\n\n\nPushes msg (and payload) to all the clients subscribed to all the channels.\n\n\n\n\n\n","category":"function"},{"location":"API/webthreads.html#Genie.WebThreads.message","page":"WebThreads","title":"Genie.WebThreads.message","text":"Pushes msg (and payload) to channel.\n\n\n\n\n\nWrites msg to message queue for client.\n\n\n\n\n\n","category":"function"},{"location":"API/webthreads.html#Genie.WebThreads.pop_subscription","page":"WebThreads","title":"Genie.WebThreads.pop_subscription","text":"Removes the subscription of client to channel.\n\n\n\n\n\nRemoves all subscriptions of client.\n\n\n\n\n\n","category":"function"},{"location":"API/webthreads.html#Genie.WebThreads.push_subscription","page":"WebThreads","title":"Genie.WebThreads.push_subscription","text":"Adds a new subscription for client to channel.\n\n\n\n\n\n","category":"function"},{"location":"API/webthreads.html#Genie.WebThreads.subscribe","page":"WebThreads","title":"Genie.WebThreads.subscribe","text":"Subscribes a web thread client wt to channel.\n\n\n\n\n\n","category":"function"},{"location":"API/webthreads.html#Genie.WebThreads.unsubscribe","page":"WebThreads","title":"Genie.WebThreads.unsubscribe","text":"Unsubscribes a web socket client wt from channel.\n\n\n\n\n\n","category":"function"},{"location":"API/webthreads.html#Genie.WebThreads.unsubscribe_client","page":"WebThreads","title":"Genie.WebThreads.unsubscribe_client","text":"Unsubscribes a web socket client wt from all the channels.\n\n\n\n\n\n","category":"function"},{"location":"API/webthreads.html#Genie.WebThreads.unsubscribe_disconnected_clients","page":"WebThreads","title":"Genie.WebThreads.unsubscribe_disconnected_clients","text":"unsubscribedisconnectedclients() :: ChannelClientsCollection\n\nUnsubscribes clients which are no longer connected.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-json.html","page":"JSON Renderer","title":"JSON Renderer","text":"CurrentModule = Renderer.Json","category":"page"},{"location":"API/renderer-json.html","page":"JSON Renderer","title":"JSON Renderer","text":"render\nGenie.Renderer.render\njson\nGenie.Router.error","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html#Developing-Genie-Web-Services","page":"Creating a web service","title":"Developing Genie Web Services","text":"","category":"section"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"Starting up ad-hoc web servers at the REPL and writing small scripts to wrap micro-services works great, but production apps tend to become complex quickly. They also have more stringent requirements, like managing dependencies, compressing assets, reloading code, logging, environments, or structuring the codebase in a way which promotes efficient workflows when working in teams.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"Genie enables a modular approach towards app building, allowing to add more components as the need arises. You can start with the web service template (which includes dependencies management, logging, environments, and routing), and grow it by sequentially adding DB persistence (through the SearchLight ORM), high performance HTML view templates with embedded Julia (via Renderer.Html), caching, authentication, and more.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html#Setting-up-a-Genie-Web-Service-project","page":"Creating a web service","title":"Setting up a Genie Web Service project","text":"","category":"section"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"Genie packs handy generator features and templates which help bootstrapping and setting up various parts of an application. These are available in the Genie.Generator module. For bootstrapping a new app we need to invoke one of the functions in the newapp family:","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"julia> using Genie\n\njulia> Genie.Generator.newapp_webservice(\"MyGenieApp\")","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"If you follow the log messages in the REPL you will see that the command will trigger a flurry of actions in order to set up the new project:","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"it creates a new folder, MyGenieApp/, which will hosts the files of the app and whose name corresponds to the name of the app,\nwithin the MyGenieApp/ folder, it creates the files and folders needed by the app,\nchanges the active directory to MyGenieApp/ and creates a new Julia project within it (adding the Project.toml file),\ninstalls all the required dependencies for the new Genie app (using Pkg and the standard Manifest.toml file), and finally,\nstarts the web server","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"TIP","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"Check out the ?help documentation for Genie.Generator.newapp, Genie.Generator.newapp_webservice, Genie.Generator.newapp_mvc, and Genie.Generator.newapp_fullstack too see what options are available for bootstrapping applications. We'll go over the different configurations in upcoming sections.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html#The-file-structure","page":"Creating a web service","title":"The file structure","text":"","category":"section"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"Our newly created web service has this file structure:","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"├── .gitattributes\n├── .gitignore\n├── Manifest.toml\n├── Project.toml\n├── bin\n├── bootstrap.jl\n├── config\n├── public\n├── routes.jl\n├── src\n└── test","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"These are the roles of each of the files and folders:","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"Manifest.toml and Project.toml are used by Julia and Pkg to manage the app's dependencies.\nbin/ includes scripts for starting up a Genie REPL or a Genie server.\nbootstrap.jl and the files within src/ are used by Genie to load the application and should not be modified unless you know what you're doing.\nconfig/ includes the per-environment configuration files.\npublic/ is the document root, which includes static files exposed by the app on the network/internet.\nroutes.jl is the dedicated file for registering Genie routes.\nthe test/ folder is set up to store the unit and integration tests for the app.\n.gitattributes and .gitignore are used by Git to manage the project's files.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"HEADS UP","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"After creating a new app you might need to change the file permissions to allow editing/saving the files such as routes.jl.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html#Adding-logic","page":"Creating a web service","title":"Adding logic","text":"","category":"section"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"You can now edit the routes.jl file to add some logic, at the bottom of the file:","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"route(\"/hello\") do\n \"Welcome to Genie!\"\nend","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"If you now visit http://127.0.0.1:8000/hello you'll see a warm greeting.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html#Extending-the-app","page":"Creating a web service","title":"Extending the app","text":"","category":"section"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"Genie apps are just plain Julia projects. This means that routes.jl will behave like any other Julia script - you can reference extra packages, you can switch into pkg> mode to manage per project dependencies, include other files, etcetera.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"If you have existing Julia code that you want to quickly load into a Genie app, you can add a lib/ folder in the root of the app and place your Julia files there. If the folder exists, lib/ and all its subfolders are automatically loaded by Genie, recursively.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"WARNING","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"If you add the lib/ folder while the Genie app is running, you will need to restart the app to load the files.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"If you need to add database support, you can always add the SearchLight ORM by running julia> Genie.Generator.db_support() in the app's REPL.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"However, if your app grows in complexity and you develop it from scratch, it is more efficient to take advantage of Genie's resource-oriented MVC structure.","category":"page"},{"location":"tutorials/15--The_Lib_Folder.html#The-lib/-folder","page":"Auto-loading user libraries","title":"The lib/ folder","text":"","category":"section"},{"location":"tutorials/15--The_Lib_Folder.html","page":"Auto-loading user libraries","title":"Auto-loading user libraries","text":"Genie makes it easy to automatically load Julia code (modules, files, etc) into an app, outside of the standard Genie MVC app structure. You simply need to add your files and folders into the lib/ folder.","category":"page"},{"location":"tutorials/15--The_Lib_Folder.html","page":"Auto-loading user libraries","title":"Auto-loading user libraries","text":"","category":"page"},{"location":"tutorials/15--The_Lib_Folder.html","page":"Auto-loading user libraries","title":"Auto-loading user libraries","text":"HEADS UP","category":"page"},{"location":"tutorials/15--The_Lib_Folder.html","page":"Auto-loading user libraries","title":"Auto-loading user libraries","text":"If the lib/ folder does not exist, just create it yourself: julia> mkdir(\"lib\")\nGenie includes the files placed within the lib/ folder and subfolders recursively\nFiles within lib/ are loaded using Revise and are automatically reloaded if changed.","category":"page"},{"location":"tutorials/15--The_Lib_Folder.html","page":"Auto-loading user libraries","title":"Auto-loading user libraries","text":"","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Advanced-routing-techniques","page":"Advanced routing","title":"Advanced routing techniques","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Genie's router can be considered the brain of the app, matching web requests to handler functions, extracting and setting up the request's variables and the execution environment, and invoking the response methods.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Static-routing","page":"Advanced routing","title":"Static routing","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Starting with the simplest case, we can register \"plain\" routes by using the route method. The method takes as its required arguments the URI string or pattern – and the handler function that will be invoked in order to provide the response. The router supports two ways of registering routes, either route(pattern::String, f::Function) or route(f::Function, pattern::String). The first syntax is for passing function references – while the second is for defining inline functions (lambdas).","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Example","page":"Advanced routing","title":"Example","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"The following snippet defines a function greet which returns the \"Welcome to Genie!\" string. We use the function as our route handler, by passing a reference to it as the second argument to the route method.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"using Genie\n\ngreet() = \"Welcome to Genie!\"\n\nroute(\"/greet\", greet) # [GET] /greet => greet\n\nup() # start the server","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"If you use your browser to navigate to http://127.0.0.1:8000/greet you'll see the code in action.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"However, defining a dedicated handler function might be overkill for simple cases like this. As such, Genie allows registering in-line handlers:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"route(\"/bye\") do\n \"Good bye!\"\nend # [GET] /bye => ()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"You can just navigate to http://127.0.0.1:8000/bye – the route is instantly available in the app.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"HEADS UP","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"The routes are matched from newest to oldest. This means that you can define a new route to overwrite a previously defined one.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Genie's router won't match the most specific rule, but the first matching one. So if, for example, you register a route to match /*, it will handle all the requests, even if you have previously defined more specific routes. As a side-note, you can use this technique to temporarily divert all users to a maintenance page (which you can later remove by deleting the route using Router.delete!(:route_name)).","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Dynamic-routing-(using-route-parameters)","page":"Advanced routing","title":"Dynamic routing (using route parameters)","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Static routing works great for fixed URLs. But what if we have dynamic URLs, where the components map to information in the backend (like database IDs) and vary with each request? For example, how would we handle a URL like \"/customers/57943/orders/458230\", where 57943 is the customer id and 458230 is the order id.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Such situations are handled through dynamic routing or route parameters. For the previous example, \"/customers/57943/orders/458230\", we can define a dynamic route as \"/customers/:customer_id/orders/:order_id\". Upon matching the request, the Router will unpack the values and expose them in the params collection.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Example-2","page":"Advanced routing","title":"Example","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"using Genie, Genie.Requests\n\nroute(\"/customers/:customer_id/orders/:order_id\") do\n \"You asked for the order $(payload(:order_id)) for customer $(payload(:customer_id))\"\nend\n\nup()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Routing-methods-(GET,-POST,-PUT,-PATCH,-DELETE,-OPTIONS)","page":"Advanced routing","title":"Routing methods (GET, POST, PUT, PATCH, DELETE, OPTIONS)","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"By default, routes handle GET requests, since these are the most common. In order to define routes for handling other types of request methods, we need to pass the method keyword argument, indicating the HTTP method we want to respond to. Genie's Router supports GET, POST, PUT, PATCH, DELETE, OPTIONS methods.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"The router defines and exports constants for each of these as Router.GET, Router.POST, Router.PUT, Router.PATCH, Router.DELETE, and Router.OPTIONS.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Example-3","page":"Advanced routing","title":"Example","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"We can setup the following PATCH route:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"using Genie, Genie.Requests\n\nroute(\"/patch_stuff\", method = PATCH) do\n \"Stuff to patch\"\nend\n\nup()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"And we can test it using the HTTP package:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"using HTTP\n\nHTTP.request(\"PATCH\", \"http://127.0.0.1:8000/patch_stuff\").body |> String","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"This will output the string \"Stuff to patch\", as the response from the PATCH request. By sending a request with the PATCH method, our route is triggered. Consequently, we access the response body and convert it to a string, which is \"Stuff to patch\".","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Named-routes","page":"Advanced routing","title":"Named routes","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Genie allows tagging routes with names. This is a very powerful feature, to be used in conjunction with the Router.tolink method, for dynamically generating URLs to various the routes. The advantage of this technique is that if we refer to the route by name and generate the links dynamically using tolink, as long as the name of the route stays the same, if we change the route pattern, all the URLs will automatically match the new route definition.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"In order to name a route we need to use the named keyword argument, which expects a Symbol.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Example-4","page":"Advanced routing","title":"Example","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"using Genie, Genie.Requests\n\nroute(\"/customers/:customer_id/orders/:order_id\", named = :get_customer_order) do\n \"Looking up order $(payload(:order_id)) for customer $(payload(:customer_id))\"\nend\n# [GET] /customers/:customer_id/orders/:order_id => ()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"We can check the status of our route with:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"julia> routes()\n :get_customer_order => [GET] /customers/:customer_id/orders/:order_id => ()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"HEADS UP","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"For consistency, Genie names all the routes. However, the auto-generated name is state dependent. So if you change the definition of the route, it's possible that the name will change as well. Thus, it's best to explicitly name the routes if you plan on referencing them throughout the app.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"We can confirm this by adding an anonymous route:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"route(\"/foo\") do\n \"foo\"\nend\n# [GET] /foo => ()\n\njulia> routes()\n :get_customer_order => [GET] /customers/:customer_id/orders/:order_id => ()\n :get_foo => [GET] /foo => ()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"The new route has been automatically named get_foo, based on the method and URI pattern.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Links-to-routes","page":"Advanced routing","title":"Links to routes","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"We can use the name of the route to link back to it using the linkto method.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Example-5","page":"Advanced routing","title":"Example","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Let's start with the previously defined two routes:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"julia> routes()\n :get_customer_order => [GET] /customers/:customer_id/orders/:order_id => ()\n :get_foo => [GET] /foo => ()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Static routes such as :get_foo are straightforward to target:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"julia> linkto(:get_foo)\n\"/foo\"","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"For dynamic routes, it's a bit more involved as we need to supply the values for each of the parameters, as keyword arguments:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"julia> linkto(:get_customer_order, customer_id = 1234, order_id = 5678)","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"This will generate the URL \"/customers/1234/orders/5678\"","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"The linkto function should be used in conjunction with the HTML code for generating links, ie:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Foo","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Listing-routes","page":"Advanced routing","title":"Listing routes","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"At any time we can check which routes are registered with Router.routes:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"julia> routes()\n [GET] /foo => getfield(Main, Symbol(\"##7#8\"))()\n [GET] /customers/:customer_id/orders/:order_id => ()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#The-Route-type","page":"Advanced routing","title":"The Route type","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"The routes are represented internally by the Route type which has the following fields:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"method::String - for storing the method of the route (GET, POST, etc)\npath::String - represents the URI pattern to be matched against\naction::Function - the route handler to be executed when the route is matched\nname::Union{Symbol,Nothing} - the name of the route\ncontext::Module - an optional context to be used when executing the route handler","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Removing-routes","page":"Advanced routing","title":"Removing routes","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"We can delete routes from the stack by calling the delete! method and passing the collection of routes and the name of the route to be removed. The method returns the collection of (remaining) routes","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Example-6","page":"Advanced routing","title":"Example","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"julia> routes()\n :get_customer_order => [GET] /customers/:customer_id/orders/:order_id => ()\n :get_foo => [GET] /foo => ()\n\njulia> Router.delete!(:get_foo)\n :get_customer_order => [GET] /customers/:customer_id/orders/:order_id => ()\n\njulia> routes()\n :get_customer_order => [GET] /customers/:customer_id/orders/:order_id => ()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Matching-routes-by-type-of-arguments","page":"Advanced routing","title":"Matching routes by type of arguments","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"By default route parameters are parsed into the payload collection as SubString{String}:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"using Genie, Genie.Requests\n\nroute(\"/customers/:customer_id/orders/:order_id\") do\n \"Order ID has type $(payload(:order_id) |> typeof) // Customer ID has type $(payload(:customer_id) |> typeof)\"\nend","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"This will output Order ID has type SubString{String} // Customer ID has type SubString{String}","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"However, for such a case, we'd very much prefer to receive our data as Int to avoid an explicit conversion – and to match only numbers. Genie supports such a workflow by allowing type annotations to route parameters:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"route(\"/customers/:customer_id::Int/orders/:order_id::Int\", named = :get_customer_order) do\n \"Order ID has type $(payload(:order_id) |> typeof) // Customer ID has type $(payload(:customer_id) |> typeof)\"\nend\n# [GET] /customers/:customer_id::Int/orders/:order_id::Int => ()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Notice how we've added type annotations to :customer_id and :order_id in the form :customer_id::Int and :order_id::Int.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"However, attempting to access the URL http://127.0.0.1:8000/customers/10/orders/20 will fail:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Failed to match URI params between Int64::DataType and 10::SubString{String}\nMethodError(convert, (Int64, \"10\"), 0x00000000000063fe)\n/customers/10/orders/20 404","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"As you can see, Genie attempts to convert the types from the default SubString{String} to Int – but doesn't know how. It fails, can't find other matching routes and returns a 404 Not Found response.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Type-conversion-in-routes","page":"Advanced routing","title":"Type conversion in routes","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"The error is easy to address though: we need to provide a type converter from SubString{String} to Int.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Base.convert(::Type{Int}, v::SubString{String}) = parse(Int, v)","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Once we register the converter our request will be correctly handled, resulting in Order ID has type Int64 // Customer ID has type Int64","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Matching-individual-URI-segments","page":"Advanced routing","title":"Matching individual URI segments","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Besides matching the full route, Genie also allows matching individual URI segments. That is, enforcing that the various route parameters obey a certain pattern. In order to introduce constraints for route parameters we append #pattern at the end of the route parameter.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Example-7","page":"Advanced routing","title":"Example","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"For instance, let's assume that we want to implement a localized website where we have a URL structure like: mywebsite.com/en, mywebsite.com/es and mywebsite.com/de. We can define a dynamic route and extract the locale variable to serve localized content:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"route(\":locale\", TranslationsController.index)","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"This will work very well, matching requests and passing the locale into our code within the payload(:locale) variable. However, it will also be too greedy, virtually matching all the requests, including things like static files (ie mywebsite.com/favicon.ico). We can constrain what the :locale variable can match, by appending the pattern (a regex pattern):","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"route(\":locale#(en|es|de)\", TranslationsController.index)","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"The refactored route only allows :locale to match one of en, es, and de strings.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"HEADS UP","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Keep in mind not to duplicate application logic. For instance, if you have an array of supported locales, you can use that to dynamically generate the pattern – routes can be fully dynamically generated!","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"const LOCALE = \":locale#($(join(TranslationsController.AVAILABLE_LOCALES, '|')))\"\n\nroute(\"/$LOCALE\", TranslationsController.index, named = :get_index)","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#The-params-collection","page":"Advanced routing","title":"The params collection","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"It's good to know that the router bundles all the parameters of the current request into the params collection (a Dict{Symbol,Any}). This contains valuable information, such as route parameters, query params, POST payload, the original HTTP.Request and HTTP.Response objects, etcetera. In general it's recommended not to access the params collection directly but through the utility methods defined by Genie.Requests and Genie.Responses – but knowing about params might come in handy for advanced users.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Working-with-Genie-apps-(projects)","page":"Working with Genie Apps","title":"Working with Genie apps (projects)","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Working with Genie in an interactive environment can be useful – but usually we want to persist the application and reuse it between sessions. One way to achieve this is to save it as an IJulia notebook and rerun the cells.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"However, you can get the best of Genie by working with Genie apps. A Genie app is a MVC (Model-View-Controller) web application which promotes the convention-over-configuration principle. By working with a few predefined files, within the Genie app structure, the framework can lift a lot of weight and massively improve development productivity. By following Genie's workflow, one instantly gets, out of the box, features like automatic module loading and reloading, dedicated configuration files, logging, support for environments, code generators, caching, support for Genie plugins, and much more.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"In order to create a new Genie app, we need to run Genie.Generator.newapp($app_name):","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> Genie.Generator.newapp(\"MyGenieApp\")","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Upon executing the command, Genie will:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"make a new dir called MyGenieApp and cd() into it,\ninstall all the app's dependencies,\ncreate a new Julia project (adding the Project.toml and Manifest.toml files),\nactivate the project,\nautomatically load the new app's environment into the REPL,\nstart the web server on the default Genie port (port 8000) and host (127.0.0.1 – aka localhost).","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"At this point you can confirm that everything worked as expected by visiting http://127.0.0.1:8000 in your favourite web browser. You should see Genie's welcome page.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Next, let's add a new route. Routes are used to map request URLs to Julia functions. These functions (also called \"handler\" functions) provide the response that will be sent back to the client. Routes are meant to be defined in the dedicated routes.jl file (but there is no restriction enforcing this rule). Open MyGenieApp/routes.jl in your editor or run the following command (making sure that you are in the app's directory):","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> edit(\"routes.jl\")","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Append this at the bottom of the routes.jl file and save it:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# routes.jl\nroute(\"/hello\") do\n \"Welcome to Genie!\"\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"We are using the route method, passing in the \"/hello\" URL and an anonymous function which returns the string \"Welcome to Genie!\". What this means is that for each request to the \"/hello\" URL, our app will invoke the route handler function and will respond with the welcome message.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Visit http://127.0.0.1:8000/hello for a warm welcome!","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Working-with-resources","page":"Working with Genie Apps","title":"Working with resources","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Adding our code to the routes.jl file works great for small projects, where you want to quickly publish features on the web. But for larger projects we're better off using Genie's MVC structure (MVC stands for Model-View-Controller). By employing the Model-View-Controller design pattern we can break our code into modules with clear responsibilities: the Model is used for data access, the View renders the response to the client, and the Controller orchestrates the interactions between Models and Views and handles requests. Modular code is easier to write, test and maintain.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"A Genie app can be architected around the concept of \"resources\". A resource represents a business entity (something like a user, or a product, or an account) and maps to a bundle of files (controller, model, views, etc). Resources live under the app/resources/ folder and each resource has its own dedicated folder, where all of its files are hosted. For example, if we have a web app about \"books\", a \"books\" folder would be found at app/resources/books and will contain all the files for publishing books on the web (usually called BooksController.jl for the controller, Books.jl for the model, BooksValidator.jl for the model validator – as well as a views folder for hosting all the view files necessary for rendering books data).","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"HEADS UP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"When creating a default Genie app, the app/ folder might be missing. It will be automatically created the first time you add a resource via Genie's generators.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Using-Controllers","page":"Working with Genie Apps","title":"Using Controllers","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Controllers are used to orchestrate interactions between client requests, Models (which handle data access), and Views (which are responsible for rendering the responses which will be sent to the clients' web browsers). In a standard workflow, a route points to a method in the controller – which is charged with building and sending the response over the network, back to the client.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Let's add a \"books\" controller. Genie comes with handy generators and one of them is for creating new controllers:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Generate-the-Controller","page":"Working with Genie Apps","title":"Generate the Controller","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Let's generate our BooksController:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> Genie.Generator.newcontroller(\"Books\")\n[info]: New controller created at ./app/resources/books/BooksController.jl","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Great! Let's edit BooksController.jl (julia> edit(\"./app/resources/books/BooksController.jl\")) and add something to it. For example, a function which returns some of Bill Gates' recommended books would be nice. Make sure that BooksController.jl looks like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# app/resources/books/BooksController.jl\nmodule BooksController\n\nstruct Book\n title::String\n author::String\nend\n\nconst BillGatesBooks = Book[\n Book(\"The Best We Could Do\", \"Thi Bui\"),\n Book(\"Evicted: Poverty and Profit in the American City\", \"Matthew Desmond\"),\n Book(\"Believe Me: A Memoir of Love, Death, and Jazz Chickens\", \"Eddie Izzard\"),\n Book(\"The Sympathizer\", \"Viet Thanh Nguyen\"),\n Book(\"Energy and Civilization, A History\", \"Vaclav Smil\")\n]\n\nfunction billgatesbooks()\n \"\n

      Bill Gates' list of recommended books

      \n
        \n $([\"
      • $(book.title) by $(book.author)
      • \" for book in BillGatesBooks]...)\n
      \n \"\nend\n\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Our controller is just a plain Julia module where we define a Book struct and set up an array of book objects. We then define a function, billgatesbooks, which returns an HTML string, with a H1 heading and an unordered list of all the books. We used an array comprehension to iterate over each book and render it in a
    • element. The elements of the array are then concatenated using the splat ... operator.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The plan is to map this function to a route and expose it on the internet.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Checkpoint","page":"Working with Genie Apps","title":"Checkpoint","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Before exposing it on the web, we can test the function in the REPL:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> using MyGenieApp.BooksController\n\njulia> BooksController.billgatesbooks()","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The output of the function call should be a HTML string which looks pretty much like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"\"

      Bill Gates' list of recommended books

      \"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Please make sure that it works as expected.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Setup-the-route","page":"Working with Genie Apps","title":"Setup the route","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now, let's expose our billgatesbooks method on the web. We need to add a new route which points to it. Add these to the routes.jl file:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# routes.jl\nusing Genie\nusing MyGenieApp.BooksController\n\nroute(\"/bgbooks\", BooksController.billgatesbooks)","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"In the snippet we declared that we're using MyGenieApp.BooksController. Do notice that Genie automatically included the module as there was no need to explicitly include the file – and in addition Genie will reload the source code every time we change it. Next we defined a route mapping the /bgbooks URL and the BooksController.billgatesbooks function (we say that the BooksController.billgatesbooks is the route handler for the /bgbooks URL or endpoint).","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"That's all! If you now visit http://localhost:8000/bgbooks you'll see Bill Gates' list of recommended books (well, at least some of them, the man reads a lot!).","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"PRO TIP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If you would rather work with Julia instead of wrangling HTML strings, you can use Genie's Renderer.Html API. It provides functions which map every standard HTML element. For instance, the BooksController.billgatesbooks function can be written as follows, as an array of HTML elements:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"using Genie.Renderer.Html\n\nfunction billgatesbooks()\n [\n h1() do\n \"Bill Gates' list of recommended books\"\n end\n ul() do\n for_each(BillGatesBooks) do book\n li() do\n book.title * \" by \" * book.author\n end\n end\n end\n ]\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The for_each function iterates over a collection of elements and concatenates the output of each loop into the result of the loop. We'll talk about it more soon.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Adding-views","page":"Working with Genie Apps","title":"Adding views","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"However, putting HTML into the controllers is a bad idea: HTML should stay in the dedicated view files and contain as little logic as possible. Let's refactor our code to use views instead.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The views used for rendering a resource should be placed inside the views/ folder, within that resource's own folder structure. So in our case, we will add an app/resources/books/views/ folder. Just go ahead and do it, Genie does not provide a generator for this task:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> mkdir(joinpath(\"app\", \"resources\", \"books\", \"views\"))","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"We created the views/ folder in app/resources/books/. We provided the full path as our REPL is running in the the root folder of the app. Also, we use the joinpath function so that Julia creates the path in a cross-platform way.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Naming-views","page":"Working with Genie Apps","title":"Naming views","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Usually each controller method will have its own rendering logic – hence, its own view file. Thus, it's a good practice to name the view files just like the methods, so that we can keep track of where they're used.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"At the moment, Genie supports HTML and Markdown view files, as well as plain Julia. Their type is identified by file extension so that's an important part. The HTML views use a .jl.html extension while the Markdown files go with .jl.md and the Julia ones by .jl.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#HTML-views","page":"Working with Genie Apps","title":"HTML views","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"All right then, let's add our first view file for the BooksController.billgatesbooks method. Let's create an HTML view file. With Julia:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> touch(joinpath(\"app\", \"resources\", \"books\", \"views\", \"billgatesbooks.jl.html\"))","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Genie supports a special type of dynamic HTML view, where we can embed Julia code. These are high performance compiled views. They are not parsed as strings: instead, the HTML is converted to a Julia function that uses native Julia rendering code and is cached to the file system and loaded like any other Julia file. Hence, the first time you load a view, or after you change one, you might notice a certain delay – it's the time needed to generate, compile and load the view. On next runs (especially in production) it's going to be blazing fast!","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"In case you're curious, the auto-generated Julia view functions are stored by default in the build/ folder in the app. Feel free to take a look. The build/ folder can be safely deleted, Genie will create it back as needed. The location of the build/ folder is configurable and can be changed from Genie's config.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now all we need to do is to move the HTML code out of the controller and into the view, improving it a bit to also show a count of the number of books. Edit the view file as follows (julia> edit(\"app/resources/books/views/billgatesbooks.jl.html\")):","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"\n

      Bill Gates' top $(length(books)) recommended books

      \n","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"As you can see, it's just plain HTML with embedded Julia. We can add Julia code by using the <% ... %> code block tags – these should be used for more complex, multiline expressions. Or by using plain Julia string interpolation with $(...) – for simple values outputting.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"To make HTML generation more efficient, Genie provides a series of helpers, like the above for_each macro which allows iterating over a collection, passing the current item into the processing function.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Rendering-views","page":"Working with Genie Apps","title":"Rendering views","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"We now need to refactor our controller to use the view, passing in the expected variables. We will use the html method which renders and outputs the response as HTML. Update the definition of the billgatesbooks function to be as follows:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# BooksController.jl\nusing Genie.Renderer.Html\n\nfunction billgatesbooks()\n html(:books, :billgatesbooks, books = BillGatesBooks)\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"First, notice that we needed to add Genie.Renderer.Html as a dependency, to get access to the html method. As for the html method itself, it takes as its arguments the name of the resource, the name of the view file, and a list of keyword arguments representing view variables:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":":books is the name of the resource (which effectively indicates in which views folder Genie should look for the","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"view file – in our case app/resources/books/views);","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":":billgatesbooks is the name of the view file. We don't need to pass the extension, Genie will figure it out since","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"there's only one file with this name;","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"and finally, we pass the values we want to expose in the view, as keyword arguments.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"That's it – our refactored app should be ready! You can try it out for yourself at http://localhost:8000/bgbooks","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Markdown-views","page":"Working with Genie Apps","title":"Markdown views","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Markdown views work similar to HTML views – employing the same embedded Julia functionality. Here is how you can add a Markdown view for our billgatesbooks function.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"First, create the corresponding view file, using the .jl.md extension. Maybe with:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> touch(joinpath(\"app\", \"resources\", \"books\", \"views\", \"billgatesbooks.jl.md\"))","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now edit the file and make sure it looks like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"\n# Bill Gates' $(length(books)) recommended books\n\n$(\n for_each(books) do book\n \"* $(book.title) by $(book.author) \\n\"\n end\n)","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Notice that Markdown views do not support Genie's HTML embedded Julia tags <% ... %>. Only string interpolation $(...) is accepted, but it works across multiple lines.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If you reload the page now, however, Genie will still load the HTML view. The reason is that, if we have only one view file, Genie will manage. But if there's more than one, the framework won't know which one to pick. It won't error out but will pick the default one, which is the HTML version.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"It's a simple change in the BookiesController: we have to explicitly tell Genie which file to load, extension and all:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# BooksController.jl\nfunction billgatesbooks()\n html(:books, \"billgatesbooks.jl.md\", books = BillGatesBooks)\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Taking-advantage-of-layouts","page":"Working with Genie Apps","title":"Taking advantage of layouts","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Genie's views are rendered within a layout file. Layouts are meant to render the theme of the website, or the \"frame\" around the view – the elements which are common on all the pages. The layout file can include visible elements, like the main menu or the footer. But also maybe the tag or the assets tags ( and \n \n","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"We can edit it. For example, add this right under the opening tag, just above the <% tag:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"

      Welcome to top books

      ","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If you reload the page at http://localhost:8000/bgbooks you will see the new heading.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"But we don't have to stick to the default; we can add additional layouts. Let's suppose that we have, for example, an admin area which should have a completely different theme. We can add a dedicated layout for that:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> touch(joinpath(\"app\", \"layouts\", \"admin.jl.html\"))","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now edit it (julia> edit(\"app/layouts/admin.jl.html\")) and make it look like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"\n\n\n \n Genie Admin\n \n \n

      Books admin

      \n <%\n @yield\n %>\n \n","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If we want to apply it, we must instruct our BooksController to use it. The html function takes a keyword argument named layout, for the layout file. Update the billgatesbooks function to look like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# BooksController.jl\nfunction billgatesbooks()\n html(:books, :billgatesbooks, books = BillGatesBooks, layout = :admin)\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Reload the page and you'll see the new heading.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#The-@yield-instruction","page":"Working with Genie Apps","title":"The @yield instruction","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"There is a special instruction in the layouts: @yield. It outputs the contents of the view as rendered through the controller. So where this macro is present, Genie will output the HTML resulting from rendering the view by executing the route handler function within the controller.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Using-view-paths","page":"Working with Genie Apps","title":"Using view paths","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"For very simple applications the MVC and the resource-centric approaches might involve too much boilerplate. In such cases, we can simplify the code by referencing the view (and layout) by file path, ex:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# BooksController.jl\nusing Genie.Renderer\n\nfunction billgatesbooks()\n html(path\"app/resources/books/views/billgatesbooks.jl.html\", books = BillGatesBooks, layout = path\"app/layouts/app.jl.html\")\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Rendering-JSON-views","page":"Working with Genie Apps","title":"Rendering JSON views","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"A common use case for web apps is to serve as backends for RESTful APIs. For such cases, JSON is the preferred data format. You'll be happy to hear that Genie has built-in support for JSON responses. Let's add an endpoint for our API – which will render Bill Gates' books as JSON.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"We can start in the routes.jl file, by appending this","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"route(\"/api/v1/bgbooks\", BooksController.API.billgatesbooks)","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Next, in BooksController.jl, append the extra logic at the end of the file, before the closing end. The whole file should look like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# BooksController.jl\nmodule BooksController\n\nusing Genie.Renderer.Html\n\nstruct Book\n title::String\n author::String\nend\n\nconst BillGatesBooks = Book[\n Book(\"The Best We Could Do\", \"Thi Bui\"),\n Book(\"Evicted: Poverty and Profit in the American City\", \"Matthew Desmond\"),\n Book(\"Believe Me: A Memoir of Love, Death, and Jazz Chickens\", \"Eddie Izzard\"),\n Book(\"The Sympathizer!\", \"Viet Thanh Nguyen\"),\n Book(\"Energy and Civilization, A History\", \"Vaclav Smil\")\n]\n\nfunction billgatesbooks()\n html(:books, :billgatesbooks, layout = :admin, books = BillGatesBooks)\nend\n\n\nmodule API\n\nusing ..BooksController\nusing Genie.Renderer.Json\n\nfunction billgatesbooks()\n json(BooksController.BillGatesBooks)\nend\n\nend\n\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"We nested an API module within the BooksController module, where we defined another billgatesbooks function which outputs a JSON.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If you go to http://localhost:8000/api/v1/bgbooks it should already work as expected.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#JSON-views","page":"Working with Genie Apps","title":"JSON views","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"However, we have just committed one of the cardinal sins of API development. We have just forever coupled our internal data structure to its external representation. This will make future refactoring very complicated and error prone as any changes in the data will break the client's integrations. The solution is to, again, use views, to fully control how we render our data – and decouple the data structure from its rendering on the web.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Genie has support for JSON views – these are plain Julia files which have the \".json.jl\" extension. Let's add one in our views/ folder:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> touch(joinpath(\"app\", \"resources\", \"books\", \"views\", \"billgatesbooks.json.jl\"))","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"We can now create a proper response. Put this in the view file:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# app/resources/books/views/billgatesbooks.json.jl\n\"Bill Gates' list of recommended books\" => books","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Final step, instructing BooksController to render the view. Simply replace the existing billgatesbooks function within the API sub-module with the following:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"function billgatesbooks()\n json(:books, :billgatesbooks, books = BooksController.BillGatesBooks)\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"This should hold no surprises – the json function is similar to the html one we've seen before. So now we're rendering a custom JSON response. That's all – everything should work!","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"HEADS UP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Why-JSON-views-have-the-extension-ending-in-.jl-but-HTML-and-Markdown-views-do-not?","page":"Working with Genie Apps","title":"Why JSON views have the extension ending in .jl but HTML and Markdown views do not?","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Good question! The extension of the views is chosen in order to preserve correct syntax highlighting in the IDE/code editor.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Since practically HTML and Markdown views are HTML and Markdown files with some embedded Julia code, we want to use the HTML or Markdown syntax highlighting. For JSON views, we use pure Julia, so we want Julia syntax highlighting.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Accessing-databases-with-SearchLight-models","page":"Working with Genie Apps","title":"Accessing databases with SearchLight models","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"You can get the most out of Genie by pairing it with its seamless ORM layer, SearchLight. SearchLight, a native Julia ORM, provides excellent support for working with relational databases. The Genie + SearchLight combo can be used to productively develop CRUD (Create-Read-Update-Delete) apps.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"HEADS UP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"CRUD stands for Create-Read-Update-Delete and describes the data workflow in many web apps, where resources are created, read (listed), updated, and deleted.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"SearchLight represents the \"M\" part in Genie's MVC architecture (thus, the Model layer).","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Let's begin by adding SearchLight to our Genie app. All Genie apps manage their dependencies in their own Julia environment, through their Project.toml and Manifest.toml files.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"So we need to make sure that we're in pkg> mode first (which is entered by typing ] in julian mode, ie: julia>]). The cursor should change to (MyGenieApp) pkg>.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Next, we add SearchLight:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"(MyGenieApp) pkg> add SearchLight","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Adding-a-database-adapter","page":"Working with Genie Apps","title":"Adding a database adapter","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"SearchLight provides a database agnostic API for working with various backends (at the moment, MySQL, SQLite, Postgres and Oracle). Thus, we also need to add the specific adapter. To keep things simple, let's use SQLite for our app. Hence, we'll need the SearchLightSQLite package:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"(MyGenieApp) pkg> add SearchLightSQLite","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Setup-the-database-connection","page":"Working with Genie Apps","title":"Setup the database connection","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Genie is designed to seamlessly integrate with SearchLight and provides access to various database oriented generators. First we need to tell Genie/SearchLight how to connect to the database. Let's use them to set up our database support. Run this in the Genie/Julia REPL:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> Genie.Generator.db_support()","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The command will add a db/ folder within the root of the app. What we're looking for is the db/connection.yml file which tells SearchLight how to connect to the database. Let's edit it. Make the file look like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"env: ENV[\"GENIE_ENV\"]\n\ndev:\n adapter: SQLite\n database: db/books.sqlite\n config:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"This instructs SearchLight to run in the environment of the current Genie app (by default dev), using SQLite for the adapter (backend) and a database stored at db/books.sqlite (the database will be created automatically if it does not exist). We could pass extra configuration options in the config object, but for now we don't need anything else.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"HEADS UP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If you are using a different adapter, make sure that the database configured already exists and that the configured user can successfully access it – SearchLight will not attempt to create the database.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now we can ask SearchLight to load it up:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> using SearchLight\n\njulia> SearchLight.Configuration.load()\nDict{String,Any} with 4 entries:\n \"options\" => Dict{String,String}()\n \"config\" => nothing\n \"database\" => \"db/books.sqlite\"\n \"adapter\" => \"SQLite\"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Let's just go ahead and try it out by connecting to the DB:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> using SearchLightSQLite\n\njulia> SearchLight.Configuration.load() |> SearchLight.connect","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"This should return a SQLite.DB(\"db/books.sqlite\") object. If that is the case, the connection succeeded and we got back a SQLite database handle.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"PRO TIP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Each database adapter exposes a CONNECTIONS collection where we can access the connection:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> SearchLightSQLite.CONNECTIONS\n1-element Array{SQLite.DB,1}:\n SQLite.DB(\"db/books.sqlite\")","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Awesome! If all went well you should have a books.sqlite database in the db/ folder.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"shell> tree db\ndb\n├── books.sqlite\n├── connection.yml\n├── migrations\n└── seeds","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Managing-the-database-schema-with-SearchLight-migrations","page":"Working with Genie Apps","title":"Managing the database schema with SearchLight migrations","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Database migrations provide a way to reliably, consistently and repeatedly apply (and undo) changes to the structure of your database (known as \"schema transformations\"). They are specialised scripts for adding, removing and altering DB tables – these scripts are placed under version control and are managed by a dedicated system which knows which scripts have been run and which not, and is able to run them in the correct order.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"SearchLight needs its own DB table to keep track of the state of the migrations so let's set it up:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> SearchLight.Migrations.init()\n[ Info: Created table schema_migrations","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"This command sets up our database with the needed table in order to manage migrations.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"PRO TIP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"You can use the SearchLight API to execute random queries against the database backend. For example we can confirm that the table is really there:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> SearchLight.query(\"SELECT name FROM sqlite_master WHERE type ='table' AND name NOT LIKE 'sqlite_%'\")\n┌ Info: SELECT name FROM sqlite_master WHERE type ='table' AND name NOT LIKE 'sqlite_%'\n└\n\n1×1 DataFrames.DataFrame\n│ Row │ name │\n│ │ String⍰ │\n├─────┼───────────────────┤\n│ 1 │ schema_migrations │","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The result is a DataFrame object.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Creating-our-Book-model","page":"Working with Genie Apps","title":"Creating our Book model","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"SearchLight, just like Genie, uses the convention-over-configuration design pattern. It prefers for things to be setup in a certain way and provides sensible defaults, versus having to define everything in extensive configuration files. And fortunately, we don't even have to remember what these conventions are, as SearchLight also comes with an extensive set of generators.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Lets ask SearchLight to create a new model:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> SearchLight.Generator.newresource(\"Book\")\n\n[ Info: New model created at /Users/adrian/Dropbox/Projects/MyGenieApp/app/resources/books/Books.jl\n[ Info: New table migration created at /Users/adrian/Dropbox/Projects/MyGenieApp/db/migrations/2020020909574048_create_table_books.jl\n[ Info: New validator created at /Users/adrian/Dropbox/Projects/MyGenieApp/app/resources/books/BooksValidator.jl\n[ Info: New unit test created at /Users/adrian/Dropbox/Projects/MyGenieApp/test/books_test.jl","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"SearchLight has created the Books.jl model, the *_create_table_books.jl migration file, the BooksValidator.jl model validator and the books_test.jl test file.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"HEADS UP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The first part of the migration file will be different for you!","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The *_create_table_books.jl file will be named differently as the first part of the name is the file creation timestamp. This timestamp part guarantees that names are unique and file name clashes are avoided (for example when working as a team a creating similar migration files).","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Writing-the-table-migration","page":"Working with Genie Apps","title":"Writing the table migration","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Lets begin by writing the migration to create our books table. SearchLight provides a powerful DSL for writing migrations. Each migration file needs to define two methods: up which applies the changes – and down which undoes the effects of the up method. So in our up method we want to create the table – and in down we want to drop the table.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The naming convention for tables in SearchLight is that the table name should be pluralized (books) – because a table contains multiple books (and each row represents an object, a single book). But don't worry, the migration file should already be pre-populated with the correct table name.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Edit the db/migrations/*_create_table_books.jl file and make it look like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"module CreateTableBooks\n\nimport SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table\n\nfunction up()\n create_table(:books) do\n [\n primary_key()\n column(:title, :string, limit = 100)\n column(:author, :string, limit = 100)\n ]\n end\n\n add_index(:books, :title)\n add_index(:books, :author)\nend\n\nfunction down()\n drop_table(:books)\nend\n\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The code is pretty easy to follow: in the up function we call create_table and pass an array of columns: a primary key, a title column and an author column (both strings have a max length of 100). We also add two indices (one on the title and the other on the author columns). As for the down method, it invokes the drop_table function to remove the table.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Running-the-migration","page":"Working with Genie Apps","title":"Running the migration","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"We can see what SearchLight knows about our migrations with the SearchLight.Migrations.status command:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> SearchLight.Migrations.status()\n| | Module name & status |\n| | File name |\n|---|----------------------------------------|\n| | CreateTableBooks: DOWN |\n| 1 | 2020020909574048_create_table_books.jl |","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"So our migration is in the down state – meaning that its up method has not been run. We can easily fix this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> SearchLight.Migrations.last_up()\n[ Info: Executed migration CreateTableBooks up","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If we recheck the status, the migration is up:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> SearchLight.Migrations.status()\n| | Module name & status |\n| | File name |\n|---|----------------------------------------|\n| | CreateTableBooks: UP |\n| 1 | 2020020909574048_create_table_books.jl |","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Our table is ready!","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Defining-the-model","page":"Working with Genie Apps","title":"Defining the model","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now it's time to edit our model file at app/resources/books/Books.jl. Another convention in SearchLight is that we're using the pluralized name (Books) for the module – because it's for managing multiple books. And within it we define a type (a mutable struct), called Book – which represents an item (a single book) which maps to a row in the underlying database.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Edit the Books.jl file to make it look like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# Books.jl\nmodule Books\n\nimport SearchLight: AbstractModel, DbId, save!\nimport Base: @kwdef\n\nexport Book\n\n@kwdef mutable struct Book <: AbstractModel\n id::DbId = DbId()\n title::String = \"\"\n author::String = \"\"\nend\n\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"We defined a mutable struct which matches our previous Book type by using the @kwdef macro, in order to also define a keyword constructor, as SearchLight needs it.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Using-our-model","page":"Working with Genie Apps","title":"Using our model","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"To make things more interesting, we should import our current books into the database. Add this function to the Books.jl module, under the Book() constructor definition (just above the module's closing end):","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# Books.jl\nfunction seed()\n BillGatesBooks = [\n (\"The Best We Could Do\", \"Thi Bui\"),\n (\"Evicted: Poverty and Profit in the American City\", \"Matthew Desmond\"),\n (\"Believe Me: A Memoir of Love, Death, and Jazz Chickens\", \"Eddie Izzard\"),\n (\"The Sympathizer!\", \"Viet Thanh Nguyen\"),\n (\"Energy and Civilization, A History\", \"Vaclav Smil\")\n ]\n\n for b in BillGatesBooks\n Book(title = b[1], author = b[2]) |> save!\n end\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Auto-loading-the-DB-configuration","page":"Working with Genie Apps","title":"Auto-loading the DB configuration","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now, to try things out. Genie takes care of loading all our resource files for us when we load the app. To do this, Genie comes with a special file called an initializer, which automatically loads the database configuration and sets up SearchLight. Check config/initializers/searchlight.jl to see how this is done.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Heads up!","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"All the *.jl files placed into the config/initializers/ folder are automatically included by Genie upon starting the Genie app. They are included early (upon initialisation), before the controllers, models, views, are loaded.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Trying-it-out","page":"Working with Genie Apps","title":"Trying it out","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now it's time to restart our REPL session and test our app. Close the Julia REPL session to exit to the OS command line and run:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"$ bin/repl","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"On Windows you will need to run bin/repl.bat instead.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The repl executable script placed within the app's bin/ folder starts a new Julia REPL session and loads the applications' environment. Everything should be automatically loaded now, DB configuration included - so we can invoke the previously defined seed function to insert the books:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> using MyGenieApp.Books\n\njulia> Books.seed()","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"There should be a list of queries showing how the data is inserted in the DB:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> Books.seed()\n[ Info: INSERT INTO books (\"title\", \"author\") VALUES ('The Best We Could Do', 'Thi Bui')\n[ Info: INSERT INTO books (\"title\", \"author\") VALUES ('Evicted: Poverty and Profit in the American City', 'Matthew Desmond')\n# output truncated","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If you want to make sure all went right (although trust me, it did, otherwise SearchLight would've thrown an Exception!), just ask SearchLight to retrieve the books we inserted:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> using SearchLight\n\njulia> all(Book)\n[ Info: 2020-02-09 13:29:32 SELECT \"books\".\"id\" AS \"books_id\", \"books\".\"title\" AS \"books_title\", \"books\".\"author\" AS \"books_author\" FROM \"books\" ORDER BY books.id ASC\n\n5-element Array{Book,1}:\n Book\n| KEY | VALUE |\n|----------------|----------------------|\n| author::String | Thi Bui |\n| id::DbId | 1 |\n| title::String | The Best We Could Do |\n\n Book\n| KEY | VALUE |\n|----------------|--------------------------------------------------|\n| author::String | Matthew Desmond |\n| id::DbId | 2 |\n| title::String | Evicted: Poverty and Profit in the American City |\n\n# output truncated","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The SearchLight.all method returns all the Book items from the database.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"All good!","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The next thing we need to do is to update our controller to use the model. Make sure that app/resources/books/BooksController.jl reads like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# BooksController.jl\nmodule BooksController\n\nusing Genie.Renderer.Html\nusing SearchLight\nusing MyGenieApp.Books\n\nfunction billgatesbooks()\n html(:books, :billgatesbooks, books = all(Book))\nend\n\nmodule API\n\nusing ..BooksController\nusing Genie.Renderer.Json\nusing SearchLight\nusing MyGenieApp.Books\n\nfunction billgatesbooks()\n json(:books, :billgatesbooks, books = all(Book))\nend\n\nend\n\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Our JSON view needs a bit of tweaking too:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# app/resources/books/views/billgatesbooks.json.jl\n\"Bill's Gates list of recommended books\" => [Dict(\"author\" => b.author, \"title\" => b.title) for b in books]","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now if we just start the server we'll be able to see the list of books served from the database:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# Start the server\njulia> up()","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The up method starts up the web server and takes us back to the interactive Julia REPL prompt.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now, if, for example, we navigate to http://localhost:8000/api/v1/bgbooks, the output should match the following JSON document:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"{\n \"Bill's Gates list of recommended books\": [\n {\n \"author\": \"Thi Bui\",\n \"title\": \"The Best We Could Do\"\n },\n {\n \"author\": \"Matthew Desmond\",\n \"title\": \"Evicted: Poverty and Profit in the American City\"\n },\n {\n \"author\": \"Eddie Izzard\",\n \"title\": \"Believe Me: A Memoir of Love, Death, and Jazz Chickens\"\n },\n {\n \"author\": \"Viet Thanh Nguyen\",\n \"title\": \"The Sympathizer!\"\n },\n {\n \"author\": \"Vaclav Smil\",\n \"title\": \"Energy and Civilization, A History\"\n }\n ]\n}","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Let's add a new book to see how it works. We'll create a new Book item and persist it using the SearchLight.save! method:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> newbook = Book(title = \"Leonardo da Vinci\", author = \"Walter Isaacson\")\n\nBook\n| KEY | VALUE |\n|----------------|-------------------|\n| author::String | Walter Isaacson |\n| id::DbId | NULL |\n| title::String | Leonardo da Vinci |\n\n\njulia> save!(newbook)\n\n[ Info: INSERT INTO books (\"title\", \"author\") VALUES ('Leonardo da Vinci', 'Walter Isaacson')\n[ Info: ; SELECT CASE WHEN last_insert_rowid() = 0 THEN -1 ELSE last_insert_rowid() END AS id\n[ Info: SELECT \"books\".\"id\" AS \"books_id\", \"books\".\"title\" AS \"books_title\", \"books\".\"author\" AS \"books_author\" FROM \"books\" WHERE \"id\" = 6 ORDER BY books.id ASC\n\nBook\n| KEY | VALUE |\n|----------------|-------------------|\n| author::String | Walter Isaacson |\n| id::DbId | 6 |\n| title::String | Leonardo da Vinci |","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Calling the save! method, SearchLight has persisted the object in the database and then retrieved it and returned it (notice the updated id::DbId field).","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The same save! operation can be written as a one-liner:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> Book(title = \"Leonardo da Vinci\", author = \"Walter Isaacson\") |> save!","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"HEADS UP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If you also run the one-liner save! example, it will add the same book again. No problem, but if you want to remove it, you can use the delete method:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> duplicated_book = find(Book, title = \"Leonardo da Vinci\")[end]\n\njulia> delete(duplicated_bookd)\n[ Info: DELETE FROM books WHERE id = '7'\n\nBook\n| KEY | VALUE |\n|----------------|-------------------|\n| author::String | Walter Isaacson |\n| id::DbId | NULL |\n| title::String | Leonardo da Vinci |","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If you reload the page at http://localhost:8000/bgbooks the new book should show up.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"{\n \"Bill's Gates list of recommended books\": [\n {\n \"author\": \"Thi Bui\",\n \"title\": \"The Best We Could Do\"\n },\n {\n \"author\": \"Matthew Desmond\",\n \"title\": \"Evicted: Poverty and Profit in the American City\"\n },\n {\n \"author\": \"Eddie Izzard\",\n \"title\": \"Believe Me: A Memoir of Love, Death, and Jazz Chickens\"\n },\n {\n \"author\": \"Viet Thanh Nguyen\",\n \"title\": \"The Sympathizer!\"\n },\n {\n \"author\": \"Vaclav Smil\",\n \"title\": \"Energy and Civilization, A History\"\n },\n {\n \"author\": \"Walter Isaacson\",\n \"title\": \"Leonardo da Vinci\"\n }\n ]\n}","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"PRO TIP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"SearchLight exposes two similar data persistence methods: save! and save. They both perform the same action (persisting the object to the database), but save will return a Bool of value true to indicate that the operation was successful or a Bool of value false to indicate that the operation has failed. While the save! variant will return the persisted object upon success or will throw an exception on failure.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Congratulations","page":"Working with Genie Apps","title":"Congratulations","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"You have successfully finished the first part of the step by step walkthrough - you have mastered the Genie basics, allowing you to set up a new app, register routes, add resources (controllers, models, and views), add database support, version the database schema with migrations, and execute basic queries with SearchLight!","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"In the next part we'll look at more advanced topics like handling forms and file uploads, templates rendering, interactivity and more.","category":"page"},{"location":"tutorials/11--Managing_External_Packages.html#Managing-external-packages-for-your-Genie-app","page":"Managing Genie app's dependencies","title":"Managing external packages for your Genie app","text":"","category":"section"},{"location":"tutorials/11--Managing_External_Packages.html","page":"Managing Genie app's dependencies","title":"Managing Genie app's dependencies","text":"Genie fully takes advantage of Julia's excellent package manager, Pkg – while allowing Genie developers to use any third party package available in Julia's ecosystem. This is achieved by taking a common sense approach: Genie apps are just plain Julia projects.","category":"page"},{"location":"tutorials/11--Managing_External_Packages.html","page":"Managing Genie app's dependencies","title":"Managing Genie app's dependencies","text":"In order to add extra packages to your Genie app, thus, we need to use Julia's Pkg features:","category":"page"},{"location":"tutorials/11--Managing_External_Packages.html","page":"Managing Genie app's dependencies","title":"Managing Genie app's dependencies","text":"start a Genie REPL with your app: $ bin/repl. This will automatically load the package environment of the app.\nswitch to Pkg mode: julia> ]\nadd the package you want, for example OhMyREPL: (MyGenieApp) pkg> add OhMyREPL","category":"page"},{"location":"tutorials/11--Managing_External_Packages.html","page":"Managing Genie app's dependencies","title":"Managing Genie app's dependencies","text":"That's all! Now you can use the packages at the Genie REPL or anywhere in your app via using or import.","category":"page"},{"location":"tutorials/11--Managing_External_Packages.html","page":"Managing Genie app's dependencies","title":"Managing Genie app's dependencies","text":"Use the same approach to update the packages in your app, via: pkg> up and apply all available updates, or pkg> up OhMyREPL to update a single package.","category":"page"},{"location":"tutorials/1--Overview.html#Welcome-to-Genie","page":"Welcome to Genie","title":"Welcome to Genie","text":"","category":"section"},{"location":"tutorials/1--Overview.html#The-Highly-Productive-Web-Framework-for-Julia","page":"Welcome to Genie","title":"The Highly Productive Web Framework for Julia","text":"","category":"section"},{"location":"tutorials/1--Overview.html","page":"Welcome to Genie","title":"Welcome to Genie","text":"Genie is a full stack web framework for the Julia programming language. Genie's goals are: excellent developer productivity, great run-time performance, and best practices and security by default.","category":"page"},{"location":"tutorials/1--Overview.html","page":"Welcome to Genie","title":"Welcome to Genie","text":"The Genie web framework follows in the footsteps of mainstream full stack web frameworks like Ruby on Rails and Django, while staying 100% true to its Julia roots. Genie's architecture and development is inspired by the best features present in other frameworks, but not by their design. Genie takes a no-magic no-nonsense approach by doing things the Julia way: Controllers are plain Julia modules, Models leverage types and multiple dispatch, Genie apps are nothing but Julia projects, versioning and dependency management is provided by Julia's own Pkg, and code loading and reloading is automatically set up with Revise.","category":"page"},{"location":"tutorials/1--Overview.html","page":"Welcome to Genie","title":"Welcome to Genie","text":"Genie also takes inspiration from Julia's \"start simple, grow as needed\" philosophy, by allowing developers to bootstrap an app in the REPL or in a notebook, or easily create web services and APIs with just a few lines of code.","category":"page"},{"location":"tutorials/1--Overview.html","page":"Welcome to Genie","title":"Welcome to Genie","text":"As the projects grow more complex, Genie allows adding progressively more structure, by exposing a micro-framework which offers features like powerful routing, flexible logging, support for environments, view templates, etc.","category":"page"},{"location":"tutorials/1--Overview.html","page":"Welcome to Genie","title":"Welcome to Genie","text":"If database persistence is needed, support for Genie's ORM, SearchLight, can be added at any time. Finally, the full MVC structure can be used in order to develop and maintain more complex, end-to-end, web applications.","category":"page"},{"location":"tutorials/2--Installing_Genie.html#How-to-Install-Genie","page":"Installing Genie","title":"How to Install Genie","text":"","category":"section"},{"location":"tutorials/2--Installing_Genie.html","page":"Installing Genie","title":"Installing Genie","text":"Install Genie from Julia's registry – for example the latest version (currently version 5):","category":"page"},{"location":"tutorials/2--Installing_Genie.html","page":"Installing Genie","title":"Installing Genie","text":"pkg> add Genie","category":"page"},{"location":"tutorials/2--Installing_Genie.html","page":"Installing Genie","title":"Installing Genie","text":"Genie, just like Julia, uses semantic versioning in the form vX.Y.Z to designate:","category":"page"},{"location":"tutorials/2--Installing_Genie.html","page":"Installing Genie","title":"Installing Genie","text":"X : major version, introducing breaking changes\nY : minor version, brings new features, no breaking changes\nZ : patch version, fixes bugs, no new features or breaking changes","category":"page"},{"location":"API/secrets.html","page":"Secrets","title":"Secrets","text":"CurrentModule = Secrets","category":"page"},{"location":"API/secrets.html","page":"Secrets","title":"Secrets","text":"load\nsecret\nsecret_token\nsecret_token!","category":"page"},{"location":"API/secrets.html#Genie.Secrets.load","page":"Secrets","title":"Genie.Secrets.load","text":"load(root_dir::String = Genie.config.path_config; context::Union{Module,Nothing} = nothing) :: Nothing\n\nLoads (includes) the framework's secrets.jl file into the app's module context. The files are set up with Revise to be automatically reloaded.\n\n\n\n\n\n","category":"function"},{"location":"API/secrets.html#Genie.Secrets.secret","page":"Secrets","title":"Genie.Secrets.secret","text":"secret() :: String\n\nGenerates a random secret token to be used for configuring the call to Genie.Secrets.secret_token!.\n\n\n\n\n\n","category":"function"},{"location":"API/secrets.html#Genie.Secrets.secret_token","page":"Secrets","title":"Genie.Secrets.secret_token","text":"secret_token(generate_if_missing=true) :: String\n\nReturn the secret token used in the app for encryption and salting.\n\nUsually, this token is defined through Genie.Secrets.secret_token! in the config/secrets.jl file. Here, a temporary one is generated for the current session if no other token is defined and generate_if_missing is true.\n\n\n\n\n\n","category":"function"},{"location":"API/secrets.html#Genie.Secrets.secret_token!","page":"Secrets","title":"Genie.Secrets.secret_token!","text":"secret_token!(value = secret())\n\nDefine the secret token used in the app for encryption and salting.\n\n\n\n\n\n","category":"function"},{"location":"API/toolbox.html","page":"Toolbox","title":"Toolbox","text":"CurrentModule = Toolbox","category":"page"},{"location":"API/toolbox.html","page":"Toolbox","title":"Toolbox","text":"TaskInfo\nTaskResult\ntasks\nVoidTaskResult\nvalidtaskname\ntaskdocs\nloadtasks\nprinttasks\nnew\ntaskfilename\ntaskmodulename\nisvalidtask!","category":"page"},{"location":"API/toolbox.html#Genie.Toolbox.validtaskname","page":"Toolbox","title":"Genie.Toolbox.validtaskname","text":"validtaskname(task_name::String) :: String\n\nAttempts to convert a potentially invalid (partial) task_name into a valid one.\n\n\n\n\n\n","category":"function"},{"location":"API/toolbox.html#Genie.Toolbox.taskdocs","page":"Toolbox","title":"Genie.Toolbox.taskdocs","text":"task_docs(module_name::Module) :: String\n\nRetrieves the docstring of the runtask method and returns it as a string.\n\n\n\n\n\n","category":"function"},{"location":"API/toolbox.html#Genie.Toolbox.loadtasks","page":"Toolbox","title":"Genie.Toolbox.loadtasks","text":"loadtasks(; filter_type_name = Symbol()) :: Vector{TaskInfo}\n\nReturns a vector of all registered Genie tasks.\n\n\n\n\n\n","category":"function"},{"location":"API/toolbox.html#Genie.Toolbox.printtasks","page":"Toolbox","title":"Genie.Toolbox.printtasks","text":"Prints a list of all the registered Genie tasks to the standard output.\n\n\n\n\n\n","category":"function"},{"location":"API/toolbox.html#new","page":"Toolbox","title":"new","text":"new, or new{A,B,...}\n\nSpecial function available to inner constructors which creates a new object of the type. The form new{A,B,...} explicitly specifies values of parameters for parametric types. See the manual section on Inner Constructor Methods for more information.\n\n\n\n\n\n","category":"keyword"},{"location":"API/toolbox.html#Genie.Toolbox.taskfilename","page":"Toolbox","title":"Genie.Toolbox.taskfilename","text":"task_file_name(cmd_args::Dict{String,Any}, config::Settings) :: String\n\nComputes the name of a Genie task based on the command line input.\n\n\n\n\n\n","category":"function"},{"location":"API/toolbox.html#Genie.Toolbox.taskmodulename","page":"Toolbox","title":"Genie.Toolbox.taskmodulename","text":"task_module_name(underscored_task_name::String) :: String\n\nComputes the name of a Genie task based on the command line input.\n\n\n\n\n\n","category":"function"},{"location":"API/toolbox.html#Genie.Toolbox.isvalidtask!","page":"Toolbox","title":"Genie.Toolbox.isvalidtask!","text":"isvalidtask!(parsed_args::Dict{String,Any}) :: Dict{String,Any}\n\nChecks if the name of the task passed as the command line arg is valid task identifier – if not, attempts to address it, by appending the TASKSUFFIX suffix. Returns the potentially modified `parsedargsDict`.\n\n\n\n\n\n","category":"function"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Using-Genie-with-Docker","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Genie comes with extended support for containerizing apps using Docker. The functionality is provided by the official GenieDeployDocker plugin.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Setting-up-GenieDeployDocker","page":"Using Genie with Docker","title":"Setting up GenieDeployDocker","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"In order to use the Docker integration features, first we need to add the GenieDeployDocker plugin for Genie.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"pkg> add GenieDeployDocker","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Generating-the-Genie-optimised-Dockerfile","page":"Using Genie with Docker","title":"Generating the Genie-optimised Dockerfile","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"You can bootstrap the Docker setup by invoking the GenieDeployDocker.dockerfile() function. This will generate a custom Dockerfile optimized for Genie web apps containerization. The file will be generated in the current work dir (or where instructed by the optional argument path – see the help for the dockerfile() function).","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Once generated, you can edit it and customize it as needed - Genie will not overwrite the file, thus preserving any changes (unless you call the dockerfile function again, passing the force=true argument).","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"The behavior of dockerfile() can be controlled by passing any of the multiple optional arguments supported.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Building-the-Docker-container","page":"Using Genie with Docker","title":"Building the Docker container","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Once we have our Dockerfile ready, we can invoke GenieDeployDocker.build() to set up the Docker container. You can pass any of the supported optional arguments to configure settings such as the container's name (by default \"genie\"), the path (defaults to current work dir), and others (see the output of help?> GenieDeployDocker.dockerfile for all the available options).","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Running-the-Genie-app-within-the-Docker-container","page":"Using Genie with Docker","title":"Running the Genie app within the Docker container","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"When the image is ready, we can run it with GenieDeployDocker.run(). We can configure any of the optional arguments in order to control how the app is run. Check the inline help for the function for more details.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Examples","page":"Using Genie with Docker","title":"Examples","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"First let's create a Genie app:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia> using Genie\n\njulia> Genie.Generator.newapp(\"DockerTest\")\n[ Info: Done! New app created at /your/app/path/DockerTest\n# output truncated","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"When it's ready, let's add the Dockerfile:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia> using GenieDeployDocker\n\njulia> GenieDeployDocker.dockerfile()\nDocker file successfully written at /your/app/path/DockerTest/Dockerfile","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Now, to build our container:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia> GenieDeployDocker.build()\n# output truncated\nSuccessfully tagged genie:latest\nDocker container successfully built","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"And finally, we can now run our app within the Docker container:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia> GenieDeployDocker.run()\nStarting docker container with `docker run -it --rm -p 80:8000 --name genieapp genie bin/server`\n# output truncated","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"We should then see the familiar Genie loading screen, indicating the app's loading progress and notifying us once the app is running.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Our application starts inside the Docker container, binding port 8000 within the container (where the Genie app is running) to the port 80 of the host. So we are now able to access our app at http://localhost. If you navigate to http://localhost w ith your favorite browser you'll see Genie's welcome page. Notice that we don't access on port 8000 - this page is served from the Docker container on the default port 80.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Inspecting-the-containers","page":"Using Genie with Docker","title":"Inspecting the containers","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"We can get a list of available container by using GenieDeployDocker.list(). This will show only the currently running containers by default, but we can pass the all=true argument to also include containers that are offline.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia> GenieDeployDocker.list()\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\nc87bfd8322cc genie \"bin/server\" 6 minutes ago Up 6 minutes 80/tcp, 0.0.0.0:80->8000/tcp genieapp\nProcess(`docker ps`, ProcessExited(0))","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Stopping-running-containers","page":"Using Genie with Docker","title":"Stopping running containers","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"The running containers can be stopped by using the GenieDeployDocker.stop() function, passing the name of the container.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia> GenieDeployDocker.stop(\"genieapp\")","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Using-Docker-during-development","page":"Using Genie with Docker","title":"Using Docker during development","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"If we want to use Docker to serve the app during development, we need to mount our app from host (your computer) into the container – so that we can keep editing our files locally, but see the changes reflected in the Docker container. In order to do this we need to pass the mountapp = true argument to GenieDeployDocker.run(), like this:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia> GenieDeployDocker.run(mountapp = true)\nStarting docker container with `docker run -it --rm -p 80:8000 --name genieapp -v /Users/adrian/DockerTest:/home/genie/app genie bin/server`","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"When the app finishes starting, we can edit the files on the host using our favorite IDE, and see the changes reflected in the Docker container.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Creating-an-optimized-Genie-sysimage-with-PackageCompiler.jl","page":"Using Genie with Docker","title":"Creating an optimized Genie sysimage with PackageCompiler.jl","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"If we are using Docker containers to deploy Genie apps in production, you can greatly improve the performance of the app by preparing a precompiled sysimage for Julia. We can include this workflow as part of the Docker build step as follows.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Edit-the-Dockerfile","page":"Using Genie with Docker","title":"Edit the Dockerfile","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"We'll start by making a few changes to our Dockerfile, as follows:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"1/ Under the line WORKDIR /home/genie/app add","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"# C compiler for PackageCompiler\nRUN apt-get update && apt-get install -y g++","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"2/ Under the line starting with RUN julia -e add","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"# Compile app\nRUN julia --project compiled/make.jl","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"You may also want to replace the line saying","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"ENV GENIE_ENV \"dev\"","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"with","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"ENV GENIE_ENV \"prod\"","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"to configure the application to run in production (first test locally to make sure that everything is properly configured to run the app in production environment).","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Add-PackageCompiler.jl","page":"Using Genie with Docker","title":"Add PackageCompiler.jl","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"We also need to add PackageCompiler as a dependency of our app:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"pkg> add PackageCompiler","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Add-the-needed-files","page":"Using Genie with Docker","title":"Add the needed files","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Create a new folder to host our files:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia> mkdir(\"compiled\")","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Now create the following files:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia> touch(\"compiled/make.jl\")\njulia> touch(\"compiled/packages.jl\")","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Edit-the-files","page":"Using Genie with Docker","title":"Edit the files","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Now to put the content into each of the files.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Preparing-the-packages.jl-file","page":"Using Genie with Docker","title":"Preparing the packages.jl file","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Here we simply put an array of package that our app uses and that we want to precompile, ex:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"# packages.jl\nconst PACKAGES = [\n \"Dates\",\n \"Genie\",\n \"Inflector\",\n \"Logging\"\n]","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Preparing-the-make.jl-file","page":"Using Genie with Docker","title":"Preparing the make.jl file","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Now edit the make.jl file as follows:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"# make.jl\nusing PackageCompiler\n\ninclude(\"packages.jl\")\n\nPackageCompiler.create_sysimage(\n PACKAGES,\n sysimage_path = \"compiled/sysimg.so\",\n cpu_target = PackageCompiler.default_app_cpu_target()\n)","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Using-the-precompiled-image","page":"Using Genie with Docker","title":"Using the precompiled image","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"The result of these changes is that PackageCompiler will create a new Julia sysimage that will be stored inside the compiled/sysimg.so file. The last step is to instruct our bin/server script to use the image.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Edit the bin/server file and make it look like this:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia --color=yes --depwarn=no --project=@. --sysimage=compiled/sysimg.so -q -i -- $(dirname $0)/../bootstrap.jl -s=true \"$@\"","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"With this change we're passing the additional --sysimage flag, indicating our new Julia sys image.","category":"page"},{"location":"API/index.html","page":"Genie","title":"Genie","text":"(Image: Genie Logo)","category":"page"},{"location":"API/index.html#Genie","page":"Genie","title":"Genie","text":"","category":"section"},{"location":"API/index.html#The-highly-productive-Julia-web-framework","page":"Genie","title":"The highly productive Julia web framework","text":"","category":"section"},{"location":"API/index.html","page":"Genie","title":"Genie","text":"Genie is Julia web framework that provides a streamlined and efficient workflow for developing modern web applications. It builds on Julia's strengths (high-level, high-performance, dynamic, JIT compiled), exposing a rich API and a powerful toolset for productive web development.","category":"page"},{"location":"API/index.html#Current-status","page":"Genie","title":"Current status","text":"","category":"section"},{"location":"API/index.html","page":"Genie","title":"Genie","text":"Genie is compatible with Julia v1.3 and up.","category":"page"},{"location":"API/index.html","page":"Genie","title":"Genie","text":"","category":"page"},{"location":"API/index.html#Acknowledgements","page":"Genie","title":"Acknowledgements","text":"","category":"section"},{"location":"API/index.html","page":"Genie","title":"Genie","text":"Genie uses a multitude of packages that have been kindly contributed by the Julia community.\nThe awesome Genie logo was designed by Alvaro Casanova.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html#Deploying-Genie-apps-to-server-with-Nginx","page":"Deploying to server with Nginx","title":"Deploying Genie apps to server with Nginx","text":"","category":"section"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"This tutorial shows how to host a Julia/Genie app on with Nginx.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html#Prerequisites","page":"Deploying to server with Nginx","title":"Prerequisites","text":"","category":"section"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"To expose the app over the internet, one needs access to a server. This can be a local machine or a cloud instance such as AWS EC2 or a Google Cloud Compute Engine for example.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"If using a local server, a static IP is needed to ensure continuous access to the app. Internet service provider generally charge a fee for such extra service.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html#The-application","page":"Deploying to server with Nginx","title":"The application","text":"","category":"section"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"We assume that a Genie app has been developed and is ready for deployment and that it is hosted as a project on a git repository.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"For example, the app MyGenieApp generated through Genie.Generator.newapp(\"MyGenieApp\") being hosted at github.com/user/MyGenieApp.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"The scripts presented in this tutorial are for Ubuntu 20.04.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html#Install-and-run-the-Genie-app-on-the-server","page":"Deploying to server with Nginx","title":"Install and run the Genie app on the server","text":"","category":"section"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Access the server:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"ssh -i \"ssh-key-for-instance.pem\" user@123.123.123.123","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Install Julia if not present. Then make the clone:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"git clone github.com/user/MyGenieApp\ncd MyGenieAp","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Install the app as any other Julia project:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"julia\n] activate .\npkg> instantiate\nexit()","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"In order to launch the app and exit the console without shutting down the app, we will launch it from a new screen:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"screen -S genie","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Then set the GENIE_ENV environment variable to prod:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"export GENIE_ENV=prod","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Now the application is almost ready to start just need to configure the secret token. Go into the project directory and execute the following command. It will generate secrets.jl inside config/secrets.jl file and if it exists then it will update with a new token string.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"julia --project=. --banner=no --eval=\"using Pkg; using Genie; Genie.Generator.write_secrets_file()\"","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Launch the app:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"./bin/server","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Now the Genie app should be running on the server and be accessible at the following address: 123.123.123.123:8000 (if port 8000 has been open - see instance security settings). Note that you should configure the Genie app so that it doesn't serve the static content (see the Settings option server_handle_static_file in config/env/prod.jl). Static content should be handled by nginx. We can now detach from the genie screen used to launch the app (Ctl+A d).","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html#Install-and-configure-nginx-server","page":"Deploying to server with Nginx","title":"Install and configure nginx server","text":"","category":"section"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Nginx server will be used as a reverse proxy. It will listen requests made on port 80 (HTTP) and redirect traffic to the Genie app running on port 8000 (default Genie setting that can be changed).","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Nginx will also be used to serve the app static files, that is, the content under the ./public folder.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Finally, it can as well handle HTTPS requests, which will also be redirected to the Genie app listening on port 8000.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Installation:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"sudo apt-get update\nsudo apt-get install nginx\nsudo systemctl start nginx\nsudo systemctl enable nginx","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"A configuration file then needs to to be created to indicate on which port to listen (80 for HTTP) and to which port to redirect the traffic (8000 for default Genie config).","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Config is created in folder /etc/nginx/sites-available: sudo nano my-genie-app. Put the following content in my-genie-app:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"server {\n listen 80;\n listen [::]:80;\n\n server_name test.com;\n root /home/ubuntu/MyGenieApp/public;\n index welcome.html;\n\n location / {\n proxy_pass http://localhost:8000/;\n }\n\n location /css/genie {\n proxy_pass http://localhost:8000/;\n }\n location /img/genie {\n proxy_pass http://localhost:8000/;\n }\n location /js/genie {\n proxy_pass http://localhost:8000/;\n }\n}","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"server_name: refers to the web domain to be used. It can be put to an arbitrary name if the app is only to be served","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"directly from the server public IP.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"root: points to the public subfolder where the genie app was cloned.\nindex: refers to the site index (the landing page).\nThe various location following the initial proxy to the genie app are used to indicate static content folders to be","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"served by nginx. These are needed when the server_handle_static_file is set to false in the Genie app settings.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"To make that config effective, it needs to be present in sites-enabled. The default config can be removed.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"sudo ln -s /etc/nginx/sites-available/my-genie-app /etc/nginx/sites-enabled/my-genie-app","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Then restart the server to make changes effective:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"sudo systemctl restart nginx","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html#Enable-HTTPS","page":"Deploying to server with Nginx","title":"Enable HTTPS","text":"","category":"section"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"To enable HTTPS, a site-certificate will be needed for the domain on which the site will be served. A practical approach is to use the utilities provided by certbot.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Following provided instructions for nginx on Ubuntu 20.04:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"sudo snap install core; sudo snap refresh core\nsudo snap install --classic certbot\nsudo ln -s /snap/bin/certbot /usr/bin/certbot","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Then, using certbot utility, a certificate will be generated and appropriate modification to nginx config will be brought to handle support for HTTPS:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"sudo certbot --nginx","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Note that this step will check for ownernship of the test.com domain mentionned in the nginx config file. For that validation to succeed, it requires to have the A record for the domain set to 123.123.123.123.","category":"page"},{"location":"API/exceptions.html","page":"Exceptions","title":"Exceptions","text":"CurrentModule = Exceptions","category":"page"},{"location":"API/exceptions.html","page":"Exceptions","title":"Exceptions","text":"ExceptionalResponse\nFileExistsException\nInternalServerException\nNotFoundException\nRuntimeException","category":"page"},{"location":"API/exceptions.html#Genie.Exceptions.ExceptionalResponse","page":"Exceptions","title":"Genie.Exceptions.ExceptionalResponse","text":"struct ExceptionalResponse <: Exception\n\nA type of exception which wraps an HTTP Response object. The thrown exception will propagate until it is caught up the app stack or ultimately by Genie and the wrapped response is sent to the client.\n\nExample\n\nIf the user is not authenticated, an ExceptionalResponse is thrown - if the exception is not caught in the app's stack, Genie will catch it and return the wrapped Response object, forcing an HTTP redirect to the login page.\n\nisauthenticated() || throw(ExceptionalResponse(redirect(:show_login)))\n\n\n\n\n\n","category":"type"},{"location":"API/exceptions.html#Genie.Exceptions.FileExistsException","page":"Exceptions","title":"Genie.Exceptions.FileExistsException","text":"struct FileExistsException <: Exception\n\nCustom exception type for signaling that the requested file already exists.\n\n\n\n\n\n","category":"type"},{"location":"API/exceptions.html#Genie.Exceptions.InternalServerException","page":"Exceptions","title":"Genie.Exceptions.InternalServerException","text":"struct InternalServerException <: Exception\n\nDedicated exception type for server side exceptions. Results in a 500 error by default.\n\nArguments\n\nmessage::String\ninfo::String\ncode::Int\n\n\n\n\n\n","category":"type"},{"location":"API/exceptions.html#Genie.Exceptions.NotFoundException","page":"Exceptions","title":"Genie.Exceptions.NotFoundException","text":"struct NotFoundException <: Exception\n\nSpecialized exception representing a not found resources. Results in a 404 response being sent to the client.\n\nArguments\n\nmessage::String\ninfo::String\ncode::Int\nresource::String\n\n\n\n\n\n","category":"type"},{"location":"API/exceptions.html#Genie.Exceptions.RuntimeException","page":"Exceptions","title":"Genie.Exceptions.RuntimeException","text":"RuntimeException\n\nRepresents an unexpected and unhandled runtime exceptions. An error event will be logged and the exception will be sent to the client, depending on the environment (the error stack is dumped by default in dev mode or an error message is displayed in production).\n\nIt allows defining custom error message and info, as well as an error code, in addition to the exception object.\n\nArguments\n\nmessage::String\ninfo::String\ncode::Int\nex::Union{Nothing,Exception}\n\n\n\n\n\n","category":"type"},{"location":"API/requests.html","page":"Requests","title":"Requests","text":"CurrentModule = Requests","category":"page"},{"location":"API/requests.html","page":"Requests","title":"Requests","text":"jsonpayload\nrawpayload\nfilespayload\ninfilespayload\nRequests.write\nRequests.read\nfilename\npostpayload\ngetpayload\nrequest\npayload\nmatchedroute\nmatchedchannel\nwsclient","category":"page"},{"location":"API/requests.html#Genie.Requests.jsonpayload","page":"Requests","title":"Genie.Requests.jsonpayload","text":"jsonpayload()\n\nProcesses an application/json POST request. If it fails to successfully parse the JSON data it returns nothing. The original payload can still be accessed invoking rawpayload()\n\n\n\n\n\njsonpayload(v)\n\nProcesses an application/json POST request attempting to return value corresponding to key v.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.rawpayload","page":"Requests","title":"Genie.Requests.rawpayload","text":"rawpayload() :: String\n\nReturns the raw POST payload as a String.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.filespayload","page":"Requests","title":"Genie.Requests.filespayload","text":"filespayload() :: Dict{String,HttpFile}\n\nCollection of form uploaded files.\n\n\n\n\n\nfilespayload(filename::Union{String,Symbol}) :: HttpFile\n\nReturns the HttpFile uploaded through the key input name.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.infilespayload","page":"Requests","title":"Genie.Requests.infilespayload","text":"infilespayload(key::Union{String,Symbol}) :: Bool\n\nChecks if the collection of uploaded files contains a file stored under the key name.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Base.write","page":"Requests","title":"Base.write","text":"write(io::IO, x)\nwrite(filename::AbstractString, x)\n\nWrite the canonical binary representation of a value to the given I/O stream or file. Return the number of bytes written into the stream. See also print to write a text representation (with an encoding that may depend upon io).\n\nThe endianness of the written value depends on the endianness of the host system. Convert to/from a fixed endianness when writing/reading (e.g. using htol and ltoh) to get results that are consistent across platforms.\n\nYou can write multiple values with the same write call. i.e. the following are equivalent:\n\nwrite(io, x, y...)\nwrite(io, x) + write(io, y...)\n\nExamples\n\nConsistent serialization:\n\njulia> fname = tempname(); # random temporary filename\n\njulia> open(fname,\"w\") do f\n # Make sure we write 64bit integer in little-endian byte order\n write(f,htol(Int64(42)))\n end\n8\n\njulia> open(fname,\"r\") do f\n # Convert back to host byte order and host integer type\n Int(ltoh(read(f,Int64)))\n end\n42\n\nMerging write calls:\n\njulia> io = IOBuffer();\n\njulia> write(io, \"JuliaLang is a GitHub organization.\", \" It has many members.\")\n56\n\njulia> String(take!(io))\n\"JuliaLang is a GitHub organization. It has many members.\"\n\njulia> write(io, \"Sometimes those members\") + write(io, \" write documentation.\")\n44\n\njulia> String(take!(io))\n\"Sometimes those members write documentation.\"\n\nUser-defined plain-data types without write methods can be written when wrapped in a Ref:\n\njulia> struct MyStruct; x::Float64; end\n\njulia> io = IOBuffer()\nIOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1)\n\njulia> write(io, Ref(MyStruct(42.0)))\n8\n\njulia> seekstart(io); read!(io, Ref(MyStruct(NaN)))\nBase.RefValue{MyStruct}(MyStruct(42.0))\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Base.read","page":"Requests","title":"Base.read","text":"read(io::IO, T)\n\nRead a single value of type T from io, in canonical binary representation.\n\nNote that Julia does not convert the endianness for you. Use ntoh or ltoh for this purpose.\n\nread(io::IO, String)\n\nRead the entirety of io, as a String (see also readchomp).\n\nExamples\n\njulia> io = IOBuffer(\"JuliaLang is a GitHub organization\");\n\njulia> read(io, Char)\n'J': ASCII/Unicode U+004A (category Lu: Letter, uppercase)\n\njulia> io = IOBuffer(\"JuliaLang is a GitHub organization\");\n\njulia> read(io, String)\n\"JuliaLang is a GitHub organization\"\n\n\n\n\n\nread(filename::AbstractString, args...)\n\nOpen a file and read its contents. args is passed to read: this is equivalent to open(io->read(io, args...), filename).\n\nread(filename::AbstractString, String)\n\nRead the entire contents of a file as a string.\n\n\n\n\n\nread(s::IO, nb=typemax(Int))\n\nRead at most nb bytes from s, returning a Vector{UInt8} of the bytes read.\n\n\n\n\n\nread(s::IOStream, nb::Integer; all=true)\n\nRead at most nb bytes from s, returning a Vector{UInt8} of the bytes read.\n\nIf all is true (the default), this function will block repeatedly trying to read all requested bytes, until an error or end-of-file occurs. If all is false, at most one read call is performed, and the amount of data returned is device-dependent. Note that not all stream types support the all option.\n\n\n\n\n\nread(command::Cmd)\n\nRun command and return the resulting output as an array of bytes.\n\n\n\n\n\nread(command::Cmd, String)\n\nRun command and return the resulting output as a String.\n\n\n\n\n\nread(stream::IO, [nb::Integer,] enc::Encoding)\nread(filename::AbstractString, [nb::Integer,] enc::Encoding)\nread(stream::IO, ::Type{String}, enc::Encoding)\nread(filename::AbstractString, ::Type{String}, enc::Encoding)\n\nMethods to read text in character encoding enc. See documentation for corresponding methods without the enc argument for details.\n\n\n\n\n\nread(file::HttpFile)\n\nReturns the content of file as string.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.filename","page":"Requests","title":"Genie.Requests.filename","text":"filename(file::HttpFile) :: String\n\nOriginal filename of the uploaded HttpFile file.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.postpayload","page":"Requests","title":"Genie.Requests.postpayload","text":"postpayload() :: Dict{Symbol,Any}\n\nA dict representing the POST variables payload of the request (corresponding to a form-data request)\n\n\n\n\n\npostpayload(key::Symbol) :: Any\n\nReturns the value of the POST variables key.\n\n\n\n\n\npostpayload(key::Symbol, default::Any)\n\nReturns the value of the POST variables key or the default value if key is not defined.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.getpayload","page":"Requests","title":"Genie.Requests.getpayload","text":"getpayload() :: Dict{Symbol,Any}\n\nA dict representing the GET/query variables payload of the request (the part corresponding to ?foo=bar&baz=moo)\n\n\n\n\n\ngetpayload(key::Symbol) :: Any\n\nThe value of the GET/query variable key, as in ?key=value\n\n\n\n\n\ngetpayload(key::Symbol, default::Any) :: Any\n\nThe value of the GET/query variable key, as in ?key=value. If key is not defined, default is returned.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.request","page":"Requests","title":"Genie.Requests.request","text":"request() :: HTTP.Request\n\nReturns the raw HTTP.Request object associated with the request. If no request is available (not within a request/response cycle) returns nothing.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.payload","page":"Requests","title":"Genie.Requests.payload","text":"payload() :: Any\n\nUtility function for accessing the params collection, which holds the request variables.\n\n\n\n\n\npayload(key::Symbol) :: Any\n\nUtility function for accessing the key value within the params collection of request variables.\n\n\n\n\n\npayload(key::Symbol, default_value::T) :: Any\n\nUtility function for accessing the key value within the params collection of request variables. If key is not defined, default_value is returned.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.matchedroute","page":"Requests","title":"Genie.Requests.matchedroute","text":"matchedroute() :: Route\n\nReturns the Route object which was matched for the current request or noting if no route is available.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.matchedchannel","page":"Requests","title":"Genie.Requests.matchedchannel","text":"matchedchannel() :: Channel\n\nReturns the Channel object which was matched for the current request or nothing if no channel is available.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.wsclient","page":"Requests","title":"Genie.Requests.wsclient","text":"wsclient() :: HTTP.WebSockets.WebSocket\n\nThe web sockets client for the current request or nothing if not available.\n\n\n\n\n\n","category":"function"},{"location":"API/watch.html","page":"Watch","title":"Watch","text":"CurrentModule = Watch","category":"page"},{"location":"API/watch.html","page":"Watch","title":"Watch","text":"WATCHED_FOLDERS\nWATCHING\ncollect_watched_files\nhandlers\nunwatch\nwatch\nwatchpath","category":"page"},{"location":"tutorials/5--Handling_Query_Params.html#Handling-query-params-(GET-variables)","page":"Handling URI/query params","title":"Handling query params (GET variables)","text":"","category":"section"},{"location":"tutorials/5--Handling_Query_Params.html","page":"Handling URI/query params","title":"Handling URI/query params","text":"Genie makes it easy to access query params, which are values sent as part of the URL over GET requests (ex: mywebsite.com/index?foo=1&bar=2 foo and bar are query params corresponding to the variables foo = 1 and bar = 2). All these values are automatically collected by Genie and exposed in the params() collection (which is part of the Router module).","category":"page"},{"location":"tutorials/5--Handling_Query_Params.html#Example","page":"Handling URI/query params","title":"Example","text":"","category":"section"},{"location":"tutorials/5--Handling_Query_Params.html","page":"Handling URI/query params","title":"Handling URI/query params","text":"using Genie\n\nroute(\"/hi\") do\n name = params(:name, \"Anon\")\n\n \"Hello $name\"\nend","category":"page"},{"location":"tutorials/5--Handling_Query_Params.html","page":"Handling URI/query params","title":"Handling URI/query params","text":"If you access http://127.0.0.1:8000/hi the app will respond with \"Hello Anon\" since we're not passing any query params.","category":"page"},{"location":"tutorials/5--Handling_Query_Params.html","page":"Handling URI/query params","title":"Handling URI/query params","text":"However, requesting http://127.0.0.1:8000/hi?name=Adrian will in turn display \"Hello Adrian\" as we're passing the name query variable with the value Adrian. This variable is exposed by Genie as params(:name).","category":"page"},{"location":"tutorials/5--Handling_Query_Params.html","page":"Handling URI/query params","title":"Handling URI/query params","text":"Genie however provides utility methods for accessing these values in the Requests module.","category":"page"},{"location":"tutorials/5--Handling_Query_Params.html#The-Requests-module","page":"Handling URI/query params","title":"The Requests module","text":"","category":"section"},{"location":"tutorials/5--Handling_Query_Params.html","page":"Handling URI/query params","title":"Handling URI/query params","text":"Genie provides a set of utilities for working with requests data within the Requests module. You can use the getpayload method to retrieve the query params as a Dict{Symbol,Any}. We can rewrite the previous route to take advantage of the Requests utilities.","category":"page"},{"location":"tutorials/5--Handling_Query_Params.html#Example-2","page":"Handling URI/query params","title":"Example","text":"","category":"section"},{"location":"tutorials/5--Handling_Query_Params.html","page":"Handling URI/query params","title":"Handling URI/query params","text":"using Genie, Genie.Requests\n\nroute(\"/hi\") do\n \"Hello $(getpayload(:name, \"Anon\"))\"\nend","category":"page"},{"location":"tutorials/5--Handling_Query_Params.html","page":"Handling URI/query params","title":"Handling URI/query params","text":"The getpayload function has a few specializations, and one of them accepts the key and a default value. The default value is returned if the key variable is not defined. You can see the various implementations for getpayload using the API docs or Julia's help> mode.","category":"page"},{"location":"API/responses.html","page":"Responses","title":"Responses","text":"CurrentModule = Responses","category":"page"},{"location":"API/responses.html","page":"Responses","title":"Responses","text":"getresponse\ngetheaders\nsetheaders!\nsetheaders\ngetstatus\nsetstatus!\nsetstatus\ngetbody\nsetbody!\nsetbody","category":"page"},{"location":"tutorials/7--Using_JSON_Payloads.html#Using-JSON-payloads","page":"Using JSON payloads","title":"Using JSON payloads","text":"","category":"section"},{"location":"tutorials/7--Using_JSON_Payloads.html","page":"Using JSON payloads","title":"Using JSON payloads","text":"A very common design pattern, especially when developing REST APIs, is to accept JSON payloads sent as application/json data over POST requests. Genie efficiently handles this use case through the utility function Requests.jsonpayload. Under the cover, Genie will process the POST request and will attempt to parse the JSON text payload. If this fails, we can still access the raw data (the text payload not converted to JSON) by using the Requests.rawpayload method.","category":"page"},{"location":"tutorials/7--Using_JSON_Payloads.html#Example","page":"Using JSON payloads","title":"Example","text":"","category":"section"},{"location":"tutorials/7--Using_JSON_Payloads.html","page":"Using JSON payloads","title":"Using JSON payloads","text":"using Genie, Genie.Requests, Genie.Renderer.Json\n\nroute(\"/jsonpayload\", method = POST) do\n @show jsonpayload()\n @show rawpayload()\n\n json(\"Hello $(jsonpayload()[\"name\"])\")\nend\n\nup()","category":"page"},{"location":"tutorials/7--Using_JSON_Payloads.html","page":"Using JSON payloads","title":"Using JSON payloads","text":"Next we make a POST request using the HTTP package:","category":"page"},{"location":"tutorials/7--Using_JSON_Payloads.html","page":"Using JSON payloads","title":"Using JSON payloads","text":"using HTTP\n\nHTTP.request(\"POST\", \"http://localhost:8000/jsonpayload\", [(\"Content-Type\", \"application/json\")], \"\"\"{\"name\":\"Adrian\"}\"\"\")","category":"page"},{"location":"tutorials/7--Using_JSON_Payloads.html","page":"Using JSON payloads","title":"Using JSON payloads","text":"We will get the following output:","category":"page"},{"location":"tutorials/7--Using_JSON_Payloads.html","page":"Using JSON payloads","title":"Using JSON payloads","text":"jsonpayload() = Dict{String,Any}(\"name\"=>\"Adrian\")\nrawpayload() = \"{\\\"name\\\":\\\"Adrian\\\"}\"\n\nINFO:Main: /jsonpayload 200\n\nHTTP.Messages.Response:\n\"\"\"\nHTTP/1.1 200 OK\nContent-Type: application/json\nTransfer-Encoding: chunked\n\n\"Hello Adrian\"\"\"\"","category":"page"},{"location":"tutorials/7--Using_JSON_Payloads.html","page":"Using JSON payloads","title":"Using JSON payloads","text":"First, for the two @show calls, notice how jsonpayload had successfully converted the POST data to a Dict. While the rawpayload returns the POST data as a String, exactly as received. Finally, our route handler returns a JSON response, greeting the user by extracting the name from within the jsonpayload Dict.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#Deploying-Genie-apps-with-Heroku-Buildpacks","page":"Deploying to Heroku with Buildpacks","title":"Deploying Genie apps with Heroku Buildpacks","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"This tutorial shows how to host a Julia/Genie app using a Heroku Buildpack.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#Prerequisites","page":"Deploying to Heroku with Buildpacks","title":"Prerequisites","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"This guide assumes you have a Heroku account and are signed into the Heroku CLI. Information on how to setup the Heroku CLI is available here.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#The-application","page":"Deploying to Heroku with Buildpacks","title":"The application","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"In order to try the deployment, you will need a sample application. Either pick one of yours or clone this sample one, as indicated next.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#All-Steps-(in-easy-copy-paste-format):","page":"Deploying to Heroku with Buildpacks","title":"All Steps (in easy copy-paste format):","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"Customize your HEROKU_APP_NAME to something unique:","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"HEROKU_APP_NAME=my-app-name","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"Clone a sample app if needed:","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"git clone https://github.com/milesfrain/GenieOnHeroku.git","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"Go into the app's folder:","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"cd GenieOnHeroku","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"And create a Heroku app:","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"heroku create $HEROKU_APP_NAME --buildpack https://github.com/Optomatica/heroku-buildpack-julia.git","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"Push the newly created app to Heroku:","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"git push heroku master","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"Now you can open the app in the browser:","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"heroku open -a $HEROKU_APP_NAME","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"If you need to check the logs:","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"heroku logs -tail -a $HEROKU_APP_NAME","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#Steps-with-more-detailed-descriptions","page":"Deploying to Heroku with Buildpacks","title":"Steps with more detailed descriptions","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#Select-an-app-name","page":"Deploying to Heroku with Buildpacks","title":"Select an app name","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"HEROKU_APP_NAME=my-app-name","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"This must be unique among all Heroku projects, and is part of the url where your project is hosted (e.g. https://my-app-name.herokuapp.com/).","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"If the name is not unique, you will see this error at the heroku create step.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"Creating ⬢ my-app-name... !\n ▸ Name my-app-name is already taken","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#Clone-an-example-project","page":"Deploying to Heroku with Buildpacks","title":"Clone an example project","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"git clone https://github.com/milesfrain/GenieOnHeroku.git\ncd GenieOnHeroku","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"You may also point to your own project, but it must be a git repo.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"A Procfile in the root contains the launch command to load your app. The contents of the Procfile for this project is this single line:","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"web: julia --project src/app.jl $PORT","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"You may edit the Procfile to point to your own project's launch script. (for example src/my_app_launch_file.jl instead of src/app.jl), but be sure to take into account the dynamically changing $PORT environment variable which is set by Heroku.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"If you're deploying a standard Genie application built with Genie.newapp, the launch script will be bin/server. Genie will automatically pick the $PORT number from the environment.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#Create-a-Heroku-project","page":"Deploying to Heroku with Buildpacks","title":"Create a Heroku project","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"heroku create $HEROKU_APP_NAME --buildpack https://github.com/Optomatica/heroku-buildpack-julia.git","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"This creates a project on the Heroku platform, which includes a separate git repository.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"This heroku repository is added to the list of tracked repositories and can be observed with git remote -v.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"heroku https://git.heroku.com/my-app-name.git (fetch)\nheroku https://git.heroku.com/my-app-name.git (push)\norigin https://github.com/milesfrain/GenieOnHeroku.git (fetch)\norigin https://github.com/milesfrain/GenieOnHeroku.git (push)","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"We are using a buildpack for Julia. This runs many of the common deployment operations required for Julia projects. It relies on the directory layout found in the example project, with Project.toml, Manifest.toml in the root, and all Julia code in the src directory.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#Deploy-your-app","page":"Deploying to Heroku with Buildpacks","title":"Deploy your app","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"git push heroku master","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"This pushes your current branch of your local repo to the heroku remote repo's master branch.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"Heroku will automatically execute the commands described in the Julia buildpack and Procfile of this latest push.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"You must push to the heroku master branch to trigger an automated deploy.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#Open-your-app's-webpage","page":"Deploying to Heroku with Buildpacks","title":"Open your app's webpage","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"heroku open -a $HEROKU_APP_NAME","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"This is a convenience command to open your app's webpage in your browser.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"The webpage is: https://$HEROKU_APP_NAME.herokuapp.com/","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"For example: https://my-app-name.herokuapp.com/","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#View-app-logs","page":"Deploying to Heroku with Buildpacks","title":"View app logs","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"heroku logs -tail -a $HEROKU_APP_NAME","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"This is another convenience command to launch a log viewer that remains open to show the latest status of your app.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"The println statements from Julia will also appear here.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"Exit this viewer with Ctrl-C.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"Logs can also be viewed from the Heroku web dashboard. For example: https://dashboard.heroku.com/apps/my-app-name/logs","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#Deploy-app-updates-changes","page":"Deploying to Heroku with Buildpacks","title":"Deploy app updates changes","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"To deploy any changes made to your app, simply commit those changes locally, and re-push to heroku.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"\ngit commit -am \"my commit message\"\ngit push heroku master","category":"page"},{"location":"API/cookies.html","page":"Cookies","title":"Cookies","text":"CurrentModule = Cookies","category":"page"},{"location":"API/cookies.html","page":"Cookies","title":"Cookies","text":"Cookies.Dict\nget\ngetcookies\nset!\nnullablevalue","category":"page"},{"location":"API/cookies.html#Base.Dict","page":"Cookies","title":"Base.Dict","text":"Dict([itr])\n\nDict{K,V}() constructs a hash table with keys of type K and values of type V. Keys are compared with isequal and hashed with hash.\n\nGiven a single iterable argument, constructs a Dict whose key-value pairs are taken from 2-tuples (key,value) generated by the argument.\n\nExamples\n\njulia> Dict([(\"A\", 1), (\"B\", 2)])\nDict{String, Int64} with 2 entries:\n \"B\" => 2\n \"A\" => 1\n\nAlternatively, a sequence of pair arguments may be passed.\n\njulia> Dict(\"A\"=>1, \"B\"=>2)\nDict{String, Int64} with 2 entries:\n \"B\" => 2\n \"A\" => 1\n\n\n\n\n\n","category":"type"},{"location":"API/cookies.html#Genie.Cookies.get","page":"Cookies","title":"Genie.Cookies.get","text":"get(payload::Union{HTTP.Response,HTTP.Request}, key::Union{String,Symbol}, default::T; encrypted::Bool = true)::T where T\n\nAttempts to get the Cookie value stored at key within payload. If the key is not set, the default value is returned.\n\nArguments\n\npayload::Union{HTTP.Response,HTTP.Request}: the request or response object containing the Cookie headers\nkey::Union{String,Symbol}: the name of the cookie value\ndefault::T: default value to be returned if no cookie value is set at key\nencrypted::Bool: if true the value stored on the cookie is automatically decrypted\n\n\n\n\n\nget(res::HTTP.Response, key::Union{String,Symbol}) :: Union{Nothing,String}\n\nRetrieves a value stored on the cookie as key from the Respose object.\n\nArguments\n\npayload::Union{HTTP.Response,HTTP.Request}: the request or response object containing the Cookie headers\nkey::Union{String,Symbol}: the name of the cookie value\nencrypted::Bool: if true the value stored on the cookie is automatically decrypted\n\n\n\n\n\nget(req::Request, key::Union{String,Symbol}) :: Union{Nothing,String}\n\nRetrieves a value stored on the cookie as key from the Request object.\n\nArguments\n\nreq::HTTP.Request: the request or response object containing the Cookie headers\nkey::Union{String,Symbol}: the name of the cookie value\nencrypted::Bool: if true the value stored on the cookie is automatically decrypted\n\n\n\n\n\n","category":"function"},{"location":"API/cookies.html#Genie.Cookies.getcookies","page":"Cookies","title":"Genie.Cookies.getcookies","text":"getcookies(req::HTTP.Request) :: Vector{HTTP.Cookies.Cookie}\n\nExtracts cookies from within req\n\n\n\n\n\ngetcookies(req::HTTP.Request) :: Vector{HTTP.Cookies.Cookie}\n\nExtracts cookies from within req, filtering them by matching name.\n\n\n\n\n\n","category":"function"},{"location":"API/cookies.html#Genie.Cookies.set!","page":"Cookies","title":"Genie.Cookies.set!","text":"set!(res::HTTP.Response, key::Union{String,Symbol}, value::Any, attributes::Dict; encrypted::Bool = true) :: HTTP.Response\n\nSets value under the key label on the Cookie.\n\nArguments\n\nres::HTTP.Response: the HTTP.Response object\nkey::Union{String,Symbol}: the key for storing the cookie value\nvalue::Any: the cookie value\nattributes::Dict: additional cookie attributes, such as path, httponly, maxage\nencrypted::Bool: if true the value is stored encoded\n\n\n\n\n\n","category":"function"},{"location":"API/cookies.html#Genie.Cookies.nullablevalue","page":"Cookies","title":"Genie.Cookies.nullablevalue","text":"nullablevalue(payload::Union{HTTP.Response,HTTP.Request}, key::Union{String,Symbol}; encrypted::Bool = true)\n\nAttempts to retrieve a cookie value stored at key in the payload object and returns a Union{Nothing,String}\n\nArguments\n\npayload::Union{HTTP.Response,HTTP.Request}: the request or response object containing the Cookie headers\nkey::Union{String,Symbol}: the name of the cookie value\nencrypted::Bool: if true the value stored on the cookie is automatically decrypted\n\n\n\n\n\n","category":"function"},{"location":"API/genie.html","page":"Genie","title":"Genie","text":"CurrentModule = Genie","category":"page"},{"location":"API/genie.html","page":"Genie","title":"Genie","text":"bootstrap\ndown\ndown!\ngenie\ngo\nisrunning\nloadapp\nrun\nup","category":"page"},{"location":"API/genie.html#Genie.bootstrap","page":"Genie","title":"Genie.bootstrap","text":"genie() :: Union{Nothing,Sockets.TCPServer}\n\n\n\n\n\n","category":"function"},{"location":"API/genie.html#Genie.down","page":"Genie","title":"Genie.down","text":"down(; webserver::Bool = true, websockets::Bool = true) :: ServersCollection\n\nShuts down the servers optionally indicating which of the webserver and websockets servers to be stopped. It does not remove the servers from the SERVERS collection. Returns the collection.\n\n\n\n\n\n","category":"function"},{"location":"API/genie.html#Genie.down!","page":"Genie","title":"Genie.down!","text":"function down!(; webserver::Bool = true, websockets::Bool = true) :: Vector{ServersCollection}\n\nShuts down all the servers and empties the SERVERS collection. Returns the empty collection.\n\n\n\n\n\n","category":"function"},{"location":"API/genie.html#Genie.genie","page":"Genie","title":"Genie.genie","text":"genie() :: Union{Nothing,Sockets.TCPServer}\n\n\n\n\n\n","category":"function"},{"location":"API/genie.html#Genie.go","page":"Genie","title":"Genie.go","text":"loadapp(path::String = \".\"; autostart::Bool = false) :: Nothing\n\nLoads an existing Genie app from the file system, within the current Julia REPL session.\n\nArguments\n\npath::String: the path to the Genie app on the file system.\nautostart::Bool: automatically start the app upon loading it.\n\nExamples\n\nshell> tree -L 1\n.\n├── Manifest.toml\n├── Project.toml\n├── bin\n├── bootstrap.jl\n├── config\n├── env.jl\n├── genie.jl\n├── log\n├── public\n├── routes.jl\n└── src\n\n5 directories, 6 files\n\njulia> using Genie\n\njulia> Genie.loadapp(\".\")\n _____ _\n| __|___ ___|_|___\n| | | -_| | | -_|\n|_____|___|_|_|_|___|\n\n┌ Info:\n│ Starting Genie in >> DEV << mode\n└\n[ Info: Logging to file at MyGenieApp/log/dev.log\n\n\n\n\n\n","category":"function"},{"location":"API/genie.html#Genie.loadapp","page":"Genie","title":"Genie.loadapp","text":"loadapp(path::String = \".\"; autostart::Bool = false) :: Nothing\n\nLoads an existing Genie app from the file system, within the current Julia REPL session.\n\nArguments\n\npath::String: the path to the Genie app on the file system.\nautostart::Bool: automatically start the app upon loading it.\n\nExamples\n\nshell> tree -L 1\n.\n├── Manifest.toml\n├── Project.toml\n├── bin\n├── bootstrap.jl\n├── config\n├── env.jl\n├── genie.jl\n├── log\n├── public\n├── routes.jl\n└── src\n\n5 directories, 6 files\n\njulia> using Genie\n\njulia> Genie.loadapp(\".\")\n _____ _\n| __|___ ___|_|___\n| | | -_| | | -_|\n|_____|___|_|_|_|___|\n\n┌ Info:\n│ Starting Genie in >> DEV << mode\n└\n[ Info: Logging to file at MyGenieApp/log/dev.log\n\n\n\n\n\n","category":"function"},{"location":"API/genie.html#Genie.run","page":"Genie","title":"Genie.run","text":"run() :: Nothing\n\nRuns the Genie app by parsing the command line args and invoking the corresponding actions. Used internally to parse command line arguments.\n\n\n\n\n\n","category":"function"},{"location":"API/genie.html#Genie.up","page":"Genie","title":"Genie.up","text":"up(port::Int = Genie.config.server_port, host::String = Genie.config.server_host;\n ws_port::Int = Genie.config.websockets_port, async::Bool = ! Genie.config.run_as_server) :: Nothing\n\nStarts the web server. Alias for Server.up\n\nArguments\n\nport::Int: the port used by the web server\nhost::String: the host used by the web server\nws_port::Int: the port used by the Web Sockets server\nasync::Bool: run the web server task asynchronously\n\nExamples\n\njulia> up(8000, \"127.0.0.1\", async = false)\n[ Info: Ready!\nWeb Server starting at http://127.0.0.1:8000\n\n\n\n\n\n","category":"function"},{"location":"index.html#Genie","page":"Home","title":"Genie","text":"","category":"section"},{"location":"index.html","page":"Home","title":"Home","text":"(Image: Genie Logo)","category":"page"},{"location":"index.html#Genie-2","page":"Home","title":"Genie","text":"","category":"section"},{"location":"index.html#The-highly-productive-Julia-web-framework","page":"Home","title":"The highly productive Julia web framework","text":"","category":"section"},{"location":"index.html","page":"Home","title":"Home","text":"Genie is a full-stack MVC web framework that provides a streamlined and efficient workflow for developing modern web applications. It builds on Julia's strengths (high-level, high-performance, dynamic, JIT compiled), exposing a rich API and a powerful toolset for productive web development.","category":"page"},{"location":"index.html#Current-status","page":"Home","title":"Current status","text":"","category":"section"},{"location":"index.html","page":"Home","title":"Home","text":"Genie is compatible with Julia v1.6 and up.","category":"page"},{"location":"index.html","page":"Home","title":"Home","text":"","category":"page"},{"location":"index.html#Documentation","page":"Home","title":"Documentation","text":"","category":"section"},{"location":"index.html","page":"Home","title":"Home","text":"https://genieframework.github.io/Genie.jl/dev/","category":"page"},{"location":"index.html","page":"Home","title":"Home","text":"","category":"page"},{"location":"index.html#Acknowledgements","page":"Home","title":"Acknowledgements","text":"","category":"section"},{"location":"index.html","page":"Home","title":"Home","text":"Genie uses a multitude of packages that have been kindly contributed by the Julia community.\nThe awesome Genie logo was designed by Alvaro Casanova.","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html#Adding-your-existing-Julia-code-into-Genie-apps","page":"Adding your libraries into Genie","title":"Adding your existing Julia code into Genie apps","text":"","category":"section"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"If you have existing Julia code (modules and files) which you'd like to quickly integrate into a web app, Genie provides an easy way to add and load your code.","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html#Adding-your-Julia-code-to-the-lib/-folder","page":"Adding your libraries into Genie","title":"Adding your Julia code to the lib/ folder","text":"","category":"section"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"If you have some Julia code which you'd like to integrate in a Genie app, the simplest thing is to add the files to the lib/ folder. The files (and folders) in the lib/ folder are automatically loaded by Genie recursively. This means that you can also add folders under lib/, and they will be recursively loaded (included) into the app. Beware though that this only happens when the Genie app is initially loaded. Hence, an app restart will be required if you add files and folders after the app is started.","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"HEADS UP","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"Genie won't create the lib/ folder by default. If the lib/ folder is not present in the root of the app, just create it yourself:","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"julia> mkdir(\"lib\")","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"Once your code is added to the lib/ folder, it will become available in your app's environment. For example, say we have a file called lib/MyLib.jl:","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"# lib/MyLib.jl\nmodule MyLib\n\nusing Dates\n\nfunction isitfriday()\n Dates.dayofweek(Dates.now()) == Dates.Friday\nend\n\nend","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"Assuming that the name of your Genie app (which is also the name of your main module in src/) is MyGenieApp, the modules loaded from lib/ will be available under the MyGenieApp namespace as MyGenieApp.MyLib.","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"HEADS UP","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"Instead of using the actual Genie app (main module) name, we can also use the alias ..Main.UserApp.","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"So we can reference and use our modules in lib/ in routes.jl as follows:","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"# routes.jl\nusing Genie\nusing MyGenieApp.MyLib # or using ..Main.UserApp.MyLib\n\nroute(\"/friday\") do\n MyLib.isitfriday() ? \"Yes, it's Friday!\" : \"No, not yet :(\"\nend","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"Use the lib/ folder to host your Julia code so that Genie knows where to look in order to load it and make it available throughout the application.","category":"page"},{"location":"tutorials/8--Handling_File_Uploads.html#Handling-file-uploads","page":"Uploading files","title":"Handling file uploads","text":"","category":"section"},{"location":"tutorials/8--Handling_File_Uploads.html","page":"Uploading files","title":"Uploading files","text":"Genie has built-in support for working with file uploads. The collection of uploaded files (as POST variables) can be accessed through the Requests.filespayload method. Or, we can retrieve the data corresponding to a given file form input by using Requests.filespayload(key) – where key is the name of the file input in the form.","category":"page"},{"location":"tutorials/8--Handling_File_Uploads.html","page":"Uploading files","title":"Uploading files","text":"In the following snippet we configure two routes in the root of the app (/): the first route, handling GET requests, displays an upload form. The second route, handling POST requests, processes the uploads, generating a file from the uploaded data, saving it, and displaying the file stats.","category":"page"},{"location":"tutorials/8--Handling_File_Uploads.html","page":"Uploading files","title":"Uploading files","text":"","category":"page"},{"location":"tutorials/8--Handling_File_Uploads.html","page":"Uploading files","title":"Uploading files","text":"HEADS UP","category":"page"},{"location":"tutorials/8--Handling_File_Uploads.html","page":"Uploading files","title":"Uploading files","text":"Notice that we can define multiple routes at the same URL if they have different methods, in our case GET and POST.","category":"page"},{"location":"tutorials/8--Handling_File_Uploads.html","page":"Uploading files","title":"Uploading files","text":"","category":"page"},{"location":"tutorials/8--Handling_File_Uploads.html#Example","page":"Uploading files","title":"Example","text":"","category":"section"},{"location":"tutorials/8--Handling_File_Uploads.html","page":"Uploading files","title":"Uploading files","text":"using Genie, Genie.Router, Genie.Renderer.Html, Genie.Requests\n\nform = \"\"\"\n
      \n
      \n \n
      \n\"\"\"\n\nroute(\"/\") do\n html(form)\nend\n\nroute(\"/\", method = POST) do\n if infilespayload(:yourfile)\n write(filespayload(:yourfile))\n\n stat(filename(filespayload(:yourfile)))\n else\n \"No file uploaded\"\n end\nend\n\nup()","category":"page"},{"location":"tutorials/8--Handling_File_Uploads.html","page":"Uploading files","title":"Uploading files","text":"Upon uploading a file and submitting the form, our app will display the file's stats.","category":"page"},{"location":"API/util.html","page":"Util","title":"Util","text":"CurrentModule = Util","category":"page"},{"location":"API/util.html","page":"Util","title":"Util","text":"expand_nullable\nfile_name_without_extension\nwalk_dir\ntime_to_unixtimestamp","category":"page"},{"location":"API/util.html#Genie.Util.file_name_without_extension","page":"Util","title":"Genie.Util.file_name_without_extension","text":"file_name_without_extension(file_name, extension = \".jl\") :: String\n\nRemoves the file extension extension from file_name.\n\n\n\n\n\n","category":"function"},{"location":"API/util.html#Genie.Util.walk_dir","page":"Util","title":"Genie.Util.walk_dir","text":"function walk_dir(dir, paths = String[]; only_extensions = [\"jl\"], only_files = true, only_dirs = false) :: Vector{String}\n\nRecursively walks dir and produces non directories. If only_files, directories will be skipped. If only_dirs, files will be skipped.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-js.html","page":"JS Renderer","title":"JS Renderer","text":"CurrentModule = Renderer.Js","category":"page"},{"location":"API/renderer-js.html","page":"JS Renderer","title":"JS Renderer","text":"get_template\nto_js\nrender\njs\nGenie.Router.error","category":"page"},{"location":"API/filetemplates.html","page":"FileTemplates","title":"FileTemplates","text":"CurrentModule = FileTemplates","category":"page"},{"location":"API/filetemplates.html","page":"FileTemplates","title":"FileTemplates","text":"appmodule\nnewcontroller\nnewtask\nnewtest","category":"page"},{"location":"API/filetemplates.html#Genie.FileTemplates.appmodule","page":"FileTemplates","title":"Genie.FileTemplates.appmodule","text":"appmodule(path::String)\n\nGenerates a custom app module when a new app is bootstrapped.\n\n\n\n\n\n","category":"function"},{"location":"API/filetemplates.html#Genie.FileTemplates.newcontroller","page":"FileTemplates","title":"Genie.FileTemplates.newcontroller","text":"newcontroller(controller_name::String) :: String\n\nDefault content for a new Genie controller.\n\n\n\n\n\n","category":"function"},{"location":"API/filetemplates.html#Genie.FileTemplates.newtask","page":"FileTemplates","title":"Genie.FileTemplates.newtask","text":"newtask(module_name::String) :: String\n\nDefault content for a new Genie Toolbox task.\n\n\n\n\n\n","category":"function"},{"location":"API/filetemplates.html#Genie.FileTemplates.newtest","page":"FileTemplates","title":"Genie.FileTemplates.newtest","text":"newtest(plural_name::String, singular_name::String) :: String\n\nDefault content for a new test file.\n\n\n\n\n\n","category":"function"},{"location":"tutorials/3--Getting_Started.html#Hello-world-with-Genie","page":"Getting started","title":"Hello world with Genie","text":"","category":"section"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"Here are a few examples to quickly get you started with building Genie web apps.","category":"page"},{"location":"tutorials/3--Getting_Started.html#Running-Genie-interactively-at-the-REPL-or-in-notebooks","page":"Getting started","title":"Running Genie interactively at the REPL or in notebooks","text":"","category":"section"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"The simplest use case is to configure a routing function at the REPL and start the web server. That's all that's needed to run your code on the web page:","category":"page"},{"location":"tutorials/3--Getting_Started.html#Example","page":"Getting started","title":"Example","text":"","category":"section"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"julia> using Genie\n\njulia> route(\"/hello\") do\n \"Hello World\"\n end\n\njulia> up()","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"The route function defines a mapping between a URL (\"/hello\") and a Julia function (a handler) which will be automatically invoked to send the response back to the client. In this case we're sending back the string \"Hello World\".","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"That's all! We have set up an app, a route, and started the web server. Open your favorite web browser and go to http://127.0.0.1:8000/hello to see the result.","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"HEADS UP","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"Keep in mind that Julia JIT-compiles. A function is automatically compiled the first time it is invoked. The function, in this case, is our route handler that is responding to the request. This will make the first response slower as it also includes compilation time. But once the function is compiled, for all the subsequent requests, it will be super fast!","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"","category":"page"},{"location":"tutorials/3--Getting_Started.html#Developing-a-simple-Genie-script","page":"Getting started","title":"Developing a simple Genie script","text":"","category":"section"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"Genie can also be used in custom scripts, for example when building micro-services with Julia. Let's create a simple \"Hello World\" micro-service.","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"Start by creating a new file to host our code – let's call it geniews.jl","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"julia> touch(\"geniews.jl\")","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"Now, open it in the editor:","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"julia> edit(\"geniews.jl\")","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"Add the following code:","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Renderer.Json\n\nroute(\"/hello.html\") do\n html(\"Hello World\")\nend\n\nroute(\"/hello.json\") do\n json(\"Hello World\")\nend\n\nroute(\"/hello.txt\") do\n respond(\"Hello World\", :text)\nend\n\nup(8001, async = false)","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"We begun by defining 2 routes and we used the html and json rendering functions (available in the Renderer.Html and the Renderer.Json modules). These functions are responsible for outputting the data using the correct format and document type (with the correct MIME), in our case HTML data for hello.html, and JSON data for hello.json.","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"The third route serves text responses. As Genie does not provide a specialized text() method for sending text/plain responses, we use the generic respond function, indicating the desired MIME type. In our case :text, corresponding to text/plain. Other available MIME types shortcuts are :xml, :markdown, :javascript and a few others others – and users can register their own mime types and response types as needed or can pass the full mime type as a string, ie \"text/csv\".","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"The up function will launch the web server on port 8001. This time, very important, we instructed it to start the server synchronously (that is, blocking the execution of the script), by passing the async = false argument. This way we make sure that our script stays running. Otherwise, at the end of the script, the Julia process would normally exit, killing our server.","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"In order to launch the script, run $ julia geniews.jl.","category":"page"},{"location":"tutorials/3--Getting_Started.html#Batteries-included","page":"Getting started","title":"Batteries included","text":"","category":"section"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"Genie readily makes available a rich set of features - you have already seen the rendering and the routing modules in action. But for instance, logging (to file and console) can also be easily triggered with one line of code, powerful caching can be enabled with a couple more lines, and so on.","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"The app already handles \"404 Page Not Found\" and \"500 Internal Error\" responses. If you try to access a URL which is not handled by the app, like say http://127.0.0.1:8001/not_here, you'll see Genie's default 404 page. The default error pages can be overwritten with custom ones.","category":"page"},{"location":"API/renderer-html.html","page":"HTML Renderer","title":"HTML Renderer","text":"CurrentModule = Renderer.Html","category":"page"},{"location":"API/renderer-html.html","page":"HTML Renderer","title":"HTML Renderer","text":"normal_element\nprepare_template\nattributes\nparseattr\nnormalize_element\ndenormalize_element\nvoid_element\nskip_element\ninclude_markdown\nget_template\ndoctype\ndoc\nparseview\nrender\nparsehtml\nGenie.Renderer.render\nhtml\nsafe_attr\nparsehtml\nhtml_to_julia\nstring_to_julia\nto_julia\npartial\ntemplate\nread_template_file\nparse_template\nparse_string\nparse\nparsetags\nregister_elements\nregister_element\nregister_normal_element\nregister_void_element\nattr\nfor_each\ncollection\nGenie.Router.error\nserve_error_file\n@yield\nel","category":"page"},{"location":"API/renderer-html.html#Genie.Renderer.Html.normal_element","page":"HTML Renderer","title":"Genie.Renderer.Html.normal_element","text":"normal_element(f::Function, elem::String, attrs::Vector{Pair{Symbol,Any}} = Pair{Symbol,Any}[]) :: HTMLString\n\nGenerates a HTML element in the form <...>\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.prepare_template","page":"HTML Renderer","title":"Genie.Renderer.Html.prepare_template","text":"prepare_template(s::String)\nprepare_template{T}(v::Vector{T})\n\nCleans up the template before rendering (ex by removing empty nodes).\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.attributes","page":"HTML Renderer","title":"Genie.Renderer.Html.attributes","text":"attributes(attrs::Vector{Pair{Symbol,String}} = Vector{Pair{Symbol,String}}()) :: Vector{String}\n\nParses HTML attributes.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.parseattr","page":"HTML Renderer","title":"Genie.Renderer.Html.parseattr","text":"parseattr(attr) :: String\n\nConverts Julia keyword arguments to HTML attributes with illegal Julia chars.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.normalize_element","page":"HTML Renderer","title":"Genie.Renderer.Html.normalize_element","text":"normalize_element(elem::String)\n\nCleans up problematic characters or DOM elements.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.denormalize_element","page":"HTML Renderer","title":"Genie.Renderer.Html.denormalize_element","text":"denormalize_element(elem::String)\n\nReplaces - with the char defined to replace dashes, as Julia does not support them in names.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.void_element","page":"HTML Renderer","title":"Genie.Renderer.Html.void_element","text":"void_element(elem::String, attrs::Vector{Pair{Symbol,String}} = Vector{Pair{Symbol,String}}()) :: HTMLString\n\nGenerates a void HTML element in the form <...>\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.get_template","page":"HTML Renderer","title":"Genie.Renderer.Html.get_template","text":"get_template(path::String; partial::Bool = true, context::Module = @__MODULE__, vars...) :: Function\n\nResolves the inclusion and rendering of a template file\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.doctype","page":"HTML Renderer","title":"Genie.Renderer.Html.doctype","text":"Outputs document's doctype.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.doc","page":"HTML Renderer","title":"Genie.Renderer.Html.doc","text":"Outputs document's doctype.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.parseview","page":"HTML Renderer","title":"Genie.Renderer.Html.parseview","text":"parseview(data::String; partial = false, context::Module = @__MODULE__) :: Function\n\nParses a view file, returning a rendering function. If necessary, the function is JIT-compiled, persisted and loaded into memory.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.render","page":"HTML Renderer","title":"Genie.Renderer.Html.render","text":"render(data::String; context::Module = @__MODULE__, layout::Union{String,Nothing} = nothing, vars...) :: Function\n\nRenders the string as an HTML view.\n\n\n\n\n\nrender(viewfile::Genie.Renderer.FilePath; layout::Union{Nothing,Genie.Renderer.FilePath} = nothing, context::Module = @__MODULE__, vars...) :: Function\n\nRenders the template file as an HTML view.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.parsehtml","page":"HTML Renderer","title":"Genie.Renderer.Html.parsehtml","text":"parsehtml(input::String; partial::Bool = true) :: String\n\n\n\n\n\nparsehtml(elem, output; partial = true) :: String\n\nParses a HTML tree structure into a string of Julia code.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.render","page":"HTML Renderer","title":"Genie.Renderer.render","text":"render\n\nAbstract function that needs to be specialized by individual renderers.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.html","page":"HTML Renderer","title":"Genie.Renderer.Html.html","text":"html(data::String; context::Module = @__MODULE__, status::Int = 200, headers::HTTPHeaders = HTTPHeaders(), layout::Union{String,Nothing} = nothing, vars...) :: HTTP.Response\n\nParses the data input as HTML, returning a HTML HTTP Response.\n\nArguments\n\ndata::String: the HTML string to be rendered\ncontext::Module: the module in which the variables are evaluated (in order to provide the scope for vars). Usually the controller.\nstatus::Int: status code of the response\nheaders::HTTPHeaders: HTTP response headers\nlayout::Union{String,Nothing}: layout file for rendering data\n\nExample\n\njulia> html(\"

      Welcome $(vars(:name))

      \", layout = \"
      <% @yield %>
      \", name = \"Adrian\")\nHTTP.Messages.Response:\n\"\nHTTP/1.1 200 OK\nContent-Type: text/html; charset=utf-8\n\n

      Welcome Adrian

      \n
      \"\n\n\n\n\n\nhtml(md::Markdown.MD; context::Module = @__MODULE__, status::Int = 200, headers::Genie.Renderer.HTTPHeaders = Genie.Renderer.HTTPHeaders(), layout::Union{String,Nothing} = nothing, forceparse::Bool = false, vars...) :: Genie.Renderer.HTTP.Response\n\nMarkdown view rendering\n\n\n\n\n\nhtml(viewfile::FilePath; layout::Union{Nothing,FilePath} = nothing,\n context::Module = @__MODULE__, status::Int = 200, headers::HTTPHeaders = HTTPHeaders(), vars...) :: HTTP.Response\n\nParses and renders the HTML viewfile, optionally rendering it within the layout file. Valid file format is .html.jl.\n\nArguments\n\nviewfile::FilePath: filesystem path to the view file as a Renderer.FilePath, ie Renderer.filepath(\"/path/to/file.html.jl\") or path\"/path/to/file.html.jl\"\nlayout::FilePath: filesystem path to the layout file as a Renderer.FilePath, ie Renderer.FilePath(\"/path/to/file.html.jl\") or path\"/path/to/file.html.jl\"\ncontext::Module: the module in which the variables are evaluated (in order to provide the scope for vars). Usually the controller.\nstatus::Int: status code of the response\nheaders::HTTPHeaders: HTTP response headers\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.safe_attr","page":"HTML Renderer","title":"Genie.Renderer.Html.safe_attr","text":"safe_attr(attr) :: String\n\nReplaces illegal Julia characters from HTML attributes with safe ones, to be used as keyword arguments.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.html_to_julia","page":"HTML Renderer","title":"Genie.Renderer.Html.html_to_julia","text":"html_to_julia(file_path::String; partial = true) :: String\n\nConverts a HTML document to Julia code.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.string_to_julia","page":"HTML Renderer","title":"Genie.Renderer.Html.string_to_julia","text":"string_to_julia(content::String; partial = true, f_name::Union{Symbol,Nothing} = nothing, prepend = \"\") :: String\n\nConverts string view data to Julia code\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.to_julia","page":"HTML Renderer","title":"Genie.Renderer.Html.to_julia","text":"to_julia(input::String, f::Function; partial = true, f_name::Union{Symbol,Nothing} = nothing, prepend = \"\") :: String\n\nConverts an input file to Julia code\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.partial","page":"HTML Renderer","title":"Genie.Renderer.Html.partial","text":"partial(path::String; context::Module = @__MODULE__, vars...) :: String\n\nRenders (includes) a view partial within a larger view or layout file.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.template","page":"HTML Renderer","title":"Genie.Renderer.Html.template","text":"template(path::String; partial::Bool = true, context::Module = @__MODULE__, vars...) :: String\n\nRenders a template file.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.read_template_file","page":"HTML Renderer","title":"Genie.Renderer.Html.read_template_file","text":"read_template_file(file_path::String) :: String\n\nReads file_path template from disk.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.parse_template","page":"HTML Renderer","title":"Genie.Renderer.Html.parse_template","text":"parse_template(file_path::String; partial = true) :: String\n\nParses a HTML file into Julia code.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.parse_string","page":"HTML Renderer","title":"Genie.Renderer.Html.parse_string","text":"parse_string(data::String; partial = true) :: String\n\nParses a HTML string into Julia code.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.register_elements","page":"HTML Renderer","title":"Genie.Renderer.Html.register_elements","text":"register_elements() :: Nothing\n\nGenerated functions that represent Julia functions definitions corresponding to HTML elements.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.register_element","page":"HTML Renderer","title":"Genie.Renderer.Html.register_element","text":"register_element(elem::Union{Symbol,String}, elem_type::Union{Symbol,String} = :normal; context = @__MODULE__) :: Nothing\n\nGenerates a Julia function representing an HTML element.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.register_normal_element","page":"HTML Renderer","title":"Genie.Renderer.Html.register_normal_element","text":"register_normal_element(elem::Union{Symbol,String}; context = @__MODULE__) :: Nothing\n\nGenerates a Julia function representing a \"normal\" HTML element: that is an element with a closing tag, ...\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.register_void_element","page":"HTML Renderer","title":"Genie.Renderer.Html.register_void_element","text":"register_void_element(elem::Union{Symbol,String}; context::Module = @__MODULE__) :: Nothing\n\nGenerates a Julia function representing a \"void\" HTML element: that is an element without a closing tag, \n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.for_each","page":"HTML Renderer","title":"Genie.Renderer.Html.for_each","text":"for_each(f::Function, v)\n\nIterates over the v Vector and applies function f for each element. The results of each iteration are concatenated and the final string is returned.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.collection","page":"HTML Renderer","title":"Genie.Renderer.Html.collection","text":"collection(template::Function, collection::Vector{T})::String where {T}\n\nCreates a view fragment by repeateadly applying a function to each element of the collection.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Router.error","page":"HTML Renderer","title":"Genie.Router.error","text":"error\n\nNot implemented function for error response.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.serve_error_file","page":"HTML Renderer","title":"Genie.Renderer.Html.serve_error_file","text":"serve_error_file(error_code::Int, error_message::String = \"\", params::Dict{Symbol,Any} = Dict{Symbol,Any}()) :: Response\n\nServes the error file correspoding to error_code and current environment.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.@yield","page":"HTML Renderer","title":"Genie.Renderer.Html.@yield","text":"@yield\n\nOutputs the rendering of the view within the template.\n\n\n\n\n\n","category":"macro"},{"location":"API/renderer.html","page":"Renderer","title":"Renderer","text":"CurrentModule = Renderer","category":"page"},{"location":"API/renderer.html","page":"Renderer","title":"Renderer","text":"WebRenderable\nrender\nredirect\nhasrequested\nrespond\nregistervars\ninjectvars\nview_file_info\nvars_signature\nfunction_name\nm_name\nbuild_is_stale\nbuild_module\npreparebuilds\npurgebuilds\nchangebuilds\nset_negotiated_content\nnegotiate_content","category":"page"},{"location":"API/renderer.html#Genie.Renderer.WebRenderable","page":"Renderer","title":"Genie.Renderer.WebRenderable","text":"mutable struct WebRenderable\n\nRepresents an object that can be rendered on the web as a HTTP Response\n\n\n\n\n\n","category":"type"},{"location":"API/renderer.html#Genie.Renderer.redirect","page":"Renderer","title":"Genie.Renderer.redirect","text":"Sets redirect headers and prepares the Response. It accepts 3 parameters: 1 - Label of a Route (to learn more, see the advanced routes section) 2 - Default HTTP 302 Found Status: indicates that the provided resource will be changed to a URL provided 3 - Tuples (key, value) to define the HTTP request header\n\nExample: julia> Genie.Renderer.redirect(:index, 302, Dict(\"Content-Type\" => \"application/json; charset=UTF-8\"))\n\nHTTP.Messages.Response: HTTP/1.1 302 Moved Temporarily Content-Type: application/json; charset=UTF-8 Location: /index\n\nRedirecting you to /index\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.hasrequested","page":"Renderer","title":"Genie.Renderer.hasrequested","text":"hasrequested(content_type::Symbol) :: Bool\n\nChecks wheter or not the requested content type matches content_type.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.respond","page":"Renderer","title":"Genie.Renderer.respond","text":"Constructs a Response corresponding to the Content-Type of the request.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.registervars","page":"Renderer","title":"Genie.Renderer.registervars","text":"registervars(vs...) :: Nothing\n\nLoads the rendering vars into the task's scope\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.view_file_info","page":"Renderer","title":"Genie.Renderer.view_file_info","text":"view_file_info(path::String, supported_extensions::Vector{String}) :: Tuple{String,String}\n\nExtracts path and extension info about a file\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.vars_signature","page":"Renderer","title":"Genie.Renderer.vars_signature","text":"vars_signature() :: String\n\nCollects the names of the view vars in order to create a unique hash/salt to identify compiled views with different vars.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.function_name","page":"Renderer","title":"Genie.Renderer.function_name","text":"function_name(file_path::String)\n\nGenerates function name for generated HTML+Julia views.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.m_name","page":"Renderer","title":"Genie.Renderer.m_name","text":"m_name(file_path::String)\n\nGenerates module name for generated HTML+Julia views.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.build_is_stale","page":"Renderer","title":"Genie.Renderer.build_is_stale","text":"build_is_stale(file_path::String, build_path::String) :: Bool\n\nChecks if the view template has been changed since the last time the template was compiled.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.build_module","page":"Renderer","title":"Genie.Renderer.build_module","text":"build_module(content::String, path::String, mod_name::String) :: String\n\nPersists compiled Julia view data to file and returns the path\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.preparebuilds","page":"Renderer","title":"Genie.Renderer.preparebuilds","text":"preparebuilds() :: Bool\n\nSets up the build folder and the build module file for generating the compiled views.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.purgebuilds","page":"Renderer","title":"Genie.Renderer.purgebuilds","text":"purgebuilds(subfolder = BUILD_NAME) :: Bool\n\nRemoves the views builds folders with all the generated views.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.changebuilds","page":"Renderer","title":"Genie.Renderer.changebuilds","text":"changebuilds(subfolder = BUILD_NAME) :: Bool\n\nChanges/creates a new builds folder.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.set_negotiated_content","page":"Renderer","title":"Genie.Renderer.set_negotiated_content","text":"set_negotiated_content(req::HTTP.Request, res::HTTP.Response, params::Dict{Symbol,Any})\n\nConfigures the request, response, and params response content type based on the request and defaults.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.negotiate_content","page":"Renderer","title":"Genie.Renderer.negotiate_content","text":"negotiate_content(req::Request, res::Response, params::Params) :: Response\n\nComputes the content-type of the Response, based on the information in the Request.\n\n\n\n\n\n","category":"function"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"we'# Developing Genie MVC Apps","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Here is a complete walk-through of developing a feature rich MVC app with Genie, including both user facing web pages, a REST API endpoint, and user authentication.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Getting-started-creating-the-app","page":"Developing MVC web applications","title":"Getting started - creating the app","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"First, let's create a new Genie MVC app. We'll use Genie's app generator, so first let's make sure we have Genie installed.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Let's start a Julia REPL and add Genie:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"pkg> add Genie # press ] from julia> prompt to enter Pkg mode","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Now, to create the app:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> using Genie\r\n\r\njulia> Genie.Generator.newapp_mvc(\"Watch Tonight\")","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"HEADS UP","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"The newapp_mvc function creates a new app with the given name, although spaces are not allowed in the name and Genie will automatically correct it as WatchTonight. WatchTonight is the name of the app and the name of the main module of the project, in the src/ folder. You will see it used when we will reference the various files in the project, in the using statements.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Genie will bootstrap a new application for us, creating the necessary files and installing dependencies. As we're creating a MVC app, Genie will offer to install support for SearchLight, Genie's ORM, and will ask what database backend we'll want to use:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Please choose the DB backend you want to use:\r\n1. SQLite\r\n2. MySQL\r\n3. PostgreSQL\r\nInput 1, 2 or 3 and press ENTER to confirm","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Note that if you select an option other than SQLite, you will need to manually create the database outside of Genie. Currently, Genie only automatically creates SQLite databases.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We'll use SQLite in this demo, so let's press \"1\". Once the process is completed, Genie will start the new application at http://127.0.0.1:8000. We can open it in the browser to see the default Genie home page.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#How-does-this-work?","page":"Developing MVC web applications","title":"How does this work?","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Genie uses the concept of routes and routing in order to map a URL to a request handler (a Julia function) within the app. If we edit the routes.jl file we will see that is has defined a route that states that for any requests to the / URL, the app will display a static file called welcome.html (which can be found in the public/ folder):","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"route(\"/\") do\r\n serve_static_file(\"welcome.html\")\r\nend","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Connecting-to-the-database","page":"Developing MVC web applications","title":"Connecting to the database","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"In order to configure the database connection we need to edit the db/connection.yml file, to make it look like this:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"env: ENV[\"GENIE_ENV\"]\r\n\r\ndev:\r\n adapter: SQLite\r\n database: db/netflix_catalog.sqlite","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Now let's manually load the database configuration:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> include(joinpath(\"config\", \"initializers\", \"searchlight.jl\"))","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Creating-a-Movie-resource","page":"Developing MVC web applications","title":"Creating a Movie resource","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"A resource is an entity exposed by the application at a URL. In a Genie MVC app it represents a bundle of Model, views, and Controller files - as well as possible additional files such as migration files for creating a database table, tests, a model data validator, etc.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"In the REPL run:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> Genie.Generator.newresource(\"movie\")\r\n\r\njulia> using SearchLight\r\n\r\njulia> SearchLight.Generator.newresource(\"movie\")","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"This should create a series of files to represent the Movie resource - just take a look at the output to see what and where was created.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Creating-the-DB-table-using-the-database-migration","page":"Developing MVC web applications","title":"Creating the DB table using the database migration","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We need to edit the migrations file that was just created in db/migrations/. Look for a file that ends in _create_table_movies.jl and make it look like this:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"module CreateTableMovies\r\n\r\nimport SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table\r\n\r\nfunction up()\r\n create_table(:movies) do\r\n [\r\n primary_key()\r\n column(:type, :string, limit = 10)\r\n column(:title, :string, limit = 100)\r\n column(:directors, :string, limit = 100)\r\n column(:actors, :string, limit = 250)\r\n column(:country, :string, limit = 100)\r\n column(:year, :integer)\r\n column(:rating, :string, limit = 10)\r\n column(:categories, :string, limit = 100)\r\n column(:description, :string, limit = 1_000)\r\n ]\r\n end\r\n\r\n add_index(:movies, :title)\r\n add_index(:movies, :actors)\r\n add_index(:movies, :categories)\r\n add_index(:movies, :description)\r\nend\r\n\r\nfunction down()\r\n drop_table(:movies)\r\nend\r\n\r\nend","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Creating-the-migrations-table","page":"Developing MVC web applications","title":"Creating the migrations table","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"In order to be able to manage the app's migrations, we need to create the DB table used by SearchLight's migration system. This is easily done using SearchLight's generators:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> SearchLight.Migration.init()","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Running-the-migration","page":"Developing MVC web applications","title":"Running the migration","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We can now check the status of the migrations:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> SearchLight.Migration.status()","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We should see that we have one migration that is DOWN (meaning that we need to run the migration because it has not been executed yet).","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We execute the migration by running the last migration UP:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> SearchLight.Migration.lastup()","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"If you now recheck the status of the migrations, you should see that the migration is now UP.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Creating-the-Movie-model","page":"Developing MVC web applications","title":"Creating the Movie model","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Now that we have the database table, we need to create the model file which allows us manage the data. The file has already been created for us in app/resources/movies/Movies.jl. Edit it and make it look like this:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"module Movies\r\n\r\nimport SearchLight: AbstractModel, DbId\r\nimport Base: @kwdef\r\n\r\nexport Movie\r\n\r\n@kwdef mutable struct Movie <: AbstractModel\r\n id::DbId = DbId()\r\n type::String = \"Movie\"\r\n title::String = \"\"\r\n directors::String = \"\"\r\n actors::String = \"\"\r\n country::String = \"\"\r\n year::Int = 0\r\n rating::String = \"\"\r\n categories::String = \"\"\r\n description::String = \"\"\r\nend\r\n\r\nend","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Interacting-with-the-movies-data","page":"Developing MVC web applications","title":"Interacting with the movies data","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Once our model is created, we can interact with the database:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> using Movies\r\n\r\njulia> m = Movie(title = \"Test movie\", actors = \"John Doe, Jane Doe\")","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We can check if our movie object is persisted (saved to the db):","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> ispersisted(m)\r\nfalse","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"And we can save it:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> save(m)\r\ntrue","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Now we can run various methods against our data:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> count(Movie)\r\n\r\njulia> all(Movie)","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Seeding-the-data","page":"Developing MVC web applications","title":"Seeding the data","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We're now ready to load the movie data into our database - we'll use a short seeding script. First make sure to place the CVS file into the /db/seeds/ folder. Create the seeds file:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> touch(joinpath(\"db\", \"seeds\", \"seed_movies.jl\"))","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"And edit it to look like this:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"using SearchLight, WatchTonight.Movies\r\nusing CSV\r\n\r\nBase.convert(::Type{String}, _::Missing) = \"\"\r\nBase.convert(::Type{Int}, _::Missing) = 0\r\nBase.convert(::Type{Int}, s::String) = parse(Int, s)\r\n\r\nfunction seed()\r\n for row in CSV.Rows(joinpath(@__DIR__, \"netflix_titles.csv\"), limit = 1_000)\r\n m = Movie()\r\n\r\n m.type = row.type\r\n m.title = row.title\r\n m.directors = row.director\r\n m.actors = row.cast\r\n m.country = row.country\r\n m.year = parse(Int, row.release_year)\r\n m.rating = row.rating\r\n m.categories = row.listed_in\r\n m.description = row.description\r\n\r\n save(m)\r\n end\r\nend","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Add CSV.jl as a dependency of the project:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"pkg> add CSV","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"And download the dataset:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> download(\"https://raw.githubusercontent.com/essenciary/genie-watch-tonight/main/db/seeds/netflix_titles.csv\", joinpath(\"db\", \"seeds\", \"netflix_titles.csv\"))","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Now, to seed the db:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> include(joinpath(\"db\", \"seeds\", \"seed_movies.jl\"))\r\njulia> seed()","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Setting-up-the-web-page","page":"Developing MVC web applications","title":"Setting up the web page","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We'll start by adding the route to our handler function. Let's open the routes.jl file and add:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"# routes.jl\r\nusing WatchTonight.MoviesController\r\n\r\nroute(\"/movies\", MoviesController.index)","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"This route declares that the /movies URL will be handled by the MoviesController.index index function. Let's put it in by editing /app/resources/movies/MoviesController.jl:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"module MoviesController\r\n\r\nfunction index()\r\n \"Welcome to movies list!\"\r\nend\r\n\r\nend","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"If we navigate to http://127.0.0.1:8000/movies we should see the welcome.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Let's make this more useful though and display a random movie upon landing here:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"module MoviesController\r\n\r\nusing Genie.Renderer.Html, SearchLight, WatchTonight.Movies\r\n\r\nfunction index()\r\n html(:movies, :index, movies = rand(Movie))\r\nend\r\n\r\nend","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"The index function renders the /app/resources/movies/views/index.jl.html view file as HTML, passing it a random movie into the movies instance. Since we don't have the view file yet, let's add it:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> touch(joinpath(\"app\", \"resources\", \"movies\", \"views\", \"index.jl.html\"))","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Make it look like this:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"

      Watch tonight

      \r\n<%\r\nif ! isempty(movies)\r\n for_each(movies) do movie\r\n partial(joinpath(Genie.config.path_resources, \"movies\", \"views\", \"_movie.jl.html\"), movie = movie)\r\n end\r\nelse\r\n partial(joinpath(Genie.config.path_resources, \"movies\", \"views\", \"_no_results.jl.html\"))\r\nend\r\n%>","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Now to create the _movie.jl.html partial file to render a movie object:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> touch(joinpath(\"app\", \"resources\", \"movies\", \"views\", \"_movie.jl.html\"))","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Edit it like this:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"
      \r\n

      <% movie.title %>

      \r\n\r\n
      \r\n <% movie.year %> |\r\n <% movie.type %> |\r\n <% movie.rating %>\r\n
      \r\n\r\n

      <% movie.description %>

      \r\n\r\n
      Directed by: <% movie.directors %>
      \r\n
      Cast: <% movie.actors %>
      \r\n
      Country: <% movie.country %>
      \r\n
      Categories: <% movie.categories %>
      \r\n
      ","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"And finally, the _no_results.jl.html partial:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> touch(joinpath(\"app\", \"resources\", \"movies\", \"views\", \"_no_results.jl.html\"))","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Which must look like this:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"

      \r\n Sorry, no results were found for \"$(params(:search_movies))\"\r\n

      ","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Using-the-layout-file","page":"Developing MVC web applications","title":"Using the layout file","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Let's make the web page nicer by loading the Twitter Bootstrap CSS library. As it will be used across all the pages of the website, we'll load it in the main layout file. Edit /app/layouts/app.jl.html to look like this:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"\r\n\r\n \r\n \r\n Genie :: The Highly Productive Julia Web Framework\r\n \r\n \r\n \r\n
      \r\n <%\r\n @yield\r\n %>\r\n
      \r\n \r\n","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Adding-the-search-feature","page":"Developing MVC web applications","title":"Adding the search feature","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Now that we can display titles, it's time to implement the search feature. We'll add a search form onto our page. Edit /app/resources/movies/views/index.jl.html to look like this:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"

      Watch tonight

      \r\n\r\n
      \r\n
      \r\n \r\n
      \r\n
      \r\n\r\n<%\r\nif ! isempty(movies)\r\n for_each(movies) do movie\r\n partial(joinpath(Genie.config.path_resources, \"movies\", \"views\", \"_movie.jl.html\"), movie = movie)\r\n end\r\nelse\r\n partial(joinpath(Genie.config.path_resources, \"movies\", \"views\", \"_no_results.jl.html\"))\r\nend\r\n%>","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We have added a HTML
      which submits a query term over GET.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Next, add the route:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"route(\"/movies/search\", MoviesController.search, named = :search_movies)","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"And the MoviesController.search function after updating the using section:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"using Genie, Genie.Renderer, Genie.Renderer.Html, SearchLight, WatchTonight.Movies\r\n\r\nfunction search()\r\n isempty(strip(params(:search_movies))) && redirect(:get_movies)\r\n\r\n movies = find(Movie,\r\n SQLWhereExpression(\"title LIKE ? OR categories LIKE ? OR description LIKE ? OR actors LIKE ?\",\r\n repeat(['%' * params(:search_movies) * '%'], 4)))\r\n\r\n html(:movies, :index, movies = movies)\r\nend","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Time to check our progress: http://127.0.0.1:8000/movies","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Building-the-REST-API","page":"Developing MVC web applications","title":"Building the REST API","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Let's start by adding a new route for the API search:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"route(\"/movies/search_api\", MoviesController.search_api)","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"With the corresponding search_api method in the MoviesController model:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"using Genie, Genie.Renderer, Genie.Renderer.Html, SearchLight, WatchTonight.Movies, Genie.Renderer.Json\r\n\r\nfunction search_api()\r\n movies = find(Movie,\r\n SQLWhereExpression(\"title LIKE ? OR categories LIKE ? OR description LIKE ? OR actors LIKE ?\",\r\n repeat(['%' * params(:search_movies) * '%'], 4)))\r\n\r\n json(Dict(\"movies\" => movies))\r\nend","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Bonus","page":"Developing MVC web applications","title":"Bonus","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Genie makes it easy to add database backed authentication for restricted area of a website, by using the GenieAuthentication plugin. Start by adding package:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"pkg> add GenieAuthentication\r\n\r\njulia> using GenieAuthentication","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Now, to install the plugin files:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> GenieAuthentication.install(@__DIR__)","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"The plugin has created a create table migration that we need to run UP:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> SearchLight.Migration.up(\"CreateTableUsers\")","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Let's generate an Admin controller that we'll want to protect by login:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> Genie.Generator.newcontroller(\"Admin\", pluralize = false)","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Let's load the plugin into the app manually to avoid restarting the app. Upon restarting the application next time, the plugin will be automatically loaded by Genie:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> include(joinpath(\"plugins\", \"genie_authentication.jl\"))","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Time to create an admin user for logging in:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> using WatchTonight.Users\r\n\r\njulia> u = User(email = \"admin@admin\", name = \"Admin\", password = Users.hash_password(\"admin\"), username = \"admin\")\r\n\r\njulia> save!(u)","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We'll also need a route for the admin area:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"using WatchTonight.AdminController\r\n\r\nroute(\"/admin/movies\", AdminController.index, named = :get_home)","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"And finally, the controller code:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"module AdminController\r\n\r\nusing GenieAuthentication, Genie.Renderer, Genie.Exceptions, Genie.Renderer.Html\r\n\r\nfunction index()\r\n authenticated!()\r\n h1(\"Welcome Admin\") |> html\r\nend\r\n\r\nend","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"If we navigate to http://127.0.0.1:8000/admin/movies we'll be asked to logged in. Using admin for the user and admin for the password will allow us to access the password protected section.","category":"page"},{"location":"API/commands.html","page":"Commands","title":"Commands","text":"CurrentModule = Commands","category":"page"},{"location":"API/commands.html","page":"Commands","title":"Commands","text":"called_command\nexecute\nparse_commandline_args","category":"page"},{"location":"API/commands.html#Genie.Commands.called_command","page":"Commands","title":"Genie.Commands.called_command","text":"called_command(args::Dict, key::String) :: Bool\n\nChecks whether or not a certain command was invoked by looking at the command line args.\n\n\n\n\n\n","category":"function"},{"location":"API/commands.html#Genie.Commands.execute","page":"Commands","title":"Genie.Commands.execute","text":"execute(config::Settings) :: Nothing\n\nRuns the requested Genie app command, based on the args passed to the script.\n\n\n\n\n\n","category":"function"},{"location":"API/commands.html#Genie.Commands.parse_commandline_args","page":"Commands","title":"Genie.Commands.parse_commandline_args","text":"parse_commandline_args() :: Dict{String,Any}\n\nExtracts the command line args passed into the app and returns them as a Dict, possibly setting up defaults. Also, it is used by the ArgParse module to populate the command line help for the app -h.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html","page":"Assets","title":"Assets","text":"CurrentModule = Assets","category":"page"},{"location":"API/assets.html","page":"Assets","title":"Assets","text":"add_fileroute\nAssetsConfig\nassets_config!\nassets_endpoint\nasset_file\nasset_path\nasset_route\nchannels\nchannels_route\nchannels_script\nchannels_script_tag\nchannels_subscribe\nchannels_support\ncss_asset\nembedded\nembedded_path\nexternal_assets\nfavicon_support\ninclude_asset\njs_asset\njsliteral\njs_settings\nwebthreads\nwebthreads_endpoint\nwebthreads_push_pull\nwebthreads_route\nwebthreads_script\nwebthreads_script_tag\nwebthreads_subscribe\nwebthreads_support","category":"page"},{"location":"API/assets.html#Genie.Assets.add_fileroute","page":"Assets","title":"Genie.Assets.add_fileroute","text":"addfileroute(assetsconfig::Genie.Assets.AssetsConfig, filename::AbstractString; basedir = pwd(), type::Union{Nothing, String} = nothing, content_type::Union{Nothing, Symbol} = nothing, ext::Union{Nothing, String} = nothing, kwargs...)\n\nHelper function to add a file route to the assets based on asset_config and filename.\n\nExample\n\nadd_fileroute(StippleUI.assets_config, \"Sortable.min.js\")\nadd_fileroute(StippleUI.assets_config, \"vuedraggable.umd.min.js\")\nadd_fileroute(StippleUI.assets_config, \"vuedraggable.umd.min.js.map\", type = \"js\")\nadd_fileroute(StippleUI.assets_config, \"QSortableTree.js\")\n\ndraggabletree_deps() = [\n script(src = \"/stippleui.jl/master/assets/js/sortable.min.js\")\n script(src = \"/stippleui.jl/master/assets/js/vuedraggable.umd.min.js\")\n script(src = \"/stippleui.jl/master/assets/js/qsortabletree.js\")\n]\nStipple.DEPS[:qdraggabletree] = draggabletree_deps\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.AssetsConfig","page":"Assets","title":"Genie.Assets.AssetsConfig","text":"mutable struct AssetsConfig\n\nManages the assets configuration for the current package. Define your own instance of AssetsConfig if you want to add support for asset management for your package through Genie.Assets.\n\n\n\n\n\n","category":"type"},{"location":"API/assets.html#Genie.Assets.assets_config!","page":"Assets","title":"Genie.Assets.assets_config!","text":"assets_config!(packages::Vector{Module}; config...) :: Nothing\nassets_config!(package::Module; config...) :: Nothing\n\nUtility function which allows bulk configuration of the assets.\n\nExample\n\nGenie.Assets.assets_config!([Genie, Stipple, StippleUI], host = \"https://cdn.statically.io/gh/GenieFramework\")\n\n\n\n\n\nassets_config!(; config...) :: Nothing\n\nUpdates the assets configuration for the current package.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.asset_file","page":"Assets","title":"Genie.Assets.asset_file","text":"asset_file(; cwd = \"\", file::String, path::String = \"\", type::String = \"\", prefix::String = \"assets\",\n ext::String = \"\", min::Bool = false, skip_ext::Bool = false) :: String\n\nGenerates the file system path to an asset file.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.asset_path","page":"Assets","title":"Genie.Assets.asset_path","text":"asset_path(; file::String, host::String = Genie.config.base_path, package::String = \"\", version::String = \"\",\n prefix::String = \"assets\", type::String = \"\", path::String = \"\", min::Bool = false,\n ext::String = \"\", skip_ext::Bool = false, query::String = \"\") :: String\nasset_path(file::String; kwargs...) :: String\nasset_path(ac::AssetsConfig, tp::Union{Symbol,String}; type::String = string(tp), path::String = \"\",\n file::String = \"\", ext::String = \"\", skip_ext::Bool = false, query::String = \"\") :: String\n\nGenerates the path to an asset file.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.asset_route","page":"Assets","title":"Genie.Assets.asset_route","text":"asset_route(; file::String, package::String = \"\", version::String = \"\", prefix::String = \"assets\",\n type::String = \"\", path::String = \"\", min::Bool = false,\n ext::String = \"\", skip_ext::Bool = false, query::String = \"\") :: String\nasset_route(file::String; kwargs...) :: String\nasset_route(ac::AssetsConfig, tp::Union{Symbol,String}; type::String = string(tp), path::String = \"\",\n file::String = \"\", ext::String = \"\", skip_ext::Bool = false, query::String = \"\") :: String\n\nGenerates the route to an asset file.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.channels","page":"Assets","title":"Genie.Assets.channels","text":"channels(channel::AbstractString = Genie.config.webchannels_default_route) :: String\n\nOutputs the channels.js file included with the Genie package.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.channels_script","page":"Assets","title":"Genie.Assets.channels_script","text":"channels_script(channel::AbstractString = Genie.config.webchannels_default_route) :: String\n\nOutputs the channels JavaScript content within tags, for embedding into the page.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.channels_subscribe","page":"Assets","title":"Genie.Assets.channels_subscribe","text":"channels_subscribe(channel::AbstractString = Genie.config.webchannels_default_route) :: Nothing\n\nRegisters subscription and unsubscription channels for channel.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.channels_support","page":"Assets","title":"Genie.Assets.channels_support","text":"channels_support(channel = Genie.config.webchannels_default_route) :: String\n\nProvides full web channels support, setting up routes for loading support JS files, web sockets subscription and returning the tags, for embedding into the page.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.webthreads_subscribe","page":"Assets","title":"Genie.Assets.webthreads_subscribe","text":"function webthreads_subscribe(channel) :: Nothing\n\nRegisters subscription and unsubscription routes for channel.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.webthreads_support","page":"Assets","title":"Genie.Assets.webthreads_support","text":"webthreads_support(channel = Genie.config.webthreads_default_route) :: String\n\nProvides full web channels support, setting up routes for loading support JS files, web sockets subscription and returning the \n \"\"\"\nend","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"Now if you reload the page and broadcast the message, it will be picked up by our custom payload handler.","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"We can remove clients that are no longer reachable (for instance, if the browser tab is closed) with:","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"julia> Genie.WebChannels.unsubscribe_disconnected_clients()","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"The output of unsubscribe_disconnected_clients() is the collection of remaining (connected) clients.","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"Heads up!","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"You should routinely unsubscribe_disconnected_clients() to free memory.","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"At any time, we can check the connected clients with Genie.WebChannels.connected_clients() and the disconnected ones with Genie.WebChannels.disconnected_clients().","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html#Pushing-messages-from-the-client","page":"Working with WebSockets","title":"Pushing messages from the client","text":"","category":"section"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"We can also push messages from client to server. As we don't have a UI, we'll use the browser's console and Genie's JavaScript API to send the messages. But first, we need to set up the channel which will receive our message. Run this in the active Julia REPL:","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"channel(\"/____/echo\") do\n @info \"Received: $(params(:payload))\"\nend","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"Now that our endpoint is up, go to the browser's console and run:","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"Genie.WebChannels.sendMessageTo('____', 'echo', 'Hello!')","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"The julia terminal and console will both immediately display the response from the server:","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"Received: Hello!\nGot this payload: Received: Hello!","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html#Summary","page":"Working with WebSockets","title":"Summary","text":"","category":"section"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"This concludes our intro to working with WebSockets in Genie. You now have the knowledge to set up the communication between client and server, send messages from both server and clients, and perform various tasks using the WebChannels API.","category":"page"},{"location":"API/sessions.html","page":"-","title":"-","text":"CurrentModule = Sessions","category":"page"},{"location":"API/sessions.html","page":"-","title":"-","text":"Session\nid\nstart\nset!\nget\nunset!\nisset\npersist\nload\nsession\ninit","category":"page"},{"location":"API/sessions.html#Base.get","page":"-","title":"Base.get","text":"get(collection, key, default)\n\nReturn the value stored for the given key, or the given default value if no mapping for the key is present.\n\ncompat: Julia 1.7\nFor tuples and numbers, this function requires at least Julia 1.7.\n\nExamples\n\njulia> d = Dict(\"a\"=>1, \"b\"=>2);\n\njulia> get(d, \"a\", 3)\n1\n\njulia> get(d, \"c\", 3)\n3\n\n\n\n\n\nget(f::Function, collection, key)\n\nReturn the value stored for the given key, or if no mapping for the key is present, return f(). Use get! to also store the default value in the dictionary.\n\nThis is intended to be called using do block syntax\n\nget(dict, key) do\n # default value calculated here\n time()\nend\n\n\n\n\n\nget(x::Nullable[, y])\n\nAttempt to access the value of x. Returns the value if it is present; otherwise, returns y if provided, or throws a NullException if not.\n\nExamples\n\njulia> get(Nullable(5))\n5\n\njulia> get(Nullable())\nERROR: NullException()\nStacktrace:\n [1] get(::Nullable{Union{}}) at ./nullable.jl:102\n\n\n\n\n\nget(sd,k,v)\n\nReturns the value associated with key k where sd is a SortedDict, or else returns v if k is not in sd. Time: O(c log n)\n\n\n\n\n\nget(collection, key, default)\n\nReturn the value stored for the given key, or the given default value if no mapping for the key is present.\n\nExamples\n\njulia> d = RobinDict(\"a\"=>1, \"b\"=>2);\n\njulia> get(d, \"a\", 3)\n1\n\njulia> get(d, \"c\", 3)\n3\n\n\n\n\n\nget(f::Function, collection, key)\n\nReturn the value stored for the given key, or if no mapping for the key is present, return f(). Use get! to also store the default value in the dictionary.\n\nThis is intended to be called using do block syntax\n\nget(dict, key) do\n # default value calculated here\n time()\nend\n\n\n\n\n\nget(collection, key, default)\n\nReturn the value stored for the given key, or the given default value if no mapping for the key is present.\n\nExamples\n\njulia> d = OrderedRobinDict(\"a\"=>1, \"b\"=>2);\n\njulia> get(d, \"a\", 3)\n1\n\njulia> get(d, \"c\", 3)\n3\n\n\n\n\n\nget(f::Function, collection, key)\n\nReturn the value stored for the given key, or if no mapping for the key is present, return f(). Use get! to also store the default value in the dictionary.\n\nThis is intended to be called using do block syntax\n\nget(dict, key) do\n # default value calculated here\n time()\nend\n\n\n\n\n\nget(collection, key, default)\n\nReturn the value stored for the given key, or the given default value if no mapping for the key is present.\n\nExamples\n\njulia> d = SwissDict(\"a\"=>1, \"b\"=>2);\n\njulia> get(d, \"a\", 3)\n1\n\njulia> get(d, \"c\", 3)\n3\n\n\n\n\n\nget(f::Function, collection, key)\n\nReturn the value stored for the given key, or if no mapping for the key is present, return f(). Use get! to also store the default value in the dictionary.\n\nThis is intended to be called using do block syntax\n\nget(dict, key) do\n # default value calculated here\n time()\nend\n\n\n\n\n\n","category":"function"}] +[{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#Working-With-Genie-Apps:-Intermediate-Topics","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"WARNING: THIS PAGE IS UNDER CONSTRUCTION – THE CONTENT IS USABLE BUT INCOMPLETE","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#Handling-forms","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Handling forms","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Now, the problem is that Bill Gates reads – a lot! It would be much easier if we would allow our users to add a few books themselves, to give us a hand. But since, obviously, we're not going to give them access to our Julia REPL, we should setup a web page with a form. Let's do it.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"We'll start by adding the new routes:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"# routes.jl\nroute(\"/bgbooks/new\", BooksController.new)\nroute(\"/bgbooks/create\", BooksController.create, method = POST, named = :create_book)","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"The first route will be used to display the page with the new book form. The second will be the target page for submitting our form - this page will accept the form's payload. Please note that it's configured to match POST requests and that we gave it a name. We'll use the name in our form so that Genie will dynamically generate the correct links to the corresponding URL (to avoid hard coding URLs). This way we'll make sure that our form will always submit to the right URL, even if we change the route (as long as we don't change the name).","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Now, to add the methods in BooksController. Add these definition under the billgatesbooks function (make sure you add them in BooksController, not in BooksController.API):","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"# BooksController.jl\nfunction new()\n html(:books, :new)\nend\n\nfunction create()\n # code here\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"The new method should be clear: we'll just render a view file called new. As for create, for now it's just a placeholder.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Next, to add our view. Add a blank file called new.jl.html in app/resources/books/views. Using Julia:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"julia> touch(\"app/resources/books/views/new.jl.html\")","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Make sure that it has this content:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"\n

      Add a new book recommended by Bill Gates

      \n

      \n For inspiration you can visit Bill Gates' website\n

      \n\n
      \n
      \n \n
      ","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Notice that the form's action calls the linkto method, passing in the name of the route to generate the URL, resulting in the following HTML:
      .","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"We should also update the BooksController.create method to do something useful with the form data. Let's make it create a new book, persist it to the database and redirect to the list of books. Here is the code:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"# BooksController.jl\nusing Genie.Router, Genie.Renderer\n\nfunction create()\n Book(title = params(:book_title), author = params(:book_author)) |> save && redirect(:get_bgbooks)\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"A few things are worth pointing out in this snippet:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"again, we're accessing the params collection to extract the request data, in this case passing in the names of our","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"form's inputs as parameters.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"We need to bring Genie.Router into scope in order to access params;","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"we're using the redirect method to perform a HTTP redirect. As the argument we're passing in the name of the route,","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"just like we did with the form's action. However, we didn't set any route to use this name. It turns out that Genie gives default names to all the routes.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"We can use these – but a word of notice: these names are generated using the properties of the route, so if the route changes it's possible that the name will change too. So either make sure your route stays unchanged – or explicitly name your routes. The autogenerated name, get_bgbooks corresponds to the method (GET) and the route (bgbooks).","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"In order to get info about the defined routes you can use the Router.named_routes function:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"julia> Router.named_routes()\nOrderedCollections.OrderedDict{Symbol, Genie.Router.Route} with 6 entries:\n :get_bgbooks => [GET] /bgbooks => billgatesbooks | :get_bgbooks\n :get_bgbooks_new => [GET] /bgbooks/new => new | :get_bgbooks_new\n :get => [GET] / => () | :get\n :get_api_v1_bgbooks => [GET] /api/v1/bgbooks | :get_api_v1_bgbooks\n :create_book => [POST] /bgbooks/create | :create_book","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Let's try it out. Input something and submit the form. If everything goes well a new book will be persisted to the database – and it will be added at the bottom of the list of books.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#Uploading-files","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Uploading files","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Our app looks great – but the list of books would be so much better if we'd display the covers as well. Let's do it!","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#Modify-the-database","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Modify the database","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"The first thing we need to do is to modify our table to add a new column, for storing a reference to the name of the cover image. Per best practices, we'll use database migrations to modify the structure of our table:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"julia> SearchLight.Generator.newmigration(\"add cover column\")\n[debug] New table migration created at db/migrations/2019030813344258_add_cover_column.jl","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Now we need to edit the migration file - please make it look like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"# db/migrations/*_add_cover_column.jl\nmodule AddCoverColumn\n\nimport SearchLight.Migrations: add_column, add_index\n\n# SQLite does not support column removal so the `remove_column` method is not implemented in the SearchLightSQLite adapter\n# If using SQLite leave the next line commented -- otherwise uncomment it\n# import SearchLight.Migrations: remove_column\n\nfunction up()\n add_column(:books, :cover, :string)\nend\n\nfunction down()\n # if using the SQLite backend, leave the next line commented -- otherwise uncomment it\n # remove_column(:books, :cover)\nend\n\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Looking good - lets ask SearchLight to run it:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"julia> SearchLight.Migration.lastup()\n[debug] Executed migration AddCoverColumn up","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"If you want to double check, ask SearchLight for the migrations status:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"julia> SearchLight.Migration.status()\n\n| | Module name & status |\n| | File name |\n|---|----------------------------------------|\n| | CreateTableBooks: UP |\n| 1 | 2018100120160530_create_table_books.jl |\n| | AddCoverColumn: UP |\n| 2 | 2019030813344258_add_cover_column.jl |","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Perfect! Now we need to add the new column as a field to the Books.Book model:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"module Books\n\nusing SearchLight, SearchLight.Validation, BooksValidator\n\nexport Book\n\nBase.@kwdef mutable struct Book <: AbstractModel\n id::DbId = DbId()\n title::String = \"\"\n author::String = \"\"\n cover::String = \"\"\nend\n\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"As a quick test we can extend our JSON view and see that all goes well - make it look like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"# app/resources/books/views/billgatesbooks.json.jl\n\"Bill's Gates list of recommended books\" => [Dict(\"author\" => b.author,\n \"title\" => b.title,\n \"cover\" => b.cover) for b in books]","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"If we navigate http://localhost:8000/api/v1/bgbooks you should see the newly added \"cover\" property (empty, but present).","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#Heads-up!","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Heads up!","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Sometimes Julia/Genie/Revise fails to update structs on changes. If you get an error saying that Book does not have a cover field or that it can not be changed, you'll need to restart the Genie app.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#File-uploading","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"File uploading","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Next step, extending our form to upload images (book covers). Please edit the new.jl.html view file as follows:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"

      Add a new book recommended by Bill Gates

      \n

      \n For inspiration you can visit Bill Gates' website\n

      \n\n
      \n
      \n
      \n \n
      ","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"The new bits are:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"we added a new attribute to our
      tag: enctype=\"multipart/form-data\". This is required in order to support files payloads.\nthere's a new input of type file: ","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"You can see the updated form by visiting http://localhost:8000/bgbooks/new","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Now, time to add a new book, with the cover! How about \"Identity\" by Francis Fukuyama? Sounds good. You can use whatever image you want for the cover, or maybe borrow the one from Bill Gates, I hope he won't mind https://www.gatesnotes.com/-/media/Images/GoodReadsBookCovers/Identity.png. Just download the file to your computer so you can upload it through our form.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Almost there - now to add the logic for handling the uploaded file server side. Please update the BooksController.create method to look like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"# BooksController\nfunction create()\n cover_path = if haskey(filespayload(), \"book_cover\")\n path = joinpath(\"img\", \"covers\", filespayload(\"book_cover\").name)\n write(joinpath(\"public\", path), IOBuffer(filespayload(\"book_cover\").data))\n\n path\n else\n \"\"\n end\n\n Book( title = params(:book_title),\n author = params(:book_author),\n cover = cover_path) |> save && redirect(:get_bgbooks)\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Also, very important, you need to make sure that BooksController is using Genie.Requests.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Regarding the code, there's nothing very fancy about it. First we check if the files payload contains an entry for our book_cover input. If yes, we compute the path where we want to store the file, write the file, and store the path in the database.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Please make sure that you create the folder covers/ within public/img/.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Great, now let's display the images. Let's start with the HTML view - please edit app/resources/books/views/billgatesbooks.jl.html and make sure it has the following content:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"\n

      Bill's Gates top $( length(books) ) recommended books

      \n
        \n<%\nfor_each(books) do book\n%>\n
      • $(book.title) by $(book.author)
      • \n<%\nend\n%>\n
      ","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Here we check if the cover property is not empty, and display the actual cover. Otherwise we show a placeholder image. You can check the result at http://localhost:8000/bgbooks","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"As for the JSON view, it already does what we want - you can check that the cover property is now outputted, as stored in the database: http://localhost:8000/api/v1/bgbooks","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Success, we're done here!","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#Heads-up!-2","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Heads up!","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"In production you will have to make the upload code more robust - the big problem here is that we store the cover file as it comes from the user which can lead to name clashes and files being overwritten - not to mention security vulnerabilities. A more robust way would be to compute a hash based on author and title and rename the cover to that.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#One-more-thing","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"One more thing","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"So far so good, but what if we want to update the books we have already uploaded? It would be nice to add those missing covers. We need to add a bit of functionality to include editing features.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"First things first - let's add the routes. Please add these two new route definitions to the routes.jl file:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"route(\"/bgbooks/:id::Int/edit\", BooksController.edit)\nroute(\"/bgbooks/:id::Int/update\", BooksController.update, method = POST, named = :update_book)","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"We defined two new routes. The first will display the book object in the form, for editing. While the second will take care of actually updating the database, server side. For both routes we need to pass the id of the book that we want to edit - and we want to constrain it to an Int. We express this as the /:id::Int/ part of the route.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"We also want to:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"reuse the form which we have defined in app/resources/books/views/new.jl.html\nmake the form aware of whether it's used to create a new book, or for editing an existing one respond accordingly by setting the correct action\npre-fill the inputs with the book's info when editing a book.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"OK, that's quite a list and this is where things become interesting. This is an important design pattern for CRUD web apps. In order to simplify the rendering of the form, we will always pass a book object into it. When editing a book it will be the book corresponding to the id passed into the route. And when creating a new book, it will be just an empty book object we'll create and then dispose of.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#Using-view-partials","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Using view partials","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"First, let's set up the views. In app/resources/books/views/ please create a new file called form.jl.html. Then, from app/resources/books/views/new.jl.html cut the code. That is, everything between the opening and closing ...
      tags. Paste it into the newly created form.jl.html file. Now, back to new.jl.html, instead of the previous
      ...
      code add:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"<% partial(\"app/resources/books/views/form.jl.html\", context = @__MODULE__) %>","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"This line, as the partial function suggests, includes a view partial, which is a part of a view file, effectively including a view within another view. Notice that we're explicitly passing the context so Genie can set the correct variable scope when including the partial.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"You can reload the new page to make sure that everything still works: http://localhost:8000/bgbooks/new","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Now, let's add an Edit option to our list of books. Please go back to our list view file, billgatesbooks.jl.html. Here, for each iteration, within the for_each block we'll want to dynamically link to the edit page for the corresponding book.","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html#for_each-with-view-partials","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"for_each with view partials","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"However, this for_each which renders a Julia string is very ugly - and we now know how to refactor it, by using a view partial. Let's do it. First, replace the body of the for_each block:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"\n\"\"\"
    • $(book.title) by $(book.author)\"\"\"","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"with:","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"partial(\"app/resources/books/views/book.jl.html\", book = book, context = @__MODULE__)","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Notice that we are using the partial function and we pass the book object into our view, under the name book (will be accessible in book inside the view partial). Again, we're passing the scope's context (our controller object).","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Next, create the book.jl.html in app/resources/books/views/, for example with","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"julia> touch(\"app/resources/books/views/book.jl.html\")","category":"page"},{"location":"guides/Working_With_Genie_Apps_Intermediary_Topics.html","page":"Working With Genie Apps: Intermediate Topics [WIP]","title":"Working With Genie Apps: Intermediate Topics [WIP]","text":"Add this content to it: TO BE CONTINUED","category":"page"},{"location":"guides/Interactive_environment.html#Using-Genie-in-an-interactive-environment-(Jupyter/IJulia,-REPL,-etc)","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment (Jupyter/IJulia, REPL, etc)","text":"","category":"section"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"Genie can be used for ad-hoc exploratory programming, to quickly whip up a web server and expose your Julia functions.","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"Once you have Genie into scope, you can define a new route. A route maps a URL to a function.","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"julia> using Genie\n\njulia> route(\"/\") do\n \"Hi there!\"\n end","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"You can now start the web server using","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"julia> up()","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"Finally, now navigate to http://localhost:8000 – you should see the message \"Hi there!\".","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"We can define more complex URIs which can also map to previously defined functions:","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"julia> function hello_world()\n \"Hello World!\"\n end\njulia> route(\"/hello/world\", hello_world)","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"The route handler functions can be defined anywhere (in any other file or module) as long as they are accessible in the current scope.","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"You can now visit http://localhost:8000/hello/world in the browser.","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"We can access route params that are defined as part of the URL, like :message in the following example:","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"julia> route(\"/echo/:message\") do\n params(:message)\n end","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"Accessing http://localhost:8000/echo/ciao should echo \"ciao\".","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"And we can even match route params by types (and automatically convert them to the correct type):","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"julia> route(\"/sum/:x::Int/:y::Int\") do\n params(:x) + params(:y)\n end","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"By default, route params are extracted as SubString (more exactly, SubString{String}). If type constraints are added, Genie will attempt to convert the SubString to the indicated type.","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"For the above to work, we also need to tell Genie how to perform the conversion:","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"julia> Base.convert(::Type{Int}, s::AbstractString) = parse(Int, s)","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"Now if we access http://localhost:8000/sum/2/3 we should see 5","category":"page"},{"location":"guides/Interactive_environment.html#Handling-query-params","page":"Using Genie in an interactive environment","title":"Handling query params","text":"","category":"section"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"Query params, which look like ...?foo=bar&baz=2 are automatically unpacked by Genie and placed into the params collection. For example:","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"julia> route(\"/sum/:x::Int/:y::Int\") do\n params(:x) + params(:y) + parse(Int, get(params, :initial_value, \"0\"))\n end","category":"page"},{"location":"guides/Interactive_environment.html","page":"Using Genie in an interactive environment","title":"Using Genie in an interactive environment","text":"Accessing http://localhost:8000/sum/2/3?initial_value=10 will now output 15.","category":"page"},{"location":"API/server.html","page":"Server","title":"Server","text":"CurrentModule = Server","category":"page"},{"location":"API/server.html","page":"Server","title":"Server","text":"SERVERS\nServersCollection\ndown\ndown!\nhandle_request\nhandle_ws_request\nisrunning\nopenbrowser\nprint_server_status\nserve\nserver_status\nsetup_http_listener\nsetup_http_streamer\nsetup_ws_handler\nup\nupdate_config","category":"page"},{"location":"API/server.html#Genie.Server.SERVERS","page":"Server","title":"Genie.Server.SERVERS","text":"SERVERS\n\nServersCollection constant containing references to the current app's web and websockets servers.\n\n\n\n\n\n","category":"constant"},{"location":"API/server.html#Genie.Server.ServersCollection","page":"Server","title":"Genie.Server.ServersCollection","text":"ServersCollection(webserver::Union{Task,Nothing}, websockets::Union{Task,Nothing})\n\nRepresents a object containing references to Genie's web and websockets servers.\n\n\n\n\n\n","category":"type"},{"location":"API/server.html#Genie.Server.down","page":"Server","title":"Genie.Server.down","text":"down(; webserver::Bool = true, websockets::Bool = true) :: ServersCollection\n\nShuts down the servers optionally indicating which of the webserver and websockets servers to be stopped. It does not remove the servers from the SERVERS collection. Returns the collection.\n\n\n\n\n\n","category":"function"},{"location":"API/server.html#Genie.Server.down!","page":"Server","title":"Genie.Server.down!","text":"function down!(; webserver::Bool = true, websockets::Bool = true) :: Vector{ServersCollection}\n\nShuts down all the servers and empties the SERVERS collection. Returns the empty collection.\n\n\n\n\n\n","category":"function"},{"location":"API/server.html#Genie.Server.handle_request","page":"Server","title":"Genie.Server.handle_request","text":"handle_request(req::HTTP.Request, res::HTTP.Response) :: HTTP.Response\n\nHttp server handler function - invoked when the server gets a request.\n\n\n\n\n\n","category":"function"},{"location":"API/server.html#Genie.Server.handle_ws_request","page":"Server","title":"Genie.Server.handle_ws_request","text":"handle_ws_request(req::HTTP.Request, msg::String, ws_client) :: String\n\nHttp server handler function - invoked when the server gets a request.\n\n\n\n\n\n","category":"function"},{"location":"API/server.html#Genie.Server.serve","page":"Server","title":"Genie.Server.serve","text":"serve(path::String = pwd(), params...; kwparams...)\n\nServes a folder of static files located at path. Allows Genie to be used as a static files web server. The params and kwparams arguments are forwarded to Genie.up().\n\nArguments\n\npath::String: the folder of static files to be served by the server\nparams: additional arguments which are passed to Genie.up to control the web server\nkwparams: additional keyword arguments which are passed to Genie.up to control the web server\n\nExamples\n\njulia> Genie.serve(\"public\", 8888, async = false, verbose = true)\n[ Info: Ready!\n2019-08-06 16:39:20:DEBUG:Main: Web Server starting at http://127.0.0.1:8888\n[ Info: Listening on: 127.0.0.1:8888\n[ Info: Accept (1): 🔗 0↑ 0↓ 1s 127.0.0.1:8888:8888 ≣16\n\n\n\n\n\n","category":"function"},{"location":"API/server.html#Genie.Server.setup_http_listener","page":"Server","title":"Genie.Server.setup_http_listener","text":"setup_http_listener(req::HTTP.Request, res::HTTP.Response = HTTP.Response()) :: HTTP.Response\n\nConfigures the handler for the HTTP Request and handles errors.\n\n\n\n\n\n","category":"function"},{"location":"API/server.html#Genie.Server.setup_ws_handler","page":"Server","title":"Genie.Server.setup_ws_handler","text":"setup_ws_handler(stream::HTTP.Stream, ws_client) :: Nothing\n\nConfigures the handler for WebSockets requests.\n\n\n\n\n\n","category":"function"},{"location":"API/server.html#Genie.Server.up","page":"Server","title":"Genie.Server.up","text":"up(port::Int = Genie.config.server_port, host::String = Genie.config.server_host;\n ws_port::Int = Genie.config.websockets_port, async::Bool = ! Genie.config.run_as_server) :: ServersCollection\n\nStarts the web server.\n\nArguments\n\nport::Int: the port used by the web server\nhost::String: the host used by the web server\nws_port::Int: the port used by the Web Sockets server\nasync::Bool: run the web server task asynchronously\n\nExamples\n\njulia> up(8000, \"127.0.0.1\", async = false)\n[ Info: Ready!\nWeb Server starting at http://127.0.0.1:8000\n\n\n\n\n\n","category":"function"},{"location":"API/server.html#Genie.Server.update_config","page":"Server","title":"Genie.Server.update_config","text":"update_config(port::Int, host::String, ws_port::Int) :: Nothing\n\nUpdates the corresponding Genie configurations to the corresponding values for port, host, and ws_port, if these are passed as arguments when starting up the server.\n\n\n\n\n\n","category":"function"},{"location":"tutorials/14--The_Secrets_File.html#The-secrets-(config/secrets.jl)-file","page":"The secrets file","title":"The secrets (config/secrets.jl) file","text":"","category":"section"},{"location":"tutorials/14--The_Secrets_File.html","page":"The secrets file","title":"The secrets file","text":"Confidential configuration data (like API keys, usernames, passwords, etc) should be added to the config/secrets.jl file. This file is by default added to .gitignore when creating a Genie app, so it won't be added to source control – to avoid that it is accidentally exposed.","category":"page"},{"location":"tutorials/14--The_Secrets_File.html#Scope","page":"The secrets file","title":"Scope","text":"","category":"section"},{"location":"tutorials/14--The_Secrets_File.html","page":"The secrets file","title":"The secrets file","text":"All the definitions (variables, constants, functions, modules, etc) added to the secrets.jl file are loaded into your app's module. So if your app (and its main module) is called MyGenieApp, the definitions will be available under the MyGenieApp namespace.","category":"page"},{"location":"tutorials/14--The_Secrets_File.html","page":"The secrets file","title":"The secrets file","text":"","category":"page"},{"location":"tutorials/14--The_Secrets_File.html","page":"The secrets file","title":"The secrets file","text":"HEADS UP","category":"page"},{"location":"tutorials/14--The_Secrets_File.html","page":"The secrets file","title":"The secrets file","text":"Given the your app's name is variable, you can also access your app's module through the Main.UserApp constant. So all the definitions added to secrets.jl can also be accessed through the Mani.UserApp module.","category":"page"},{"location":"tutorials/14--The_Secrets_File.html","page":"The secrets file","title":"The secrets file","text":"","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html#How-to-deploy-Genie-applications","page":"Deploying Genie Apps On AWS","title":"How to deploy Genie applications","text":"","category":"section"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"In this guide we will walk through how to deploy a Genie App to Amazon Cloud. This article will be added with more information in future.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html#Prerequisite:","page":"Deploying Genie Apps On AWS","title":"Prerequisite:","text":"","category":"section"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Genie Application with ready Dockerfile(Refer to Genie Docker Tutorial)\nOpened account in main cloud providers","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"That’s it, from this point we can start with deployment.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html#AWS","page":"Deploying Genie Apps On AWS","title":"AWS","text":"","category":"section"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html#Networking","page":"Deploying Genie Apps On AWS","title":"Networking","text":"","category":"section"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"The easiest and quickest to deploy and expose Genie applications is to create a EC2 instance and run docker container inside it.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"After opening an AWS console you should be able to see your network under VPC tab. This is default one, and we’ll use it for hosting our EC2 instance.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"(Image: network-vpc)","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"This VPC already contains a predefined subnets. We need a public subnet as we should access our Application via Internet.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"(Image: subnet)","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Great, let’s move on.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html#Setup-EC2-instance","page":"Deploying Genie Apps On AWS","title":"Setup EC2 instance","text":"","category":"section"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Navigate to EC2 tab in AWS Console and then click on “Launch instances“. ","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"(Image: launch-instance)","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"We will use a Free Tier Amazon Linux 2 AMI image. It can fit our needs for this demo","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"(Image: free-tier)","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"For an Instance type lets choose t2.small, in that case we’ll get 1vCPU and 2Gb Memory. We should have enough memory with this instance to meet our application requirements. Also we should allow HTTP & HTTPS access. For SSH let’s specify your IP or let 0.0.0.0/0 with is allows connect to this instance from everywhere (not secure). Also double check that “Auto-assign public IP“ is enabled.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"(Image: instance-conf)","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Click on \"Launch instance\". It’ll take around 2min and we can proceed.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"(Image: launch-instance)","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Ok, now our Instance is Running and have a public IP.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html#Setup-Docker-on-EC2","page":"Deploying Genie Apps On AWS","title":"Setup Docker on EC2","text":"","category":"section"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Now let’s connect to is and install a docker. Here is the command to connect via SSH. Please change this IP by yours.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"ssh -i my-demo-app.pem ec2-user@13.xx.xxx.x63","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Let’s Install Docker on it, it’s pretty easy, just execute next commands:","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"sudo yum update\nsudo yum install docker -y\nsudo usermod -a -G docker ec2-user\nid ec2-user\nnewgrp docker\nsudo systemctl enable docker.service\nsudo systemctl start docker.service","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Now we can verify that our docker was installed and runs successfully","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"sudo systemctl status docker.service","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"For more information you can check this guide. Amazing, basically that’s it, now we can deploy our application.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Let’s clone repository with our applications and Dockerfile to EC2. If git is not installed on EC2 just execute","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"sudo yum git install -y\n# And clone your git repo\ngit clone \ncd ","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html#Building-a-Genie-Application","page":"Deploying Genie Apps On AWS","title":"Building a Genie Application","text":"","category":"section"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"First of all let’s build our app using docker cli","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"docker build -t my-demo-app .\ndocker images ","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"If build was successful your should be able to see docker image. Now let’s just start it. In our case application works on port 8000, so we did a port-mapping to port 80.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"docker run -p 80:8000 my-demo-app","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"Then just grep a public IP from AWS console and open it in your browser.","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"(Image: deployed-app)","category":"page"},{"location":"guides/Deploying_Genie_Apps_On_AWS.html","page":"Deploying Genie Apps On AWS","title":"Deploying Genie Apps On AWS","text":"That’s it, your application was successfully deployed. Thanks.","category":"page"},{"location":"API/logger.html","page":"Logger","title":"Logger","text":"CurrentModule = Logger","category":"page"},{"location":"API/logger.html","page":"Logger","title":"Logger","text":"initialize_logging\ntimestamp_logger","category":"page"},{"location":"API/webthreads.html","page":"WebThreads","title":"WebThreads","text":"CurrentModule = WebThreads","category":"page"},{"location":"API/webthreads.html","page":"WebThreads","title":"WebThreads","text":"CLIENTS\nMESSAGE_QUEUE\nSUBSCRIPTIONS\nChannelClient\nChannelClientsCollection\nChannelMessage\nChannelSubscriptionsCollection\nClientId\nChannelName\nMessagePayload\nbroadcast\nchannels\nclients\nconnected_clients\ndisconnected_clients\nmessage\npop_subscription\npull\npush\npush_subscription\nsubscribe\nsubscriptions\ntimestamp_client\nunsubscribe\nunsubscribe_client\nunsubscribe_clients\nunsubscribe_disconnected_clients\nwebthreads","category":"page"},{"location":"API/webthreads.html#Genie.WebThreads.ChannelClientsCollection","page":"WebThreads","title":"Genie.WebThreads.ChannelClientsCollection","text":"Dict([itr])\n\nDict{K,V}() constructs a hash table with keys of type K and values of type V. Keys are compared with isequal and hashed with hash.\n\nGiven a single iterable argument, constructs a Dict whose key-value pairs are taken from 2-tuples (key,value) generated by the argument.\n\nExamples\n\njulia> Dict([(\"A\", 1), (\"B\", 2)])\nDict{String, Int64} with 2 entries:\n \"B\" => 2\n \"A\" => 1\n\nAlternatively, a sequence of pair arguments may be passed.\n\njulia> Dict(\"A\"=>1, \"B\"=>2)\nDict{String, Int64} with 2 entries:\n \"B\" => 2\n \"A\" => 1\n\n\n\n\n\n","category":"type"},{"location":"API/webthreads.html#Genie.WebThreads.ChannelSubscriptionsCollection","page":"WebThreads","title":"Genie.WebThreads.ChannelSubscriptionsCollection","text":"Dict([itr])\n\nDict{K,V}() constructs a hash table with keys of type K and values of type V. Keys are compared with isequal and hashed with hash.\n\nGiven a single iterable argument, constructs a Dict whose key-value pairs are taken from 2-tuples (key,value) generated by the argument.\n\nExamples\n\njulia> Dict([(\"A\", 1), (\"B\", 2)])\nDict{String, Int64} with 2 entries:\n \"B\" => 2\n \"A\" => 1\n\nAlternatively, a sequence of pair arguments may be passed.\n\njulia> Dict(\"A\"=>1, \"B\"=>2)\nDict{String, Int64} with 2 entries:\n \"B\" => 2\n \"A\" => 1\n\n\n\n\n\n","category":"type"},{"location":"API/webthreads.html#Genie.WebThreads.ClientId","page":"WebThreads","title":"Genie.WebThreads.ClientId","text":"UInt64 <: Unsigned\n\n64-bit unsigned integer type.\n\n\n\n\n\n","category":"type"},{"location":"API/webthreads.html#Genie.WebThreads.ChannelName","page":"WebThreads","title":"Genie.WebThreads.ChannelName","text":"String <: AbstractString\n\nThe default string type in Julia, used by e.g. string literals.\n\nStrings are immutable sequences of Chars. A String is stored internally as a contiguous byte array, and while they are interpreted as being UTF-8 encoded, they can be composed of any byte sequence. Use isvalid to validate that the underlying byte sequence is valid as UTF-8.\n\n\n\n\n\n","category":"type"},{"location":"API/webthreads.html#Genie.WebThreads.broadcast","page":"WebThreads","title":"Genie.WebThreads.broadcast","text":"Pushes msg (and payload) to all the clients subscribed to the channels in channels.\n\n\n\n\n\nPushes msg (and payload) to all the clients subscribed to all the channels.\n\n\n\n\n\n","category":"function"},{"location":"API/webthreads.html#Genie.WebThreads.message","page":"WebThreads","title":"Genie.WebThreads.message","text":"Pushes msg (and payload) to channel.\n\n\n\n\n\nWrites msg to message queue for client.\n\n\n\n\n\n","category":"function"},{"location":"API/webthreads.html#Genie.WebThreads.pop_subscription","page":"WebThreads","title":"Genie.WebThreads.pop_subscription","text":"Removes the subscription of client to channel.\n\n\n\n\n\nRemoves all subscriptions of client.\n\n\n\n\n\n","category":"function"},{"location":"API/webthreads.html#Genie.WebThreads.push_subscription","page":"WebThreads","title":"Genie.WebThreads.push_subscription","text":"Adds a new subscription for client to channel.\n\n\n\n\n\n","category":"function"},{"location":"API/webthreads.html#Genie.WebThreads.subscribe","page":"WebThreads","title":"Genie.WebThreads.subscribe","text":"Subscribes a web thread client wt to channel.\n\n\n\n\n\n","category":"function"},{"location":"API/webthreads.html#Genie.WebThreads.unsubscribe","page":"WebThreads","title":"Genie.WebThreads.unsubscribe","text":"Unsubscribes a web socket client wt from channel.\n\n\n\n\n\n","category":"function"},{"location":"API/webthreads.html#Genie.WebThreads.unsubscribe_client","page":"WebThreads","title":"Genie.WebThreads.unsubscribe_client","text":"Unsubscribes a web socket client wt from all the channels.\n\n\n\n\n\n","category":"function"},{"location":"API/webthreads.html#Genie.WebThreads.unsubscribe_disconnected_clients","page":"WebThreads","title":"Genie.WebThreads.unsubscribe_disconnected_clients","text":"unsubscribedisconnectedclients() :: ChannelClientsCollection\n\nUnsubscribes clients which are no longer connected.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-json.html","page":"JSON Renderer","title":"JSON Renderer","text":"CurrentModule = Renderer.Json","category":"page"},{"location":"API/renderer-json.html","page":"JSON Renderer","title":"JSON Renderer","text":"render\nGenie.Renderer.render\njson\nGenie.Router.error","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html#Developing-Genie-Web-Services","page":"Creating a web service","title":"Developing Genie Web Services","text":"","category":"section"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"Starting up ad-hoc web servers at the REPL and writing small scripts to wrap micro-services works great, but production apps tend to become complex quickly. They also have more stringent requirements, like managing dependencies, compressing assets, reloading code, logging, environments, or structuring the codebase in a way which promotes efficient workflows when working in teams.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"Genie enables a modular approach towards app building, allowing to add more components as the need arises. You can start with the web service template (which includes dependencies management, logging, environments, and routing), and grow it by sequentially adding DB persistence (through the SearchLight ORM), high performance HTML view templates with embedded Julia (via Renderer.Html), caching, authentication, and more.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html#Setting-up-a-Genie-Web-Service-project","page":"Creating a web service","title":"Setting up a Genie Web Service project","text":"","category":"section"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"Genie packs handy generator features and templates which help bootstrapping and setting up various parts of an application. These are available in the Genie.Generator module. For bootstrapping a new app we need to invoke one of the functions in the newapp family:","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"julia> using Genie\n\njulia> Genie.Generator.newapp_webservice(\"MyGenieApp\")","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"If you follow the log messages in the REPL you will see that the command will trigger a flurry of actions in order to set up the new project:","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"it creates a new folder, MyGenieApp/, which will hosts the files of the app and whose name corresponds to the name of the app,\nwithin the MyGenieApp/ folder, it creates the files and folders needed by the app,\nchanges the active directory to MyGenieApp/ and creates a new Julia project within it (adding the Project.toml file),\ninstalls all the required dependencies for the new Genie app (using Pkg and the standard Manifest.toml file), and finally,\nstarts the web server","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"TIP","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"Check out the ?help documentation for Genie.Generator.newapp, Genie.Generator.newapp_webservice, Genie.Generator.newapp_mvc, and Genie.Generator.newapp_fullstack too see what options are available for bootstrapping applications. We'll go over the different configurations in upcoming sections.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html#The-file-structure","page":"Creating a web service","title":"The file structure","text":"","category":"section"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"Our newly created web service has this file structure:","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"├── .gitattributes\n├── .gitignore\n├── Manifest.toml\n├── Project.toml\n├── bin\n├── bootstrap.jl\n├── config\n├── public\n├── routes.jl\n├── src\n└── test","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"These are the roles of each of the files and folders:","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"Manifest.toml and Project.toml are used by Julia and Pkg to manage the app's dependencies.\nbin/ includes scripts for starting up a Genie REPL or a Genie server.\nbootstrap.jl and the files within src/ are used by Genie to load the application and should not be modified unless you know what you're doing.\nconfig/ includes the per-environment configuration files.\npublic/ is the document root, which includes static files exposed by the app on the network/internet.\nroutes.jl is the dedicated file for registering Genie routes.\nthe test/ folder is set up to store the unit and integration tests for the app.\n.gitattributes and .gitignore are used by Git to manage the project's files.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"HEADS UP","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"After creating a new app you might need to change the file permissions to allow editing/saving the files such as routes.jl.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html#Adding-logic","page":"Creating a web service","title":"Adding logic","text":"","category":"section"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"You can now edit the routes.jl file to add some logic, at the bottom of the file:","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"route(\"/hello\") do\n \"Welcome to Genie!\"\nend","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"If you now visit http://127.0.0.1:8000/hello you'll see a warm greeting.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html#Extending-the-app","page":"Creating a web service","title":"Extending the app","text":"","category":"section"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"Genie apps are just plain Julia projects. This means that routes.jl will behave like any other Julia script - you can reference extra packages, you can switch into pkg> mode to manage per project dependencies, include other files, etcetera.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"If you have existing Julia code that you want to quickly load into a Genie app, you can add a lib/ folder in the root of the app and place your Julia files there. If the folder exists, lib/ and all its subfolders are automatically loaded by Genie, recursively.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"WARNING","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"If you add the lib/ folder while the Genie app is running, you will need to restart the app to load the files.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"If you need to add database support, you can always add the SearchLight ORM by running julia> Genie.Generator.db_support() in the app's REPL.","category":"page"},{"location":"tutorials/4--Developing_Web_Services.html","page":"Creating a web service","title":"Creating a web service","text":"However, if your app grows in complexity and you develop it from scratch, it is more efficient to take advantage of Genie's resource-oriented MVC structure.","category":"page"},{"location":"tutorials/15--The_Lib_Folder.html#The-lib/-folder","page":"Auto-loading user libraries","title":"The lib/ folder","text":"","category":"section"},{"location":"tutorials/15--The_Lib_Folder.html","page":"Auto-loading user libraries","title":"Auto-loading user libraries","text":"Genie makes it easy to automatically load Julia code (modules, files, etc) into an app, outside of the standard Genie MVC app structure. You simply need to add your files and folders into the lib/ folder.","category":"page"},{"location":"tutorials/15--The_Lib_Folder.html","page":"Auto-loading user libraries","title":"Auto-loading user libraries","text":"","category":"page"},{"location":"tutorials/15--The_Lib_Folder.html","page":"Auto-loading user libraries","title":"Auto-loading user libraries","text":"HEADS UP","category":"page"},{"location":"tutorials/15--The_Lib_Folder.html","page":"Auto-loading user libraries","title":"Auto-loading user libraries","text":"If the lib/ folder does not exist, just create it yourself: julia> mkdir(\"lib\")\nGenie includes the files placed within the lib/ folder and subfolders recursively\nFiles within lib/ are loaded using Revise and are automatically reloaded if changed.","category":"page"},{"location":"tutorials/15--The_Lib_Folder.html","page":"Auto-loading user libraries","title":"Auto-loading user libraries","text":"","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Advanced-routing-techniques","page":"Advanced routing","title":"Advanced routing techniques","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Genie's router can be considered the brain of the app, matching web requests to handler functions, extracting and setting up the request's variables and the execution environment, and invoking the response methods.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Static-routing","page":"Advanced routing","title":"Static routing","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Starting with the simplest case, we can register \"plain\" routes by using the route method. The method takes as its required arguments the URI string or pattern – and the handler function that will be invoked in order to provide the response. The router supports two ways of registering routes, either route(pattern::String, f::Function) or route(f::Function, pattern::String). The first syntax is for passing function references – while the second is for defining inline functions (lambdas).","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Example","page":"Advanced routing","title":"Example","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"The following snippet defines a function greet which returns the \"Welcome to Genie!\" string. We use the function as our route handler, by passing a reference to it as the second argument to the route method.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"using Genie\n\ngreet() = \"Welcome to Genie!\"\n\nroute(\"/greet\", greet) # [GET] /greet => greet\n\nup() # start the server","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"If you use your browser to navigate to http://127.0.0.1:8000/greet you'll see the code in action.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"However, defining a dedicated handler function might be overkill for simple cases like this. As such, Genie allows registering in-line handlers:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"route(\"/bye\") do\n \"Good bye!\"\nend # [GET] /bye => ()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"You can just navigate to http://127.0.0.1:8000/bye – the route is instantly available in the app.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"HEADS UP","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"The routes are matched from newest to oldest. This means that you can define a new route to overwrite a previously defined one.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Genie's router won't match the most specific rule, but the first matching one. So if, for example, you register a route to match /*, it will handle all the requests, even if you have previously defined more specific routes. As a side-note, you can use this technique to temporarily divert all users to a maintenance page (which you can later remove by deleting the route using Router.delete!(:route_name)).","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Dynamic-routing-(using-route-parameters)","page":"Advanced routing","title":"Dynamic routing (using route parameters)","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Static routing works great for fixed URLs. But what if we have dynamic URLs, where the components map to information in the backend (like database IDs) and vary with each request? For example, how would we handle a URL like \"/customers/57943/orders/458230\", where 57943 is the customer id and 458230 is the order id.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Such situations are handled through dynamic routing or route parameters. For the previous example, \"/customers/57943/orders/458230\", we can define a dynamic route as \"/customers/:customer_id/orders/:order_id\". Upon matching the request, the Router will unpack the values and expose them in the params collection.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Example-2","page":"Advanced routing","title":"Example","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"using Genie, Genie.Requests\n\nroute(\"/customers/:customer_id/orders/:order_id\") do\n \"You asked for the order $(payload(:order_id)) for customer $(payload(:customer_id))\"\nend\n\nup()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Routing-methods-(GET,-POST,-PUT,-PATCH,-DELETE,-OPTIONS)","page":"Advanced routing","title":"Routing methods (GET, POST, PUT, PATCH, DELETE, OPTIONS)","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"By default, routes handle GET requests, since these are the most common. In order to define routes for handling other types of request methods, we need to pass the method keyword argument, indicating the HTTP method we want to respond to. Genie's Router supports GET, POST, PUT, PATCH, DELETE, OPTIONS methods.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"The router defines and exports constants for each of these as Router.GET, Router.POST, Router.PUT, Router.PATCH, Router.DELETE, and Router.OPTIONS.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Example-3","page":"Advanced routing","title":"Example","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"We can setup the following PATCH route:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"using Genie, Genie.Requests\n\nroute(\"/patch_stuff\", method = PATCH) do\n \"Stuff to patch\"\nend\n\nup()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"And we can test it using the HTTP package:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"using HTTP\n\nHTTP.request(\"PATCH\", \"http://127.0.0.1:8000/patch_stuff\").body |> String","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"This will output the string \"Stuff to patch\", as the response from the PATCH request. By sending a request with the PATCH method, our route is triggered. Consequently, we access the response body and convert it to a string, which is \"Stuff to patch\".","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Named-routes","page":"Advanced routing","title":"Named routes","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Genie allows tagging routes with names. This is a very powerful feature, to be used in conjunction with the Router.tolink method, for dynamically generating URLs to various the routes. The advantage of this technique is that if we refer to the route by name and generate the links dynamically using tolink, as long as the name of the route stays the same, if we change the route pattern, all the URLs will automatically match the new route definition.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"In order to name a route we need to use the named keyword argument, which expects a Symbol.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Example-4","page":"Advanced routing","title":"Example","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"using Genie, Genie.Requests\n\nroute(\"/customers/:customer_id/orders/:order_id\", named = :get_customer_order) do\n \"Looking up order $(payload(:order_id)) for customer $(payload(:customer_id))\"\nend\n# [GET] /customers/:customer_id/orders/:order_id => ()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"We can check the status of our route with:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"julia> routes()\n :get_customer_order => [GET] /customers/:customer_id/orders/:order_id => ()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"HEADS UP","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"For consistency, Genie names all the routes. However, the auto-generated name is state dependent. So if you change the definition of the route, it's possible that the name will change as well. Thus, it's best to explicitly name the routes if you plan on referencing them throughout the app.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"We can confirm this by adding an anonymous route:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"route(\"/foo\") do\n \"foo\"\nend\n# [GET] /foo => ()\n\njulia> routes()\n :get_customer_order => [GET] /customers/:customer_id/orders/:order_id => ()\n :get_foo => [GET] /foo => ()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"The new route has been automatically named get_foo, based on the method and URI pattern.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Links-to-routes","page":"Advanced routing","title":"Links to routes","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"We can use the name of the route to link back to it using the linkto method.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Example-5","page":"Advanced routing","title":"Example","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Let's start with the previously defined two routes:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"julia> routes()\n :get_customer_order => [GET] /customers/:customer_id/orders/:order_id => ()\n :get_foo => [GET] /foo => ()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Static routes such as :get_foo are straightforward to target:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"julia> linkto(:get_foo)\n\"/foo\"","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"For dynamic routes, it's a bit more involved as we need to supply the values for each of the parameters, as keyword arguments:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"julia> linkto(:get_customer_order, customer_id = 1234, order_id = 5678)","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"This will generate the URL \"/customers/1234/orders/5678\"","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"The linkto function should be used in conjunction with the HTML code for generating links, ie:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Foo","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Listing-routes","page":"Advanced routing","title":"Listing routes","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"At any time we can check which routes are registered with Router.routes:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"julia> routes()\n [GET] /foo => getfield(Main, Symbol(\"##7#8\"))()\n [GET] /customers/:customer_id/orders/:order_id => ()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#The-Route-type","page":"Advanced routing","title":"The Route type","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"The routes are represented internally by the Route type which has the following fields:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"method::String - for storing the method of the route (GET, POST, etc)\npath::String - represents the URI pattern to be matched against\naction::Function - the route handler to be executed when the route is matched\nname::Union{Symbol,Nothing} - the name of the route\ncontext::Module - an optional context to be used when executing the route handler","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Removing-routes","page":"Advanced routing","title":"Removing routes","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"We can delete routes from the stack by calling the delete! method and passing the collection of routes and the name of the route to be removed. The method returns the collection of (remaining) routes","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Example-6","page":"Advanced routing","title":"Example","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"julia> routes()\n :get_customer_order => [GET] /customers/:customer_id/orders/:order_id => ()\n :get_foo => [GET] /foo => ()\n\njulia> Router.delete!(:get_foo)\n :get_customer_order => [GET] /customers/:customer_id/orders/:order_id => ()\n\njulia> routes()\n :get_customer_order => [GET] /customers/:customer_id/orders/:order_id => ()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Matching-routes-by-type-of-arguments","page":"Advanced routing","title":"Matching routes by type of arguments","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"By default route parameters are parsed into the payload collection as SubString{String}:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"using Genie, Genie.Requests\n\nroute(\"/customers/:customer_id/orders/:order_id\") do\n \"Order ID has type $(payload(:order_id) |> typeof) // Customer ID has type $(payload(:customer_id) |> typeof)\"\nend","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"This will output Order ID has type SubString{String} // Customer ID has type SubString{String}","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"However, for such a case, we'd very much prefer to receive our data as Int to avoid an explicit conversion – and to match only numbers. Genie supports such a workflow by allowing type annotations to route parameters:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"route(\"/customers/:customer_id::Int/orders/:order_id::Int\", named = :get_customer_order) do\n \"Order ID has type $(payload(:order_id) |> typeof) // Customer ID has type $(payload(:customer_id) |> typeof)\"\nend\n# [GET] /customers/:customer_id::Int/orders/:order_id::Int => ()","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Notice how we've added type annotations to :customer_id and :order_id in the form :customer_id::Int and :order_id::Int.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"However, attempting to access the URL http://127.0.0.1:8000/customers/10/orders/20 will fail:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Failed to match URI params between Int64::DataType and 10::SubString{String}\nMethodError(convert, (Int64, \"10\"), 0x00000000000063fe)\n/customers/10/orders/20 404","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"As you can see, Genie attempts to convert the types from the default SubString{String} to Int – but doesn't know how. It fails, can't find other matching routes and returns a 404 Not Found response.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Type-conversion-in-routes","page":"Advanced routing","title":"Type conversion in routes","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"The error is easy to address though: we need to provide a type converter from SubString{String} to Int.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Base.convert(::Type{Int}, v::SubString{String}) = parse(Int, v)","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Once we register the converter our request will be correctly handled, resulting in Order ID has type Int64 // Customer ID has type Int64","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Matching-individual-URI-segments","page":"Advanced routing","title":"Matching individual URI segments","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Besides matching the full route, Genie also allows matching individual URI segments. That is, enforcing that the various route parameters obey a certain pattern. In order to introduce constraints for route parameters we append #pattern at the end of the route parameter.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#Example-7","page":"Advanced routing","title":"Example","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"For instance, let's assume that we want to implement a localized website where we have a URL structure like: mywebsite.com/en, mywebsite.com/es and mywebsite.com/de. We can define a dynamic route and extract the locale variable to serve localized content:","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"route(\":locale\", TranslationsController.index)","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"This will work very well, matching requests and passing the locale into our code within the payload(:locale) variable. However, it will also be too greedy, virtually matching all the requests, including things like static files (ie mywebsite.com/favicon.ico). We can constrain what the :locale variable can match, by appending the pattern (a regex pattern):","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"route(\":locale#(en|es|de)\", TranslationsController.index)","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"The refactored route only allows :locale to match one of en, es, and de strings.","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"HEADS UP","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"Keep in mind not to duplicate application logic. For instance, if you have an array of supported locales, you can use that to dynamically generate the pattern – routes can be fully dynamically generated!","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"const LOCALE = \":locale#($(join(TranslationsController.AVAILABLE_LOCALES, '|')))\"\n\nroute(\"/$LOCALE\", TranslationsController.index, named = :get_index)","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"","category":"page"},{"location":"tutorials/12--Advanced_Routing_Techniques.html#The-params-collection","page":"Advanced routing","title":"The params collection","text":"","category":"section"},{"location":"tutorials/12--Advanced_Routing_Techniques.html","page":"Advanced routing","title":"Advanced routing","text":"It's good to know that the router bundles all the parameters of the current request into the params collection (a Dict{Symbol,Any}). This contains valuable information, such as route parameters, query params, POST payload, the original HTTP.Request and HTTP.Response objects, etcetera. In general it's recommended not to access the params collection directly but through the utility methods defined by Genie.Requests and Genie.Responses – but knowing about params might come in handy for advanced users.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Working-with-Genie-apps-(projects)","page":"Working with Genie Apps","title":"Working with Genie apps (projects)","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Working with Genie in an interactive environment can be useful – but usually we want to persist the application and reuse it between sessions. One way to achieve this is to save it as an IJulia notebook and rerun the cells.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"However, you can get the best of Genie by working with Genie apps. A Genie app is a MVC (Model-View-Controller) web application which promotes the convention-over-configuration principle. By working with a few predefined files, within the Genie app structure, the framework can lift a lot of weight and massively improve development productivity. By following Genie's workflow, one instantly gets, out of the box, features like automatic module loading and reloading, dedicated configuration files, logging, support for environments, code generators, caching, support for Genie plugins, and much more.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"In order to create a new Genie app, we need to run Genie.Generator.newapp($app_name):","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> Genie.Generator.newapp(\"MyGenieApp\")","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Upon executing the command, Genie will:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"make a new dir called MyGenieApp and cd() into it,\ninstall all the app's dependencies,\ncreate a new Julia project (adding the Project.toml and Manifest.toml files),\nactivate the project,\nautomatically load the new app's environment into the REPL,\nstart the web server on the default Genie port (port 8000) and host (127.0.0.1 – aka localhost).","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"At this point you can confirm that everything worked as expected by visiting http://127.0.0.1:8000 in your favourite web browser. You should see Genie's welcome page.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Next, let's add a new route. Routes are used to map request URLs to Julia functions. These functions (also called \"handler\" functions) provide the response that will be sent back to the client. Routes are meant to be defined in the dedicated routes.jl file (but there is no restriction enforcing this rule). Open MyGenieApp/routes.jl in your editor or run the following command (making sure that you are in the app's directory):","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> edit(\"routes.jl\")","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Append this at the bottom of the routes.jl file and save it:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# routes.jl\nroute(\"/hello\") do\n \"Welcome to Genie!\"\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"We are using the route method, passing in the \"/hello\" URL and an anonymous function which returns the string \"Welcome to Genie!\". What this means is that for each request to the \"/hello\" URL, our app will invoke the route handler function and will respond with the welcome message.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Visit http://127.0.0.1:8000/hello for a warm welcome!","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Working-with-resources","page":"Working with Genie Apps","title":"Working with resources","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Adding our code to the routes.jl file works great for small projects, where you want to quickly publish features on the web. But for larger projects we're better off using Genie's MVC structure (MVC stands for Model-View-Controller). By employing the Model-View-Controller design pattern we can break our code into modules with clear responsibilities: the Model is used for data access, the View renders the response to the client, and the Controller orchestrates the interactions between Models and Views and handles requests. Modular code is easier to write, test and maintain.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"A Genie app can be architected around the concept of \"resources\". A resource represents a business entity (something like a user, or a product, or an account) and maps to a bundle of files (controller, model, views, etc). Resources live under the app/resources/ folder and each resource has its own dedicated folder, where all of its files are hosted. For example, if we have a web app about \"books\", a \"books\" folder would be found at app/resources/books and will contain all the files for publishing books on the web (usually called BooksController.jl for the controller, Books.jl for the model, BooksValidator.jl for the model validator – as well as a views folder for hosting all the view files necessary for rendering books data).","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"HEADS UP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"When creating a default Genie app, the app/ folder might be missing. It will be automatically created the first time you add a resource via Genie's generators.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Using-Controllers","page":"Working with Genie Apps","title":"Using Controllers","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Controllers are used to orchestrate interactions between client requests, Models (which handle data access), and Views (which are responsible for rendering the responses which will be sent to the clients' web browsers). In a standard workflow, a route points to a method in the controller – which is charged with building and sending the response over the network, back to the client.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Let's add a \"books\" controller. Genie comes with handy generators and one of them is for creating new controllers:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Generate-the-Controller","page":"Working with Genie Apps","title":"Generate the Controller","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Let's generate our BooksController:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> Genie.Generator.newcontroller(\"Books\")\n[info]: New controller created at ./app/resources/books/BooksController.jl","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Great! Let's edit BooksController.jl (julia> edit(\"./app/resources/books/BooksController.jl\")) and add something to it. For example, a function which returns some of Bill Gates' recommended books would be nice. Make sure that BooksController.jl looks like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# app/resources/books/BooksController.jl\nmodule BooksController\n\nstruct Book\n title::String\n author::String\nend\n\nconst BillGatesBooks = Book[\n Book(\"The Best We Could Do\", \"Thi Bui\"),\n Book(\"Evicted: Poverty and Profit in the American City\", \"Matthew Desmond\"),\n Book(\"Believe Me: A Memoir of Love, Death, and Jazz Chickens\", \"Eddie Izzard\"),\n Book(\"The Sympathizer\", \"Viet Thanh Nguyen\"),\n Book(\"Energy and Civilization, A History\", \"Vaclav Smil\")\n]\n\nfunction billgatesbooks()\n \"\n

      Bill Gates' list of recommended books

      \n
        \n $([\"
      • $(book.title) by $(book.author)
      • \" for book in BillGatesBooks]...)\n
      \n \"\nend\n\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Our controller is just a plain Julia module where we define a Book struct and set up an array of book objects. We then define a function, billgatesbooks, which returns an HTML string, with a H1 heading and an unordered list of all the books. We used an array comprehension to iterate over each book and render it in a
    • element. The elements of the array are then concatenated using the splat ... operator.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The plan is to map this function to a route and expose it on the internet.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Checkpoint","page":"Working with Genie Apps","title":"Checkpoint","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Before exposing it on the web, we can test the function in the REPL:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> using MyGenieApp.BooksController\n\njulia> BooksController.billgatesbooks()","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The output of the function call should be a HTML string which looks pretty much like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"\"

      Bill Gates' list of recommended books

      \"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Please make sure that it works as expected.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Setup-the-route","page":"Working with Genie Apps","title":"Setup the route","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now, let's expose our billgatesbooks method on the web. We need to add a new route which points to it. Add these to the routes.jl file:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# routes.jl\nusing Genie\nusing MyGenieApp.BooksController\n\nroute(\"/bgbooks\", BooksController.billgatesbooks)","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"In the snippet we declared that we're using MyGenieApp.BooksController. Do notice that Genie automatically included the module as there was no need to explicitly include the file – and in addition Genie will reload the source code every time we change it. Next we defined a route mapping the /bgbooks URL and the BooksController.billgatesbooks function (we say that the BooksController.billgatesbooks is the route handler for the /bgbooks URL or endpoint).","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"That's all! If you now visit http://localhost:8000/bgbooks you'll see Bill Gates' list of recommended books (well, at least some of them, the man reads a lot!).","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"PRO TIP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If you would rather work with Julia instead of wrangling HTML strings, you can use Genie's Renderer.Html API. It provides functions which map every standard HTML element. For instance, the BooksController.billgatesbooks function can be written as follows, as an array of HTML elements:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"using Genie.Renderer.Html\n\nfunction billgatesbooks()\n [\n h1() do\n \"Bill Gates' list of recommended books\"\n end\n ul() do\n for_each(BillGatesBooks) do book\n li() do\n book.title * \" by \" * book.author\n end\n end\n end\n ]\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The for_each function iterates over a collection of elements and concatenates the output of each loop into the result of the loop. We'll talk about it more soon.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Adding-views","page":"Working with Genie Apps","title":"Adding views","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"However, putting HTML into the controllers is a bad idea: HTML should stay in the dedicated view files and contain as little logic as possible. Let's refactor our code to use views instead.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The views used for rendering a resource should be placed inside the views/ folder, within that resource's own folder structure. So in our case, we will add an app/resources/books/views/ folder. Just go ahead and do it, Genie does not provide a generator for this task:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> mkdir(joinpath(\"app\", \"resources\", \"books\", \"views\"))","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"We created the views/ folder in app/resources/books/. We provided the full path as our REPL is running in the the root folder of the app. Also, we use the joinpath function so that Julia creates the path in a cross-platform way.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Naming-views","page":"Working with Genie Apps","title":"Naming views","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Usually each controller method will have its own rendering logic – hence, its own view file. Thus, it's a good practice to name the view files just like the methods, so that we can keep track of where they're used.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"At the moment, Genie supports HTML and Markdown view files, as well as plain Julia. Their type is identified by file extension so that's an important part. The HTML views use a .jl.html extension while the Markdown files go with .jl.md and the Julia ones by .jl.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#HTML-views","page":"Working with Genie Apps","title":"HTML views","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"All right then, let's add our first view file for the BooksController.billgatesbooks method. Let's create an HTML view file. With Julia:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> touch(joinpath(\"app\", \"resources\", \"books\", \"views\", \"billgatesbooks.jl.html\"))","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Genie supports a special type of dynamic HTML view, where we can embed Julia code. These are high performance compiled views. They are not parsed as strings: instead, the HTML is converted to a Julia function that uses native Julia rendering code and is cached to the file system and loaded like any other Julia file. Hence, the first time you load a view, or after you change one, you might notice a certain delay – it's the time needed to generate, compile and load the view. On next runs (especially in production) it's going to be blazing fast!","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"In case you're curious, the auto-generated Julia view functions are stored by default in the build/ folder in the app. Feel free to take a look. The build/ folder can be safely deleted, Genie will create it back as needed. The location of the build/ folder is configurable and can be changed from Genie's config.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now all we need to do is to move the HTML code out of the controller and into the view, improving it a bit to also show a count of the number of books. Edit the view file as follows (julia> edit(\"app/resources/books/views/billgatesbooks.jl.html\")):","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"\n

      Bill Gates' top $(length(books)) recommended books

      \n","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"As you can see, it's just plain HTML with embedded Julia. We can add Julia code by using the <% ... %> code block tags – these should be used for more complex, multiline expressions. Or by using plain Julia string interpolation with $(...) – for simple values outputting.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"To make HTML generation more efficient, Genie provides a series of helpers, like the above for_each macro which allows iterating over a collection, passing the current item into the processing function.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Rendering-views","page":"Working with Genie Apps","title":"Rendering views","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"We now need to refactor our controller to use the view, passing in the expected variables. We will use the html method which renders and outputs the response as HTML. Update the definition of the billgatesbooks function to be as follows:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# BooksController.jl\nusing Genie.Renderer.Html\n\nfunction billgatesbooks()\n html(:books, :billgatesbooks, books = BillGatesBooks)\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"First, notice that we needed to add Genie.Renderer.Html as a dependency, to get access to the html method. As for the html method itself, it takes as its arguments the name of the resource, the name of the view file, and a list of keyword arguments representing view variables:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":":books is the name of the resource (which effectively indicates in which views folder Genie should look for the","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"view file – in our case app/resources/books/views);","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":":billgatesbooks is the name of the view file. We don't need to pass the extension, Genie will figure it out since","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"there's only one file with this name;","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"and finally, we pass the values we want to expose in the view, as keyword arguments.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"That's it – our refactored app should be ready! You can try it out for yourself at http://localhost:8000/bgbooks","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Markdown-views","page":"Working with Genie Apps","title":"Markdown views","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Markdown views work similar to HTML views – employing the same embedded Julia functionality. Here is how you can add a Markdown view for our billgatesbooks function.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"First, create the corresponding view file, using the .jl.md extension. Maybe with:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> touch(joinpath(\"app\", \"resources\", \"books\", \"views\", \"billgatesbooks.jl.md\"))","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now edit the file and make sure it looks like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"\n# Bill Gates' $(length(books)) recommended books\n\n$(\n for_each(books) do book\n \"* $(book.title) by $(book.author) \\n\"\n end\n)","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Notice that Markdown views do not support Genie's HTML embedded Julia tags <% ... %>. Only string interpolation $(...) is accepted, but it works across multiple lines.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If you reload the page now, however, Genie will still load the HTML view. The reason is that, if we have only one view file, Genie will manage. But if there's more than one, the framework won't know which one to pick. It won't error out but will pick the default one, which is the HTML version.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"It's a simple change in the BookiesController: we have to explicitly tell Genie which file to load, extension and all:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# BooksController.jl\nfunction billgatesbooks()\n html(:books, \"billgatesbooks.jl.md\", books = BillGatesBooks)\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Taking-advantage-of-layouts","page":"Working with Genie Apps","title":"Taking advantage of layouts","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Genie's views are rendered within a layout file. Layouts are meant to render the theme of the website, or the \"frame\" around the view – the elements which are common on all the pages. The layout file can include visible elements, like the main menu or the footer. But also maybe the tag or the assets tags ( and \n \n","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"We can edit it. For example, add this right under the opening tag, just above the <% tag:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"

      Welcome to top books

      ","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If you reload the page at http://localhost:8000/bgbooks you will see the new heading.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"But we don't have to stick to the default; we can add additional layouts. Let's suppose that we have, for example, an admin area which should have a completely different theme. We can add a dedicated layout for that:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> touch(joinpath(\"app\", \"layouts\", \"admin.jl.html\"))","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now edit it (julia> edit(\"app/layouts/admin.jl.html\")) and make it look like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"\n\n\n \n Genie Admin\n \n \n

      Books admin

      \n <%\n @yield\n %>\n \n","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If we want to apply it, we must instruct our BooksController to use it. The html function takes a keyword argument named layout, for the layout file. Update the billgatesbooks function to look like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# BooksController.jl\nfunction billgatesbooks()\n html(:books, :billgatesbooks, books = BillGatesBooks, layout = :admin)\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Reload the page and you'll see the new heading.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#The-@yield-instruction","page":"Working with Genie Apps","title":"The @yield instruction","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"There is a special instruction in the layouts: @yield. It outputs the contents of the view as rendered through the controller. So where this macro is present, Genie will output the HTML resulting from rendering the view by executing the route handler function within the controller.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Using-view-paths","page":"Working with Genie Apps","title":"Using view paths","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"For very simple applications the MVC and the resource-centric approaches might involve too much boilerplate. In such cases, we can simplify the code by referencing the view (and layout) by file path, ex:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# BooksController.jl\nusing Genie.Renderer\n\nfunction billgatesbooks()\n html(path\"app/resources/books/views/billgatesbooks.jl.html\", books = BillGatesBooks, layout = path\"app/layouts/app.jl.html\")\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Rendering-JSON-views","page":"Working with Genie Apps","title":"Rendering JSON views","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"A common use case for web apps is to serve as backends for RESTful APIs. For such cases, JSON is the preferred data format. You'll be happy to hear that Genie has built-in support for JSON responses. Let's add an endpoint for our API – which will render Bill Gates' books as JSON.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"We can start in the routes.jl file, by appending this","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"route(\"/api/v1/bgbooks\", BooksController.API.billgatesbooks)","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Next, in BooksController.jl, append the extra logic at the end of the file, before the closing end. The whole file should look like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# BooksController.jl\nmodule BooksController\n\nusing Genie.Renderer.Html\n\nstruct Book\n title::String\n author::String\nend\n\nconst BillGatesBooks = Book[\n Book(\"The Best We Could Do\", \"Thi Bui\"),\n Book(\"Evicted: Poverty and Profit in the American City\", \"Matthew Desmond\"),\n Book(\"Believe Me: A Memoir of Love, Death, and Jazz Chickens\", \"Eddie Izzard\"),\n Book(\"The Sympathizer!\", \"Viet Thanh Nguyen\"),\n Book(\"Energy and Civilization, A History\", \"Vaclav Smil\")\n]\n\nfunction billgatesbooks()\n html(:books, :billgatesbooks, layout = :admin, books = BillGatesBooks)\nend\n\n\nmodule API\n\nusing ..BooksController\nusing Genie.Renderer.Json\n\nfunction billgatesbooks()\n json(BooksController.BillGatesBooks)\nend\n\nend\n\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"We nested an API module within the BooksController module, where we defined another billgatesbooks function which outputs a JSON.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If you go to http://localhost:8000/api/v1/bgbooks it should already work as expected.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#JSON-views","page":"Working with Genie Apps","title":"JSON views","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"However, we have just committed one of the cardinal sins of API development. We have just forever coupled our internal data structure to its external representation. This will make future refactoring very complicated and error prone as any changes in the data will break the client's integrations. The solution is to, again, use views, to fully control how we render our data – and decouple the data structure from its rendering on the web.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Genie has support for JSON views – these are plain Julia files which have the \".json.jl\" extension. Let's add one in our views/ folder:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> touch(joinpath(\"app\", \"resources\", \"books\", \"views\", \"billgatesbooks.json.jl\"))","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"We can now create a proper response. Put this in the view file:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# app/resources/books/views/billgatesbooks.json.jl\n\"Bill Gates' list of recommended books\" => books","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Final step, instructing BooksController to render the view. Simply replace the existing billgatesbooks function within the API sub-module with the following:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"function billgatesbooks()\n json(:books, :billgatesbooks, books = BooksController.BillGatesBooks)\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"This should hold no surprises – the json function is similar to the html one we've seen before. So now we're rendering a custom JSON response. That's all – everything should work!","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"HEADS UP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Why-JSON-views-have-the-extension-ending-in-.jl-but-HTML-and-Markdown-views-do-not?","page":"Working with Genie Apps","title":"Why JSON views have the extension ending in .jl but HTML and Markdown views do not?","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Good question! The extension of the views is chosen in order to preserve correct syntax highlighting in the IDE/code editor.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Since practically HTML and Markdown views are HTML and Markdown files with some embedded Julia code, we want to use the HTML or Markdown syntax highlighting. For JSON views, we use pure Julia, so we want Julia syntax highlighting.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Accessing-databases-with-SearchLight-models","page":"Working with Genie Apps","title":"Accessing databases with SearchLight models","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"You can get the most out of Genie by pairing it with its seamless ORM layer, SearchLight. SearchLight, a native Julia ORM, provides excellent support for working with relational databases. The Genie + SearchLight combo can be used to productively develop CRUD (Create-Read-Update-Delete) apps.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"HEADS UP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"CRUD stands for Create-Read-Update-Delete and describes the data workflow in many web apps, where resources are created, read (listed), updated, and deleted.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"SearchLight represents the \"M\" part in Genie's MVC architecture (thus, the Model layer).","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Let's begin by adding SearchLight to our Genie app. All Genie apps manage their dependencies in their own Julia environment, through their Project.toml and Manifest.toml files.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"So we need to make sure that we're in pkg> mode first (which is entered by typing ] in julian mode, ie: julia>]). The cursor should change to (MyGenieApp) pkg>.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Next, we add SearchLight:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"(MyGenieApp) pkg> add SearchLight","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Adding-a-database-adapter","page":"Working with Genie Apps","title":"Adding a database adapter","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"SearchLight provides a database agnostic API for working with various backends (at the moment, MySQL, SQLite, Postgres and Oracle). Thus, we also need to add the specific adapter. To keep things simple, let's use SQLite for our app. Hence, we'll need the SearchLightSQLite package:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"(MyGenieApp) pkg> add SearchLightSQLite","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Setup-the-database-connection","page":"Working with Genie Apps","title":"Setup the database connection","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Genie is designed to seamlessly integrate with SearchLight and provides access to various database oriented generators. First we need to tell Genie/SearchLight how to connect to the database. Let's use them to set up our database support. Run this in the Genie/Julia REPL:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> Genie.Generator.db_support()","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The command will add a db/ folder within the root of the app. What we're looking for is the db/connection.yml file which tells SearchLight how to connect to the database. Let's edit it. Make the file look like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"env: ENV[\"GENIE_ENV\"]\n\ndev:\n adapter: SQLite\n database: db/books.sqlite\n config:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"This instructs SearchLight to run in the environment of the current Genie app (by default dev), using SQLite for the adapter (backend) and a database stored at db/books.sqlite (the database will be created automatically if it does not exist). We could pass extra configuration options in the config object, but for now we don't need anything else.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"HEADS UP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If you are using a different adapter, make sure that the database configured already exists and that the configured user can successfully access it – SearchLight will not attempt to create the database.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now we can ask SearchLight to load it up:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> using SearchLight\n\njulia> SearchLight.Configuration.load()\nDict{String,Any} with 4 entries:\n \"options\" => Dict{String,String}()\n \"config\" => nothing\n \"database\" => \"db/books.sqlite\"\n \"adapter\" => \"SQLite\"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Let's just go ahead and try it out by connecting to the DB:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> using SearchLightSQLite\n\njulia> SearchLight.Configuration.load() |> SearchLight.connect","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"This should return a SQLite.DB(\"db/books.sqlite\") object. If that is the case, the connection succeeded and we got back a SQLite database handle.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"PRO TIP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Each database adapter exposes a CONNECTIONS collection where we can access the connection:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> SearchLightSQLite.CONNECTIONS\n1-element Array{SQLite.DB,1}:\n SQLite.DB(\"db/books.sqlite\")","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Awesome! If all went well you should have a books.sqlite database in the db/ folder.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"shell> tree db\ndb\n├── books.sqlite\n├── connection.yml\n├── migrations\n└── seeds","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Managing-the-database-schema-with-SearchLight-migrations","page":"Working with Genie Apps","title":"Managing the database schema with SearchLight migrations","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Database migrations provide a way to reliably, consistently and repeatedly apply (and undo) changes to the structure of your database (known as \"schema transformations\"). They are specialised scripts for adding, removing and altering DB tables – these scripts are placed under version control and are managed by a dedicated system which knows which scripts have been run and which not, and is able to run them in the correct order.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"SearchLight needs its own DB table to keep track of the state of the migrations so let's set it up:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> SearchLight.Migrations.init()\n[ Info: Created table schema_migrations","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"This command sets up our database with the needed table in order to manage migrations.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"PRO TIP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"You can use the SearchLight API to execute random queries against the database backend. For example we can confirm that the table is really there:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> SearchLight.query(\"SELECT name FROM sqlite_master WHERE type ='table' AND name NOT LIKE 'sqlite_%'\")\n┌ Info: SELECT name FROM sqlite_master WHERE type ='table' AND name NOT LIKE 'sqlite_%'\n└\n\n1×1 DataFrames.DataFrame\n│ Row │ name │\n│ │ String⍰ │\n├─────┼───────────────────┤\n│ 1 │ schema_migrations │","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The result is a DataFrame object.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Creating-our-Book-model","page":"Working with Genie Apps","title":"Creating our Book model","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"SearchLight, just like Genie, uses the convention-over-configuration design pattern. It prefers for things to be setup in a certain way and provides sensible defaults, versus having to define everything in extensive configuration files. And fortunately, we don't even have to remember what these conventions are, as SearchLight also comes with an extensive set of generators.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Lets ask SearchLight to create a new model:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> SearchLight.Generator.newresource(\"Book\")\n\n[ Info: New model created at /Users/adrian/Dropbox/Projects/MyGenieApp/app/resources/books/Books.jl\n[ Info: New table migration created at /Users/adrian/Dropbox/Projects/MyGenieApp/db/migrations/2020020909574048_create_table_books.jl\n[ Info: New validator created at /Users/adrian/Dropbox/Projects/MyGenieApp/app/resources/books/BooksValidator.jl\n[ Info: New unit test created at /Users/adrian/Dropbox/Projects/MyGenieApp/test/books_test.jl","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"SearchLight has created the Books.jl model, the *_create_table_books.jl migration file, the BooksValidator.jl model validator and the books_test.jl test file.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"HEADS UP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The first part of the migration file will be different for you!","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The *_create_table_books.jl file will be named differently as the first part of the name is the file creation timestamp. This timestamp part guarantees that names are unique and file name clashes are avoided (for example when working as a team a creating similar migration files).","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Writing-the-table-migration","page":"Working with Genie Apps","title":"Writing the table migration","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Lets begin by writing the migration to create our books table. SearchLight provides a powerful DSL for writing migrations. Each migration file needs to define two methods: up which applies the changes – and down which undoes the effects of the up method. So in our up method we want to create the table – and in down we want to drop the table.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The naming convention for tables in SearchLight is that the table name should be pluralized (books) – because a table contains multiple books (and each row represents an object, a single book). But don't worry, the migration file should already be pre-populated with the correct table name.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Edit the db/migrations/*_create_table_books.jl file and make it look like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"module CreateTableBooks\n\nimport SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table\n\nfunction up()\n create_table(:books) do\n [\n primary_key()\n column(:title, :string, limit = 100)\n column(:author, :string, limit = 100)\n ]\n end\n\n add_index(:books, :title)\n add_index(:books, :author)\nend\n\nfunction down()\n drop_table(:books)\nend\n\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The code is pretty easy to follow: in the up function we call create_table and pass an array of columns: a primary key, a title column and an author column (both strings have a max length of 100). We also add two indices (one on the title and the other on the author columns). As for the down method, it invokes the drop_table function to remove the table.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Running-the-migration","page":"Working with Genie Apps","title":"Running the migration","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"We can see what SearchLight knows about our migrations with the SearchLight.Migrations.status command:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> SearchLight.Migrations.status()\n| | Module name & status |\n| | File name |\n|---|----------------------------------------|\n| | CreateTableBooks: DOWN |\n| 1 | 2020020909574048_create_table_books.jl |","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"So our migration is in the down state – meaning that its up method has not been run. We can easily fix this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> SearchLight.Migrations.last_up()\n[ Info: Executed migration CreateTableBooks up","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If we recheck the status, the migration is up:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> SearchLight.Migrations.status()\n| | Module name & status |\n| | File name |\n|---|----------------------------------------|\n| | CreateTableBooks: UP |\n| 1 | 2020020909574048_create_table_books.jl |","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Our table is ready!","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Defining-the-model","page":"Working with Genie Apps","title":"Defining the model","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now it's time to edit our model file at app/resources/books/Books.jl. Another convention in SearchLight is that we're using the pluralized name (Books) for the module – because it's for managing multiple books. And within it we define a type (a mutable struct), called Book – which represents an item (a single book) which maps to a row in the underlying database.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Edit the Books.jl file to make it look like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# Books.jl\nmodule Books\n\nimport SearchLight: AbstractModel, DbId, save!\nimport Base: @kwdef\n\nexport Book\n\n@kwdef mutable struct Book <: AbstractModel\n id::DbId = DbId()\n title::String = \"\"\n author::String = \"\"\nend\n\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"We defined a mutable struct which matches our previous Book type by using the @kwdef macro, in order to also define a keyword constructor, as SearchLight needs it.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Using-our-model","page":"Working with Genie Apps","title":"Using our model","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"To make things more interesting, we should import our current books into the database. Add this function to the Books.jl module, under the Book() constructor definition (just above the module's closing end):","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# Books.jl\nfunction seed()\n BillGatesBooks = [\n (\"The Best We Could Do\", \"Thi Bui\"),\n (\"Evicted: Poverty and Profit in the American City\", \"Matthew Desmond\"),\n (\"Believe Me: A Memoir of Love, Death, and Jazz Chickens\", \"Eddie Izzard\"),\n (\"The Sympathizer!\", \"Viet Thanh Nguyen\"),\n (\"Energy and Civilization, A History\", \"Vaclav Smil\")\n ]\n\n for b in BillGatesBooks\n Book(title = b[1], author = b[2]) |> save!\n end\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Auto-loading-the-DB-configuration","page":"Working with Genie Apps","title":"Auto-loading the DB configuration","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now, to try things out. Genie takes care of loading all our resource files for us when we load the app. To do this, Genie comes with a special file called an initializer, which automatically loads the database configuration and sets up SearchLight. Check config/initializers/searchlight.jl to see how this is done.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Heads up!","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"All the *.jl files placed into the config/initializers/ folder are automatically included by Genie upon starting the Genie app. They are included early (upon initialisation), before the controllers, models, views, are loaded.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Trying-it-out","page":"Working with Genie Apps","title":"Trying it out","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now it's time to restart our REPL session and test our app. Close the Julia REPL session to exit to the OS command line and run:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"$ bin/repl","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"On Windows you will need to run bin/repl.bat instead.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The repl executable script placed within the app's bin/ folder starts a new Julia REPL session and loads the applications' environment. Everything should be automatically loaded now, DB configuration included - so we can invoke the previously defined seed function to insert the books:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> using MyGenieApp.Books\n\njulia> Books.seed()","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"There should be a list of queries showing how the data is inserted in the DB:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> Books.seed()\n[ Info: INSERT INTO books (\"title\", \"author\") VALUES ('The Best We Could Do', 'Thi Bui')\n[ Info: INSERT INTO books (\"title\", \"author\") VALUES ('Evicted: Poverty and Profit in the American City', 'Matthew Desmond')\n# output truncated","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If you want to make sure all went right (although trust me, it did, otherwise SearchLight would've thrown an Exception!), just ask SearchLight to retrieve the books we inserted:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> using SearchLight\n\njulia> all(Book)\n[ Info: 2020-02-09 13:29:32 SELECT \"books\".\"id\" AS \"books_id\", \"books\".\"title\" AS \"books_title\", \"books\".\"author\" AS \"books_author\" FROM \"books\" ORDER BY books.id ASC\n\n5-element Array{Book,1}:\n Book\n| KEY | VALUE |\n|----------------|----------------------|\n| author::String | Thi Bui |\n| id::DbId | 1 |\n| title::String | The Best We Could Do |\n\n Book\n| KEY | VALUE |\n|----------------|--------------------------------------------------|\n| author::String | Matthew Desmond |\n| id::DbId | 2 |\n| title::String | Evicted: Poverty and Profit in the American City |\n\n# output truncated","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The SearchLight.all method returns all the Book items from the database.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"All good!","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The next thing we need to do is to update our controller to use the model. Make sure that app/resources/books/BooksController.jl reads like this:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# BooksController.jl\nmodule BooksController\n\nusing Genie.Renderer.Html\nusing SearchLight\nusing MyGenieApp.Books\n\nfunction billgatesbooks()\n html(:books, :billgatesbooks, books = all(Book))\nend\n\nmodule API\n\nusing ..BooksController\nusing Genie.Renderer.Json\nusing SearchLight\nusing MyGenieApp.Books\n\nfunction billgatesbooks()\n json(:books, :billgatesbooks, books = all(Book))\nend\n\nend\n\nend","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Our JSON view needs a bit of tweaking too:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# app/resources/books/views/billgatesbooks.json.jl\n\"Bill's Gates list of recommended books\" => [Dict(\"author\" => b.author, \"title\" => b.title) for b in books]","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now if we just start the server we'll be able to see the list of books served from the database:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"# Start the server\njulia> up()","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The up method starts up the web server and takes us back to the interactive Julia REPL prompt.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Now, if, for example, we navigate to http://localhost:8000/api/v1/bgbooks, the output should match the following JSON document:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"{\n \"Bill's Gates list of recommended books\": [\n {\n \"author\": \"Thi Bui\",\n \"title\": \"The Best We Could Do\"\n },\n {\n \"author\": \"Matthew Desmond\",\n \"title\": \"Evicted: Poverty and Profit in the American City\"\n },\n {\n \"author\": \"Eddie Izzard\",\n \"title\": \"Believe Me: A Memoir of Love, Death, and Jazz Chickens\"\n },\n {\n \"author\": \"Viet Thanh Nguyen\",\n \"title\": \"The Sympathizer!\"\n },\n {\n \"author\": \"Vaclav Smil\",\n \"title\": \"Energy and Civilization, A History\"\n }\n ]\n}","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Let's add a new book to see how it works. We'll create a new Book item and persist it using the SearchLight.save! method:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> newbook = Book(title = \"Leonardo da Vinci\", author = \"Walter Isaacson\")\n\nBook\n| KEY | VALUE |\n|----------------|-------------------|\n| author::String | Walter Isaacson |\n| id::DbId | NULL |\n| title::String | Leonardo da Vinci |\n\n\njulia> save!(newbook)\n\n[ Info: INSERT INTO books (\"title\", \"author\") VALUES ('Leonardo da Vinci', 'Walter Isaacson')\n[ Info: ; SELECT CASE WHEN last_insert_rowid() = 0 THEN -1 ELSE last_insert_rowid() END AS id\n[ Info: SELECT \"books\".\"id\" AS \"books_id\", \"books\".\"title\" AS \"books_title\", \"books\".\"author\" AS \"books_author\" FROM \"books\" WHERE \"id\" = 6 ORDER BY books.id ASC\n\nBook\n| KEY | VALUE |\n|----------------|-------------------|\n| author::String | Walter Isaacson |\n| id::DbId | 6 |\n| title::String | Leonardo da Vinci |","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"Calling the save! method, SearchLight has persisted the object in the database and then retrieved it and returned it (notice the updated id::DbId field).","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"The same save! operation can be written as a one-liner:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> Book(title = \"Leonardo da Vinci\", author = \"Walter Isaacson\") |> save!","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"HEADS UP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If you also run the one-liner save! example, it will add the same book again. No problem, but if you want to remove it, you can use the delete method:","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"julia> duplicated_book = find(Book, title = \"Leonardo da Vinci\")[end]\n\njulia> delete(duplicated_bookd)\n[ Info: DELETE FROM books WHERE id = '7'\n\nBook\n| KEY | VALUE |\n|----------------|-------------------|\n| author::String | Walter Isaacson |\n| id::DbId | NULL |\n| title::String | Leonardo da Vinci |","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"If you reload the page at http://localhost:8000/bgbooks the new book should show up.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"{\n \"Bill's Gates list of recommended books\": [\n {\n \"author\": \"Thi Bui\",\n \"title\": \"The Best We Could Do\"\n },\n {\n \"author\": \"Matthew Desmond\",\n \"title\": \"Evicted: Poverty and Profit in the American City\"\n },\n {\n \"author\": \"Eddie Izzard\",\n \"title\": \"Believe Me: A Memoir of Love, Death, and Jazz Chickens\"\n },\n {\n \"author\": \"Viet Thanh Nguyen\",\n \"title\": \"The Sympathizer!\"\n },\n {\n \"author\": \"Vaclav Smil\",\n \"title\": \"Energy and Civilization, A History\"\n },\n {\n \"author\": \"Walter Isaacson\",\n \"title\": \"Leonardo da Vinci\"\n }\n ]\n}","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"PRO TIP","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"SearchLight exposes two similar data persistence methods: save! and save. They both perform the same action (persisting the object to the database), but save will return a Bool of value true to indicate that the operation was successful or a Bool of value false to indicate that the operation has failed. While the save! variant will return the persisted object upon success or will throw an exception on failure.","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"","category":"page"},{"location":"guides/Working_With_Genie_Apps.html#Congratulations","page":"Working with Genie Apps","title":"Congratulations","text":"","category":"section"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"You have successfully finished the first part of the step by step walkthrough - you have mastered the Genie basics, allowing you to set up a new app, register routes, add resources (controllers, models, and views), add database support, version the database schema with migrations, and execute basic queries with SearchLight!","category":"page"},{"location":"guides/Working_With_Genie_Apps.html","page":"Working with Genie Apps","title":"Working with Genie Apps","text":"In the next part we'll look at more advanced topics like handling forms and file uploads, templates rendering, interactivity and more.","category":"page"},{"location":"tutorials/11--Managing_External_Packages.html#Managing-external-packages-for-your-Genie-app","page":"Managing Genie app's dependencies","title":"Managing external packages for your Genie app","text":"","category":"section"},{"location":"tutorials/11--Managing_External_Packages.html","page":"Managing Genie app's dependencies","title":"Managing Genie app's dependencies","text":"Genie fully takes advantage of Julia's excellent package manager, Pkg – while allowing Genie developers to use any third party package available in Julia's ecosystem. This is achieved by taking a common sense approach: Genie apps are just plain Julia projects.","category":"page"},{"location":"tutorials/11--Managing_External_Packages.html","page":"Managing Genie app's dependencies","title":"Managing Genie app's dependencies","text":"In order to add extra packages to your Genie app, thus, we need to use Julia's Pkg features:","category":"page"},{"location":"tutorials/11--Managing_External_Packages.html","page":"Managing Genie app's dependencies","title":"Managing Genie app's dependencies","text":"start a Genie REPL with your app: $ bin/repl. This will automatically load the package environment of the app.\nswitch to Pkg mode: julia> ]\nadd the package you want, for example OhMyREPL: (MyGenieApp) pkg> add OhMyREPL","category":"page"},{"location":"tutorials/11--Managing_External_Packages.html","page":"Managing Genie app's dependencies","title":"Managing Genie app's dependencies","text":"That's all! Now you can use the packages at the Genie REPL or anywhere in your app via using or import.","category":"page"},{"location":"tutorials/11--Managing_External_Packages.html","page":"Managing Genie app's dependencies","title":"Managing Genie app's dependencies","text":"Use the same approach to update the packages in your app, via: pkg> up and apply all available updates, or pkg> up OhMyREPL to update a single package.","category":"page"},{"location":"tutorials/1--Overview.html#Welcome-to-Genie","page":"Welcome to Genie","title":"Welcome to Genie","text":"","category":"section"},{"location":"tutorials/1--Overview.html#The-Highly-Productive-Web-Framework-for-Julia","page":"Welcome to Genie","title":"The Highly Productive Web Framework for Julia","text":"","category":"section"},{"location":"tutorials/1--Overview.html","page":"Welcome to Genie","title":"Welcome to Genie","text":"Genie is a full stack web framework for the Julia programming language. Genie's goals are: excellent developer productivity, great run-time performance, and best practices and security by default.","category":"page"},{"location":"tutorials/1--Overview.html","page":"Welcome to Genie","title":"Welcome to Genie","text":"The Genie web framework follows in the footsteps of mainstream full stack web frameworks like Ruby on Rails and Django, while staying 100% true to its Julia roots. Genie's architecture and development is inspired by the best features present in other frameworks, but not by their design. Genie takes a no-magic no-nonsense approach by doing things the Julia way: Controllers are plain Julia modules, Models leverage types and multiple dispatch, Genie apps are nothing but Julia projects, versioning and dependency management is provided by Julia's own Pkg, and code loading and reloading is automatically set up with Revise.","category":"page"},{"location":"tutorials/1--Overview.html","page":"Welcome to Genie","title":"Welcome to Genie","text":"Genie also takes inspiration from Julia's \"start simple, grow as needed\" philosophy, by allowing developers to bootstrap an app in the REPL or in a notebook, or easily create web services and APIs with just a few lines of code.","category":"page"},{"location":"tutorials/1--Overview.html","page":"Welcome to Genie","title":"Welcome to Genie","text":"As the projects grow more complex, Genie allows adding progressively more structure, by exposing a micro-framework which offers features like powerful routing, flexible logging, support for environments, view templates, etc.","category":"page"},{"location":"tutorials/1--Overview.html","page":"Welcome to Genie","title":"Welcome to Genie","text":"If database persistence is needed, support for Genie's ORM, SearchLight, can be added at any time. Finally, the full MVC structure can be used in order to develop and maintain more complex, end-to-end, web applications.","category":"page"},{"location":"tutorials/2--Installing_Genie.html#How-to-Install-Genie","page":"Installing Genie","title":"How to Install Genie","text":"","category":"section"},{"location":"tutorials/2--Installing_Genie.html","page":"Installing Genie","title":"Installing Genie","text":"Install Genie from Julia's registry – for example the latest version (currently version 5):","category":"page"},{"location":"tutorials/2--Installing_Genie.html","page":"Installing Genie","title":"Installing Genie","text":"pkg> add Genie","category":"page"},{"location":"tutorials/2--Installing_Genie.html","page":"Installing Genie","title":"Installing Genie","text":"Genie, just like Julia, uses semantic versioning in the form vX.Y.Z to designate:","category":"page"},{"location":"tutorials/2--Installing_Genie.html","page":"Installing Genie","title":"Installing Genie","text":"X : major version, introducing breaking changes\nY : minor version, brings new features, no breaking changes\nZ : patch version, fixes bugs, no new features or breaking changes","category":"page"},{"location":"API/secrets.html","page":"Secrets","title":"Secrets","text":"CurrentModule = Secrets","category":"page"},{"location":"API/secrets.html","page":"Secrets","title":"Secrets","text":"load\nsecret\nsecret_token\nsecret_token!","category":"page"},{"location":"API/secrets.html#Genie.Secrets.load","page":"Secrets","title":"Genie.Secrets.load","text":"load(root_dir::String = Genie.config.path_config; context::Union{Module,Nothing} = nothing) :: Nothing\n\nLoads (includes) the framework's secrets.jl file into the app's module context. The files are set up with Revise to be automatically reloaded.\n\n\n\n\n\n","category":"function"},{"location":"API/secrets.html#Genie.Secrets.secret","page":"Secrets","title":"Genie.Secrets.secret","text":"secret() :: String\n\nGenerates a random secret token to be used for configuring the call to Genie.Secrets.secret_token!.\n\n\n\n\n\n","category":"function"},{"location":"API/secrets.html#Genie.Secrets.secret_token","page":"Secrets","title":"Genie.Secrets.secret_token","text":"secret_token(generate_if_missing=true) :: String\n\nReturn the secret token used in the app for encryption and salting.\n\nUsually, this token is defined through Genie.Secrets.secret_token! in the config/secrets.jl file. Here, a temporary one is generated for the current session if no other token is defined and generate_if_missing is true.\n\n\n\n\n\n","category":"function"},{"location":"API/secrets.html#Genie.Secrets.secret_token!","page":"Secrets","title":"Genie.Secrets.secret_token!","text":"secret_token!(value = secret())\n\nDefine the secret token used in the app for encryption and salting.\n\n\n\n\n\n","category":"function"},{"location":"API/toolbox.html","page":"Toolbox","title":"Toolbox","text":"CurrentModule = Toolbox","category":"page"},{"location":"API/toolbox.html","page":"Toolbox","title":"Toolbox","text":"TaskInfo\nTaskResult\ntasks\nVoidTaskResult\nvalidtaskname\ntaskdocs\nloadtasks\nprinttasks\nnew\ntaskfilename\ntaskmodulename\nisvalidtask!","category":"page"},{"location":"API/toolbox.html#Genie.Toolbox.validtaskname","page":"Toolbox","title":"Genie.Toolbox.validtaskname","text":"validtaskname(task_name::String) :: String\n\nAttempts to convert a potentially invalid (partial) task_name into a valid one.\n\n\n\n\n\n","category":"function"},{"location":"API/toolbox.html#Genie.Toolbox.taskdocs","page":"Toolbox","title":"Genie.Toolbox.taskdocs","text":"task_docs(module_name::Module) :: String\n\nRetrieves the docstring of the runtask method and returns it as a string.\n\n\n\n\n\n","category":"function"},{"location":"API/toolbox.html#Genie.Toolbox.loadtasks","page":"Toolbox","title":"Genie.Toolbox.loadtasks","text":"loadtasks(; filter_type_name = Symbol()) :: Vector{TaskInfo}\n\nReturns a vector of all registered Genie tasks.\n\n\n\n\n\n","category":"function"},{"location":"API/toolbox.html#Genie.Toolbox.printtasks","page":"Toolbox","title":"Genie.Toolbox.printtasks","text":"Prints a list of all the registered Genie tasks to the standard output.\n\n\n\n\n\n","category":"function"},{"location":"API/toolbox.html#new","page":"Toolbox","title":"new","text":"new, or new{A,B,...}\n\nSpecial function available to inner constructors which creates a new object of the type. The form new{A,B,...} explicitly specifies values of parameters for parametric types. See the manual section on Inner Constructor Methods for more information.\n\n\n\n\n\n","category":"keyword"},{"location":"API/toolbox.html#Genie.Toolbox.taskfilename","page":"Toolbox","title":"Genie.Toolbox.taskfilename","text":"task_file_name(cmd_args::Dict{String,Any}, config::Settings) :: String\n\nComputes the name of a Genie task based on the command line input.\n\n\n\n\n\n","category":"function"},{"location":"API/toolbox.html#Genie.Toolbox.taskmodulename","page":"Toolbox","title":"Genie.Toolbox.taskmodulename","text":"task_module_name(underscored_task_name::String) :: String\n\nComputes the name of a Genie task based on the command line input.\n\n\n\n\n\n","category":"function"},{"location":"API/toolbox.html#Genie.Toolbox.isvalidtask!","page":"Toolbox","title":"Genie.Toolbox.isvalidtask!","text":"isvalidtask!(parsed_args::Dict{String,Any}) :: Dict{String,Any}\n\nChecks if the name of the task passed as the command line arg is valid task identifier – if not, attempts to address it, by appending the TASKSUFFIX suffix. Returns the potentially modified `parsedargsDict`.\n\n\n\n\n\n","category":"function"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Using-Genie-with-Docker","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Genie comes with extended support for containerizing apps using Docker. The functionality is provided by the official GenieDeployDocker plugin.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Setting-up-GenieDeployDocker","page":"Using Genie with Docker","title":"Setting up GenieDeployDocker","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"In order to use the Docker integration features, first we need to add the GenieDeployDocker plugin for Genie.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"pkg> add GenieDeployDocker","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Generating-the-Genie-optimised-Dockerfile","page":"Using Genie with Docker","title":"Generating the Genie-optimised Dockerfile","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"You can bootstrap the Docker setup by invoking the GenieDeployDocker.dockerfile() function. This will generate a custom Dockerfile optimized for Genie web apps containerization. The file will be generated in the current work dir (or where instructed by the optional argument path – see the help for the dockerfile() function).","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Once generated, you can edit it and customize it as needed - Genie will not overwrite the file, thus preserving any changes (unless you call the dockerfile function again, passing the force=true argument).","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"The behavior of dockerfile() can be controlled by passing any of the multiple optional arguments supported.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Building-the-Docker-container","page":"Using Genie with Docker","title":"Building the Docker container","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Once we have our Dockerfile ready, we can invoke GenieDeployDocker.build() to set up the Docker container. You can pass any of the supported optional arguments to configure settings such as the container's name (by default \"genie\"), the path (defaults to current work dir), and others (see the output of help?> GenieDeployDocker.dockerfile for all the available options).","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Running-the-Genie-app-within-the-Docker-container","page":"Using Genie with Docker","title":"Running the Genie app within the Docker container","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"When the image is ready, we can run it with GenieDeployDocker.run(). We can configure any of the optional arguments in order to control how the app is run. Check the inline help for the function for more details.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Examples","page":"Using Genie with Docker","title":"Examples","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"First let's create a Genie app:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia> using Genie\n\njulia> Genie.Generator.newapp(\"DockerTest\")\n[ Info: Done! New app created at /your/app/path/DockerTest\n# output truncated","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"When it's ready, let's add the Dockerfile:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia> using GenieDeployDocker\n\njulia> GenieDeployDocker.dockerfile()\nDocker file successfully written at /your/app/path/DockerTest/Dockerfile","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Now, to build our container:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia> GenieDeployDocker.build()\n# output truncated\nSuccessfully tagged genie:latest\nDocker container successfully built","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"And finally, we can now run our app within the Docker container:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia> GenieDeployDocker.run()\nStarting docker container with `docker run -it --rm -p 80:8000 --name genieapp genie bin/server`\n# output truncated","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"We should then see the familiar Genie loading screen, indicating the app's loading progress and notifying us once the app is running.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Our application starts inside the Docker container, binding port 8000 within the container (where the Genie app is running) to the port 80 of the host. So we are now able to access our app at http://localhost. If you navigate to http://localhost w ith your favorite browser you'll see Genie's welcome page. Notice that we don't access on port 8000 - this page is served from the Docker container on the default port 80.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Inspecting-the-containers","page":"Using Genie with Docker","title":"Inspecting the containers","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"We can get a list of available container by using GenieDeployDocker.list(). This will show only the currently running containers by default, but we can pass the all=true argument to also include containers that are offline.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia> GenieDeployDocker.list()\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\nc87bfd8322cc genie \"bin/server\" 6 minutes ago Up 6 minutes 80/tcp, 0.0.0.0:80->8000/tcp genieapp\nProcess(`docker ps`, ProcessExited(0))","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Stopping-running-containers","page":"Using Genie with Docker","title":"Stopping running containers","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"The running containers can be stopped by using the GenieDeployDocker.stop() function, passing the name of the container.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia> GenieDeployDocker.stop(\"genieapp\")","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Using-Docker-during-development","page":"Using Genie with Docker","title":"Using Docker during development","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"If we want to use Docker to serve the app during development, we need to mount our app from host (your computer) into the container – so that we can keep editing our files locally, but see the changes reflected in the Docker container. In order to do this we need to pass the mountapp = true argument to GenieDeployDocker.run(), like this:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia> GenieDeployDocker.run(mountapp = true)\nStarting docker container with `docker run -it --rm -p 80:8000 --name genieapp -v /Users/adrian/DockerTest:/home/genie/app genie bin/server`","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"When the app finishes starting, we can edit the files on the host using our favorite IDE, and see the changes reflected in the Docker container.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Creating-an-optimized-Genie-sysimage-with-PackageCompiler.jl","page":"Using Genie with Docker","title":"Creating an optimized Genie sysimage with PackageCompiler.jl","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"If we are using Docker containers to deploy Genie apps in production, you can greatly improve the performance of the app by preparing a precompiled sysimage for Julia. We can include this workflow as part of the Docker build step as follows.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Edit-the-Dockerfile","page":"Using Genie with Docker","title":"Edit the Dockerfile","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"We'll start by making a few changes to our Dockerfile, as follows:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"1/ Under the line WORKDIR /home/genie/app add","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"# C compiler for PackageCompiler\nRUN apt-get update && apt-get install -y g++","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"2/ Under the line starting with RUN julia -e add","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"# Compile app\nRUN julia --project compiled/make.jl","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"You may also want to replace the line saying","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"ENV GENIE_ENV \"dev\"","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"with","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"ENV GENIE_ENV \"prod\"","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"to configure the application to run in production (first test locally to make sure that everything is properly configured to run the app in production environment).","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Add-PackageCompiler.jl","page":"Using Genie with Docker","title":"Add PackageCompiler.jl","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"We also need to add PackageCompiler as a dependency of our app:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"pkg> add PackageCompiler","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Add-the-needed-files","page":"Using Genie with Docker","title":"Add the needed files","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Create a new folder to host our files:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia> mkdir(\"compiled\")","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Now create the following files:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia> touch(\"compiled/make.jl\")\njulia> touch(\"compiled/packages.jl\")","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Edit-the-files","page":"Using Genie with Docker","title":"Edit the files","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Now to put the content into each of the files.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Preparing-the-packages.jl-file","page":"Using Genie with Docker","title":"Preparing the packages.jl file","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Here we simply put an array of package that our app uses and that we want to precompile, ex:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"# packages.jl\nconst PACKAGES = [\n \"Dates\",\n \"Genie\",\n \"Inflector\",\n \"Logging\"\n]","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Preparing-the-make.jl-file","page":"Using Genie with Docker","title":"Preparing the make.jl file","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Now edit the make.jl file as follows:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"# make.jl\nusing PackageCompiler\n\ninclude(\"packages.jl\")\n\nPackageCompiler.create_sysimage(\n PACKAGES,\n sysimage_path = \"compiled/sysimg.so\",\n cpu_target = PackageCompiler.default_app_cpu_target()\n)","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html#Using-the-precompiled-image","page":"Using Genie with Docker","title":"Using the precompiled image","text":"","category":"section"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"The result of these changes is that PackageCompiler will create a new Julia sysimage that will be stored inside the compiled/sysimg.so file. The last step is to instruct our bin/server script to use the image.","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"Edit the bin/server file and make it look like this:","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"julia --color=yes --depwarn=no --project=@. --sysimage=compiled/sysimg.so -q -i -- $(dirname $0)/../bootstrap.jl -s=true \"$@\"","category":"page"},{"location":"tutorials/16--Using_Genie_With_Docker.html","page":"Using Genie with Docker","title":"Using Genie with Docker","text":"With this change we're passing the additional --sysimage flag, indicating our new Julia sys image.","category":"page"},{"location":"API/index.html","page":"Genie","title":"Genie","text":"(Image: Genie Logo)","category":"page"},{"location":"API/index.html#Genie","page":"Genie","title":"Genie","text":"","category":"section"},{"location":"API/index.html#The-highly-productive-Julia-web-framework","page":"Genie","title":"The highly productive Julia web framework","text":"","category":"section"},{"location":"API/index.html","page":"Genie","title":"Genie","text":"Genie is Julia web framework that provides a streamlined and efficient workflow for developing modern web applications. It builds on Julia's strengths (high-level, high-performance, dynamic, JIT compiled), exposing a rich API and a powerful toolset for productive web development.","category":"page"},{"location":"API/index.html#Current-status","page":"Genie","title":"Current status","text":"","category":"section"},{"location":"API/index.html","page":"Genie","title":"Genie","text":"Genie is compatible with Julia v1.3 and up.","category":"page"},{"location":"API/index.html","page":"Genie","title":"Genie","text":"","category":"page"},{"location":"API/index.html#Acknowledgements","page":"Genie","title":"Acknowledgements","text":"","category":"section"},{"location":"API/index.html","page":"Genie","title":"Genie","text":"Genie uses a multitude of packages that have been kindly contributed by the Julia community.\nThe awesome Genie logo was designed by Alvaro Casanova.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html#Deploying-Genie-apps-to-server-with-Nginx","page":"Deploying to server with Nginx","title":"Deploying Genie apps to server with Nginx","text":"","category":"section"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"This tutorial shows how to host a Julia/Genie app on with Nginx.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html#Prerequisites","page":"Deploying to server with Nginx","title":"Prerequisites","text":"","category":"section"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"To expose the app over the internet, one needs access to a server. This can be a local machine or a cloud instance such as AWS EC2 or a Google Cloud Compute Engine for example.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"If using a local server, a static IP is needed to ensure continuous access to the app. Internet service provider generally charge a fee for such extra service.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html#The-application","page":"Deploying to server with Nginx","title":"The application","text":"","category":"section"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"We assume that a Genie app has been developed and is ready for deployment and that it is hosted as a project on a git repository.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"For example, the app MyGenieApp generated through Genie.Generator.newapp(\"MyGenieApp\") being hosted at github.com/user/MyGenieApp.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"The scripts presented in this tutorial are for Ubuntu 20.04.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html#Install-and-run-the-Genie-app-on-the-server","page":"Deploying to server with Nginx","title":"Install and run the Genie app on the server","text":"","category":"section"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Access the server:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"ssh -i \"ssh-key-for-instance.pem\" user@123.123.123.123","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Install Julia if not present. Then make the clone:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"git clone github.com/user/MyGenieApp\ncd MyGenieAp","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Install the app as any other Julia project:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"julia\n] activate .\npkg> instantiate\nexit()","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"In order to launch the app and exit the console without shutting down the app, we will launch it from a new screen:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"screen -S genie","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Then set the GENIE_ENV environment variable to prod:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"export GENIE_ENV=prod","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Now the application is almost ready to start just need to configure the secret token. Go into the project directory and execute the following command. It will generate secrets.jl inside config/secrets.jl file and if it exists then it will update with a new token string.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"julia --project=. --banner=no --eval=\"using Pkg; using Genie; Genie.Generator.write_secrets_file()\"","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Launch the app:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"./bin/server","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Now the Genie app should be running on the server and be accessible at the following address: 123.123.123.123:8000 (if port 8000 has been open - see instance security settings). Note that you should configure the Genie app so that it doesn't serve the static content (see the Settings option server_handle_static_file in config/env/prod.jl). Static content should be handled by nginx. We can now detach from the genie screen used to launch the app (Ctl+A d).","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html#Install-and-configure-nginx-server","page":"Deploying to server with Nginx","title":"Install and configure nginx server","text":"","category":"section"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Nginx server will be used as a reverse proxy. It will listen requests made on port 80 (HTTP) and redirect traffic to the Genie app running on port 8000 (default Genie setting that can be changed).","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Nginx will also be used to serve the app static files, that is, the content under the ./public folder.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Finally, it can as well handle HTTPS requests, which will also be redirected to the Genie app listening on port 8000.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Installation:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"sudo apt-get update\nsudo apt-get install nginx\nsudo systemctl start nginx\nsudo systemctl enable nginx","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"A configuration file then needs to to be created to indicate on which port to listen (80 for HTTP) and to which port to redirect the traffic (8000 for default Genie config).","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Config is created in folder /etc/nginx/sites-available: sudo nano my-genie-app. Put the following content in my-genie-app:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"server {\n listen 80;\n listen [::]:80;\n\n server_name test.com;\n root /home/ubuntu/MyGenieApp/public;\n index welcome.html;\n\n location / {\n proxy_pass http://localhost:8000/;\n }\n\n location /css/genie {\n proxy_pass http://localhost:8000/;\n }\n location /img/genie {\n proxy_pass http://localhost:8000/;\n }\n location /js/genie {\n proxy_pass http://localhost:8000/;\n }\n}","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"server_name: refers to the web domain to be used. It can be put to an arbitrary name if the app is only to be served","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"directly from the server public IP.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"root: points to the public subfolder where the genie app was cloned.\nindex: refers to the site index (the landing page).\nThe various location following the initial proxy to the genie app are used to indicate static content folders to be","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"served by nginx. These are needed when the server_handle_static_file is set to false in the Genie app settings.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"To make that config effective, it needs to be present in sites-enabled. The default config can be removed.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"sudo ln -s /etc/nginx/sites-available/my-genie-app /etc/nginx/sites-enabled/my-genie-app","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Then restart the server to make changes effective:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"sudo systemctl restart nginx","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html#Enable-HTTPS","page":"Deploying to server with Nginx","title":"Enable HTTPS","text":"","category":"section"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"To enable HTTPS, a site-certificate will be needed for the domain on which the site will be served. A practical approach is to use the utilities provided by certbot.","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Following provided instructions for nginx on Ubuntu 20.04:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"sudo snap install core; sudo snap refresh core\nsudo snap install --classic certbot\nsudo ln -s /snap/bin/certbot /usr/bin/certbot","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Then, using certbot utility, a certificate will be generated and appropriate modification to nginx config will be brought to handle support for HTTPS:","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"sudo certbot --nginx","category":"page"},{"location":"tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html","page":"Deploying to server with Nginx","title":"Deploying to server with Nginx","text":"Note that this step will check for ownernship of the test.com domain mentionned in the nginx config file. For that validation to succeed, it requires to have the A record for the domain set to 123.123.123.123.","category":"page"},{"location":"API/exceptions.html","page":"Exceptions","title":"Exceptions","text":"CurrentModule = Exceptions","category":"page"},{"location":"API/exceptions.html","page":"Exceptions","title":"Exceptions","text":"ExceptionalResponse\nFileExistsException\nInternalServerException\nNotFoundException\nRuntimeException","category":"page"},{"location":"API/exceptions.html#Genie.Exceptions.ExceptionalResponse","page":"Exceptions","title":"Genie.Exceptions.ExceptionalResponse","text":"struct ExceptionalResponse <: Exception\n\nA type of exception which wraps an HTTP Response object. The thrown exception will propagate until it is caught up the app stack or ultimately by Genie and the wrapped response is sent to the client.\n\nExample\n\nIf the user is not authenticated, an ExceptionalResponse is thrown - if the exception is not caught in the app's stack, Genie will catch it and return the wrapped Response object, forcing an HTTP redirect to the login page.\n\nisauthenticated() || throw(ExceptionalResponse(redirect(:show_login)))\n\n\n\n\n\n","category":"type"},{"location":"API/exceptions.html#Genie.Exceptions.FileExistsException","page":"Exceptions","title":"Genie.Exceptions.FileExistsException","text":"struct FileExistsException <: Exception\n\nCustom exception type for signaling that the requested file already exists.\n\n\n\n\n\n","category":"type"},{"location":"API/exceptions.html#Genie.Exceptions.InternalServerException","page":"Exceptions","title":"Genie.Exceptions.InternalServerException","text":"struct InternalServerException <: Exception\n\nDedicated exception type for server side exceptions. Results in a 500 error by default.\n\nArguments\n\nmessage::String\ninfo::String\ncode::Int\n\n\n\n\n\n","category":"type"},{"location":"API/exceptions.html#Genie.Exceptions.NotFoundException","page":"Exceptions","title":"Genie.Exceptions.NotFoundException","text":"struct NotFoundException <: Exception\n\nSpecialized exception representing a not found resources. Results in a 404 response being sent to the client.\n\nArguments\n\nmessage::String\ninfo::String\ncode::Int\nresource::String\n\n\n\n\n\n","category":"type"},{"location":"API/exceptions.html#Genie.Exceptions.RuntimeException","page":"Exceptions","title":"Genie.Exceptions.RuntimeException","text":"RuntimeException\n\nRepresents an unexpected and unhandled runtime exceptions. An error event will be logged and the exception will be sent to the client, depending on the environment (the error stack is dumped by default in dev mode or an error message is displayed in production).\n\nIt allows defining custom error message and info, as well as an error code, in addition to the exception object.\n\nArguments\n\nmessage::String\ninfo::String\ncode::Int\nex::Union{Nothing,Exception}\n\n\n\n\n\n","category":"type"},{"location":"API/requests.html","page":"Requests","title":"Requests","text":"CurrentModule = Requests","category":"page"},{"location":"API/requests.html","page":"Requests","title":"Requests","text":"jsonpayload\nrawpayload\nfilespayload\ninfilespayload\nRequests.write\nRequests.read\nfilename\npostpayload\ngetpayload\nrequest\npayload\nmatchedroute\nmatchedchannel\nwsclient","category":"page"},{"location":"API/requests.html#Genie.Requests.jsonpayload","page":"Requests","title":"Genie.Requests.jsonpayload","text":"jsonpayload()\n\nProcesses an application/json POST request. If it fails to successfully parse the JSON data it returns nothing. The original payload can still be accessed invoking rawpayload()\n\n\n\n\n\njsonpayload(v)\n\nProcesses an application/json POST request attempting to return value corresponding to key v.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.rawpayload","page":"Requests","title":"Genie.Requests.rawpayload","text":"rawpayload() :: String\n\nReturns the raw POST payload as a String.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.filespayload","page":"Requests","title":"Genie.Requests.filespayload","text":"filespayload() :: Dict{String,HttpFile}\n\nCollection of form uploaded files.\n\n\n\n\n\nfilespayload(filename::Union{String,Symbol}) :: HttpFile\n\nReturns the HttpFile uploaded through the key input name.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.infilespayload","page":"Requests","title":"Genie.Requests.infilespayload","text":"infilespayload(key::Union{String,Symbol}) :: Bool\n\nChecks if the collection of uploaded files contains a file stored under the key name.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Base.write","page":"Requests","title":"Base.write","text":"write(io::IO, x)\nwrite(filename::AbstractString, x)\n\nWrite the canonical binary representation of a value to the given I/O stream or file. Return the number of bytes written into the stream. See also print to write a text representation (with an encoding that may depend upon io).\n\nThe endianness of the written value depends on the endianness of the host system. Convert to/from a fixed endianness when writing/reading (e.g. using htol and ltoh) to get results that are consistent across platforms.\n\nYou can write multiple values with the same write call. i.e. the following are equivalent:\n\nwrite(io, x, y...)\nwrite(io, x) + write(io, y...)\n\nExamples\n\nConsistent serialization:\n\njulia> fname = tempname(); # random temporary filename\n\njulia> open(fname,\"w\") do f\n # Make sure we write 64bit integer in little-endian byte order\n write(f,htol(Int64(42)))\n end\n8\n\njulia> open(fname,\"r\") do f\n # Convert back to host byte order and host integer type\n Int(ltoh(read(f,Int64)))\n end\n42\n\nMerging write calls:\n\njulia> io = IOBuffer();\n\njulia> write(io, \"JuliaLang is a GitHub organization.\", \" It has many members.\")\n56\n\njulia> String(take!(io))\n\"JuliaLang is a GitHub organization. It has many members.\"\n\njulia> write(io, \"Sometimes those members\") + write(io, \" write documentation.\")\n44\n\njulia> String(take!(io))\n\"Sometimes those members write documentation.\"\n\nUser-defined plain-data types without write methods can be written when wrapped in a Ref:\n\njulia> struct MyStruct; x::Float64; end\n\njulia> io = IOBuffer()\nIOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1)\n\njulia> write(io, Ref(MyStruct(42.0)))\n8\n\njulia> seekstart(io); read!(io, Ref(MyStruct(NaN)))\nBase.RefValue{MyStruct}(MyStruct(42.0))\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Base.read","page":"Requests","title":"Base.read","text":"read(io::IO, T)\n\nRead a single value of type T from io, in canonical binary representation.\n\nNote that Julia does not convert the endianness for you. Use ntoh or ltoh for this purpose.\n\nread(io::IO, String)\n\nRead the entirety of io, as a String (see also readchomp).\n\nExamples\n\njulia> io = IOBuffer(\"JuliaLang is a GitHub organization\");\n\njulia> read(io, Char)\n'J': ASCII/Unicode U+004A (category Lu: Letter, uppercase)\n\njulia> io = IOBuffer(\"JuliaLang is a GitHub organization\");\n\njulia> read(io, String)\n\"JuliaLang is a GitHub organization\"\n\n\n\n\n\nread(filename::AbstractString, args...)\n\nOpen a file and read its contents. args is passed to read: this is equivalent to open(io->read(io, args...), filename).\n\nread(filename::AbstractString, String)\n\nRead the entire contents of a file as a string.\n\n\n\n\n\nread(s::IO, nb=typemax(Int))\n\nRead at most nb bytes from s, returning a Vector{UInt8} of the bytes read.\n\n\n\n\n\nread(s::IOStream, nb::Integer; all=true)\n\nRead at most nb bytes from s, returning a Vector{UInt8} of the bytes read.\n\nIf all is true (the default), this function will block repeatedly trying to read all requested bytes, until an error or end-of-file occurs. If all is false, at most one read call is performed, and the amount of data returned is device-dependent. Note that not all stream types support the all option.\n\n\n\n\n\nread(command::Cmd)\n\nRun command and return the resulting output as an array of bytes.\n\n\n\n\n\nread(command::Cmd, String)\n\nRun command and return the resulting output as a String.\n\n\n\n\n\nread(stream::IO, [nb::Integer,] enc::Encoding)\nread(filename::AbstractString, [nb::Integer,] enc::Encoding)\nread(stream::IO, ::Type{String}, enc::Encoding)\nread(filename::AbstractString, ::Type{String}, enc::Encoding)\n\nMethods to read text in character encoding enc. See documentation for corresponding methods without the enc argument for details.\n\n\n\n\n\nread(file::HttpFile)\n\nReturns the content of file as string.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.filename","page":"Requests","title":"Genie.Requests.filename","text":"filename(file::HttpFile) :: String\n\nOriginal filename of the uploaded HttpFile file.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.postpayload","page":"Requests","title":"Genie.Requests.postpayload","text":"postpayload() :: Dict{Symbol,Any}\n\nA dict representing the POST variables payload of the request (corresponding to a form-data request)\n\n\n\n\n\npostpayload(key::Symbol) :: Any\n\nReturns the value of the POST variables key.\n\n\n\n\n\npostpayload(key::Symbol, default::Any)\n\nReturns the value of the POST variables key or the default value if key is not defined.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.getpayload","page":"Requests","title":"Genie.Requests.getpayload","text":"getpayload() :: Dict{Symbol,Any}\n\nA dict representing the GET/query variables payload of the request (the part corresponding to ?foo=bar&baz=moo)\n\n\n\n\n\ngetpayload(key::Symbol) :: Any\n\nThe value of the GET/query variable key, as in ?key=value\n\n\n\n\n\ngetpayload(key::Symbol, default::Any) :: Any\n\nThe value of the GET/query variable key, as in ?key=value. If key is not defined, default is returned.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.request","page":"Requests","title":"Genie.Requests.request","text":"request() :: HTTP.Request\n\nReturns the raw HTTP.Request object associated with the request. If no request is available (not within a request/response cycle) returns nothing.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.payload","page":"Requests","title":"Genie.Requests.payload","text":"payload() :: Any\n\nUtility function for accessing the params collection, which holds the request variables.\n\n\n\n\n\npayload(key::Symbol) :: Any\n\nUtility function for accessing the key value within the params collection of request variables.\n\n\n\n\n\npayload(key::Symbol, default_value::T) :: Any\n\nUtility function for accessing the key value within the params collection of request variables. If key is not defined, default_value is returned.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.matchedroute","page":"Requests","title":"Genie.Requests.matchedroute","text":"matchedroute() :: Route\n\nReturns the Route object which was matched for the current request or noting if no route is available.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.matchedchannel","page":"Requests","title":"Genie.Requests.matchedchannel","text":"matchedchannel() :: Channel\n\nReturns the Channel object which was matched for the current request or nothing if no channel is available.\n\n\n\n\n\n","category":"function"},{"location":"API/requests.html#Genie.Requests.wsclient","page":"Requests","title":"Genie.Requests.wsclient","text":"wsclient() :: HTTP.WebSockets.WebSocket\n\nThe web sockets client for the current request or nothing if not available.\n\n\n\n\n\n","category":"function"},{"location":"API/watch.html","page":"Watch","title":"Watch","text":"CurrentModule = Watch","category":"page"},{"location":"API/watch.html","page":"Watch","title":"Watch","text":"WATCHED_FOLDERS\nWATCHING\ncollect_watched_files\nhandlers\nunwatch\nwatch\nwatchpath","category":"page"},{"location":"tutorials/5--Handling_Query_Params.html#Handling-query-params-(GET-variables)","page":"Handling URI/query params","title":"Handling query params (GET variables)","text":"","category":"section"},{"location":"tutorials/5--Handling_Query_Params.html","page":"Handling URI/query params","title":"Handling URI/query params","text":"Genie makes it easy to access query params, which are values sent as part of the URL over GET requests (ex: mywebsite.com/index?foo=1&bar=2 foo and bar are query params corresponding to the variables foo = 1 and bar = 2). All these values are automatically collected by Genie and exposed in the params() collection (which is part of the Router module).","category":"page"},{"location":"tutorials/5--Handling_Query_Params.html#Example","page":"Handling URI/query params","title":"Example","text":"","category":"section"},{"location":"tutorials/5--Handling_Query_Params.html","page":"Handling URI/query params","title":"Handling URI/query params","text":"using Genie\n\nroute(\"/hi\") do\n name = params(:name, \"Anon\")\n\n \"Hello $name\"\nend","category":"page"},{"location":"tutorials/5--Handling_Query_Params.html","page":"Handling URI/query params","title":"Handling URI/query params","text":"If you access http://127.0.0.1:8000/hi the app will respond with \"Hello Anon\" since we're not passing any query params.","category":"page"},{"location":"tutorials/5--Handling_Query_Params.html","page":"Handling URI/query params","title":"Handling URI/query params","text":"However, requesting http://127.0.0.1:8000/hi?name=Adrian will in turn display \"Hello Adrian\" as we're passing the name query variable with the value Adrian. This variable is exposed by Genie as params(:name).","category":"page"},{"location":"tutorials/5--Handling_Query_Params.html","page":"Handling URI/query params","title":"Handling URI/query params","text":"Genie however provides utility methods for accessing these values in the Requests module.","category":"page"},{"location":"tutorials/5--Handling_Query_Params.html#The-Requests-module","page":"Handling URI/query params","title":"The Requests module","text":"","category":"section"},{"location":"tutorials/5--Handling_Query_Params.html","page":"Handling URI/query params","title":"Handling URI/query params","text":"Genie provides a set of utilities for working with requests data within the Requests module. You can use the getpayload method to retrieve the query params as a Dict{Symbol,Any}. We can rewrite the previous route to take advantage of the Requests utilities.","category":"page"},{"location":"tutorials/5--Handling_Query_Params.html#Example-2","page":"Handling URI/query params","title":"Example","text":"","category":"section"},{"location":"tutorials/5--Handling_Query_Params.html","page":"Handling URI/query params","title":"Handling URI/query params","text":"using Genie, Genie.Requests\n\nroute(\"/hi\") do\n \"Hello $(getpayload(:name, \"Anon\"))\"\nend","category":"page"},{"location":"tutorials/5--Handling_Query_Params.html","page":"Handling URI/query params","title":"Handling URI/query params","text":"The getpayload function has a few specializations, and one of them accepts the key and a default value. The default value is returned if the key variable is not defined. You can see the various implementations for getpayload using the API docs or Julia's help> mode.","category":"page"},{"location":"API/responses.html","page":"Responses","title":"Responses","text":"CurrentModule = Responses","category":"page"},{"location":"API/responses.html","page":"Responses","title":"Responses","text":"getresponse\ngetheaders\nsetheaders!\nsetheaders\ngetstatus\nsetstatus!\nsetstatus\ngetbody\nsetbody!\nsetbody","category":"page"},{"location":"tutorials/7--Using_JSON_Payloads.html#Using-JSON-payloads","page":"Using JSON payloads","title":"Using JSON payloads","text":"","category":"section"},{"location":"tutorials/7--Using_JSON_Payloads.html","page":"Using JSON payloads","title":"Using JSON payloads","text":"A very common design pattern, especially when developing REST APIs, is to accept JSON payloads sent as application/json data over POST requests. Genie efficiently handles this use case through the utility function Requests.jsonpayload. Under the cover, Genie will process the POST request and will attempt to parse the JSON text payload. If this fails, we can still access the raw data (the text payload not converted to JSON) by using the Requests.rawpayload method.","category":"page"},{"location":"tutorials/7--Using_JSON_Payloads.html#Example","page":"Using JSON payloads","title":"Example","text":"","category":"section"},{"location":"tutorials/7--Using_JSON_Payloads.html","page":"Using JSON payloads","title":"Using JSON payloads","text":"using Genie, Genie.Requests, Genie.Renderer.Json\n\nroute(\"/jsonpayload\", method = POST) do\n @show jsonpayload()\n @show rawpayload()\n\n json(\"Hello $(jsonpayload()[\"name\"])\")\nend\n\nup()","category":"page"},{"location":"tutorials/7--Using_JSON_Payloads.html","page":"Using JSON payloads","title":"Using JSON payloads","text":"Next we make a POST request using the HTTP package:","category":"page"},{"location":"tutorials/7--Using_JSON_Payloads.html","page":"Using JSON payloads","title":"Using JSON payloads","text":"using HTTP\n\nHTTP.request(\"POST\", \"http://localhost:8000/jsonpayload\", [(\"Content-Type\", \"application/json\")], \"\"\"{\"name\":\"Adrian\"}\"\"\")","category":"page"},{"location":"tutorials/7--Using_JSON_Payloads.html","page":"Using JSON payloads","title":"Using JSON payloads","text":"We will get the following output:","category":"page"},{"location":"tutorials/7--Using_JSON_Payloads.html","page":"Using JSON payloads","title":"Using JSON payloads","text":"jsonpayload() = Dict{String,Any}(\"name\"=>\"Adrian\")\nrawpayload() = \"{\\\"name\\\":\\\"Adrian\\\"}\"\n\nINFO:Main: /jsonpayload 200\n\nHTTP.Messages.Response:\n\"\"\"\nHTTP/1.1 200 OK\nContent-Type: application/json\nTransfer-Encoding: chunked\n\n\"Hello Adrian\"\"\"\"","category":"page"},{"location":"tutorials/7--Using_JSON_Payloads.html","page":"Using JSON payloads","title":"Using JSON payloads","text":"First, for the two @show calls, notice how jsonpayload had successfully converted the POST data to a Dict. While the rawpayload returns the POST data as a String, exactly as received. Finally, our route handler returns a JSON response, greeting the user by extracting the name from within the jsonpayload Dict.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#Deploying-Genie-apps-with-Heroku-Buildpacks","page":"Deploying to Heroku with Buildpacks","title":"Deploying Genie apps with Heroku Buildpacks","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"This tutorial shows how to host a Julia/Genie app using a Heroku Buildpack.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#Prerequisites","page":"Deploying to Heroku with Buildpacks","title":"Prerequisites","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"This guide assumes you have a Heroku account and are signed into the Heroku CLI. Information on how to setup the Heroku CLI is available here.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#The-application","page":"Deploying to Heroku with Buildpacks","title":"The application","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"In order to try the deployment, you will need a sample application. Either pick one of yours or clone this sample one, as indicated next.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#All-Steps-(in-easy-copy-paste-format):","page":"Deploying to Heroku with Buildpacks","title":"All Steps (in easy copy-paste format):","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"Customize your HEROKU_APP_NAME to something unique:","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"HEROKU_APP_NAME=my-app-name","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"Clone a sample app if needed:","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"git clone https://github.com/milesfrain/GenieOnHeroku.git","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"Go into the app's folder:","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"cd GenieOnHeroku","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"And create a Heroku app:","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"heroku create $HEROKU_APP_NAME --buildpack https://github.com/Optomatica/heroku-buildpack-julia.git","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"Push the newly created app to Heroku:","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"git push heroku master","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"Now you can open the app in the browser:","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"heroku open -a $HEROKU_APP_NAME","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"If you need to check the logs:","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"heroku logs -tail -a $HEROKU_APP_NAME","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#Steps-with-more-detailed-descriptions","page":"Deploying to Heroku with Buildpacks","title":"Steps with more detailed descriptions","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#Select-an-app-name","page":"Deploying to Heroku with Buildpacks","title":"Select an app name","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"HEROKU_APP_NAME=my-app-name","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"This must be unique among all Heroku projects, and is part of the url where your project is hosted (e.g. https://my-app-name.herokuapp.com/).","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"If the name is not unique, you will see this error at the heroku create step.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"Creating ⬢ my-app-name... !\n ▸ Name my-app-name is already taken","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#Clone-an-example-project","page":"Deploying to Heroku with Buildpacks","title":"Clone an example project","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"git clone https://github.com/milesfrain/GenieOnHeroku.git\ncd GenieOnHeroku","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"You may also point to your own project, but it must be a git repo.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"A Procfile in the root contains the launch command to load your app. The contents of the Procfile for this project is this single line:","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"web: julia --project src/app.jl $PORT","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"You may edit the Procfile to point to your own project's launch script. (for example src/my_app_launch_file.jl instead of src/app.jl), but be sure to take into account the dynamically changing $PORT environment variable which is set by Heroku.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"If you're deploying a standard Genie application built with Genie.newapp, the launch script will be bin/server. Genie will automatically pick the $PORT number from the environment.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#Create-a-Heroku-project","page":"Deploying to Heroku with Buildpacks","title":"Create a Heroku project","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"heroku create $HEROKU_APP_NAME --buildpack https://github.com/Optomatica/heroku-buildpack-julia.git","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"This creates a project on the Heroku platform, which includes a separate git repository.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"This heroku repository is added to the list of tracked repositories and can be observed with git remote -v.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"heroku https://git.heroku.com/my-app-name.git (fetch)\nheroku https://git.heroku.com/my-app-name.git (push)\norigin https://github.com/milesfrain/GenieOnHeroku.git (fetch)\norigin https://github.com/milesfrain/GenieOnHeroku.git (push)","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"We are using a buildpack for Julia. This runs many of the common deployment operations required for Julia projects. It relies on the directory layout found in the example project, with Project.toml, Manifest.toml in the root, and all Julia code in the src directory.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#Deploy-your-app","page":"Deploying to Heroku with Buildpacks","title":"Deploy your app","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"git push heroku master","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"This pushes your current branch of your local repo to the heroku remote repo's master branch.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"Heroku will automatically execute the commands described in the Julia buildpack and Procfile of this latest push.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"You must push to the heroku master branch to trigger an automated deploy.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#Open-your-app's-webpage","page":"Deploying to Heroku with Buildpacks","title":"Open your app's webpage","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"heroku open -a $HEROKU_APP_NAME","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"This is a convenience command to open your app's webpage in your browser.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"The webpage is: https://$HEROKU_APP_NAME.herokuapp.com/","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"For example: https://my-app-name.herokuapp.com/","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#View-app-logs","page":"Deploying to Heroku with Buildpacks","title":"View app logs","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"heroku logs -tail -a $HEROKU_APP_NAME","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"This is another convenience command to launch a log viewer that remains open to show the latest status of your app.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"The println statements from Julia will also appear here.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"Exit this viewer with Ctrl-C.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"Logs can also be viewed from the Heroku web dashboard. For example: https://dashboard.heroku.com/apps/my-app-name/logs","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html#Deploy-app-updates-changes","page":"Deploying to Heroku with Buildpacks","title":"Deploy app updates changes","text":"","category":"section"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"To deploy any changes made to your app, simply commit those changes locally, and re-push to heroku.","category":"page"},{"location":"tutorials/90--Deploying_With_Heroku_Buildpacks.html","page":"Deploying to Heroku with Buildpacks","title":"Deploying to Heroku with Buildpacks","text":"\ngit commit -am \"my commit message\"\ngit push heroku master","category":"page"},{"location":"API/cookies.html","page":"Cookies","title":"Cookies","text":"CurrentModule = Cookies","category":"page"},{"location":"API/cookies.html","page":"Cookies","title":"Cookies","text":"Cookies.Dict\nget\ngetcookies\nset!\nnullablevalue","category":"page"},{"location":"API/cookies.html#Base.Dict","page":"Cookies","title":"Base.Dict","text":"Dict([itr])\n\nDict{K,V}() constructs a hash table with keys of type K and values of type V. Keys are compared with isequal and hashed with hash.\n\nGiven a single iterable argument, constructs a Dict whose key-value pairs are taken from 2-tuples (key,value) generated by the argument.\n\nExamples\n\njulia> Dict([(\"A\", 1), (\"B\", 2)])\nDict{String, Int64} with 2 entries:\n \"B\" => 2\n \"A\" => 1\n\nAlternatively, a sequence of pair arguments may be passed.\n\njulia> Dict(\"A\"=>1, \"B\"=>2)\nDict{String, Int64} with 2 entries:\n \"B\" => 2\n \"A\" => 1\n\n\n\n\n\n","category":"type"},{"location":"API/cookies.html#Genie.Cookies.get","page":"Cookies","title":"Genie.Cookies.get","text":"get(payload::Union{HTTP.Response,HTTP.Request}, key::Union{String,Symbol}, default::T; encrypted::Bool = true)::T where T\n\nAttempts to get the Cookie value stored at key within payload. If the key is not set, the default value is returned.\n\nArguments\n\npayload::Union{HTTP.Response,HTTP.Request}: the request or response object containing the Cookie headers\nkey::Union{String,Symbol}: the name of the cookie value\ndefault::T: default value to be returned if no cookie value is set at key\nencrypted::Bool: if true the value stored on the cookie is automatically decrypted\n\n\n\n\n\nget(res::HTTP.Response, key::Union{String,Symbol}) :: Union{Nothing,String}\n\nRetrieves a value stored on the cookie as key from the Respose object.\n\nArguments\n\npayload::Union{HTTP.Response,HTTP.Request}: the request or response object containing the Cookie headers\nkey::Union{String,Symbol}: the name of the cookie value\nencrypted::Bool: if true the value stored on the cookie is automatically decrypted\n\n\n\n\n\nget(req::Request, key::Union{String,Symbol}) :: Union{Nothing,String}\n\nRetrieves a value stored on the cookie as key from the Request object.\n\nArguments\n\nreq::HTTP.Request: the request or response object containing the Cookie headers\nkey::Union{String,Symbol}: the name of the cookie value\nencrypted::Bool: if true the value stored on the cookie is automatically decrypted\n\n\n\n\n\n","category":"function"},{"location":"API/cookies.html#Genie.Cookies.getcookies","page":"Cookies","title":"Genie.Cookies.getcookies","text":"getcookies(req::HTTP.Request) :: Vector{HTTP.Cookies.Cookie}\n\nExtracts cookies from within req\n\n\n\n\n\ngetcookies(req::HTTP.Request) :: Vector{HTTP.Cookies.Cookie}\n\nExtracts cookies from within req, filtering them by matching name.\n\n\n\n\n\n","category":"function"},{"location":"API/cookies.html#Genie.Cookies.set!","page":"Cookies","title":"Genie.Cookies.set!","text":"set!(res::HTTP.Response, key::Union{String,Symbol}, value::Any, attributes::Dict; encrypted::Bool = true) :: HTTP.Response\n\nSets value under the key label on the Cookie.\n\nArguments\n\nres::HTTP.Response: the HTTP.Response object\nkey::Union{String,Symbol}: the key for storing the cookie value\nvalue::Any: the cookie value\nattributes::Dict: additional cookie attributes, such as path, httponly, maxage\nencrypted::Bool: if true the value is stored encoded\n\n\n\n\n\n","category":"function"},{"location":"API/cookies.html#Genie.Cookies.nullablevalue","page":"Cookies","title":"Genie.Cookies.nullablevalue","text":"nullablevalue(payload::Union{HTTP.Response,HTTP.Request}, key::Union{String,Symbol}; encrypted::Bool = true)\n\nAttempts to retrieve a cookie value stored at key in the payload object and returns a Union{Nothing,String}\n\nArguments\n\npayload::Union{HTTP.Response,HTTP.Request}: the request or response object containing the Cookie headers\nkey::Union{String,Symbol}: the name of the cookie value\nencrypted::Bool: if true the value stored on the cookie is automatically decrypted\n\n\n\n\n\n","category":"function"},{"location":"API/genie.html","page":"Genie","title":"Genie","text":"CurrentModule = Genie","category":"page"},{"location":"API/genie.html","page":"Genie","title":"Genie","text":"bootstrap\ndown\ndown!\ngenie\ngo\nisrunning\nloadapp\nrun\nup","category":"page"},{"location":"API/genie.html#Genie.bootstrap","page":"Genie","title":"Genie.bootstrap","text":"genie() :: Union{Nothing,Sockets.TCPServer}\n\n\n\n\n\n","category":"function"},{"location":"API/genie.html#Genie.down","page":"Genie","title":"Genie.down","text":"down(; webserver::Bool = true, websockets::Bool = true) :: ServersCollection\n\nShuts down the servers optionally indicating which of the webserver and websockets servers to be stopped. It does not remove the servers from the SERVERS collection. Returns the collection.\n\n\n\n\n\n","category":"function"},{"location":"API/genie.html#Genie.down!","page":"Genie","title":"Genie.down!","text":"function down!(; webserver::Bool = true, websockets::Bool = true) :: Vector{ServersCollection}\n\nShuts down all the servers and empties the SERVERS collection. Returns the empty collection.\n\n\n\n\n\n","category":"function"},{"location":"API/genie.html#Genie.genie","page":"Genie","title":"Genie.genie","text":"genie() :: Union{Nothing,Sockets.TCPServer}\n\n\n\n\n\n","category":"function"},{"location":"API/genie.html#Genie.go","page":"Genie","title":"Genie.go","text":"loadapp(path::String = \".\"; autostart::Bool = false) :: Nothing\n\nLoads an existing Genie app from the file system, within the current Julia REPL session.\n\nArguments\n\npath::String: the path to the Genie app on the file system.\nautostart::Bool: automatically start the app upon loading it.\n\nExamples\n\nshell> tree -L 1\n.\n├── Manifest.toml\n├── Project.toml\n├── bin\n├── bootstrap.jl\n├── config\n├── env.jl\n├── genie.jl\n├── log\n├── public\n├── routes.jl\n└── src\n\n5 directories, 6 files\n\njulia> using Genie\n\njulia> Genie.loadapp(\".\")\n _____ _\n| __|___ ___|_|___\n| | | -_| | | -_|\n|_____|___|_|_|_|___|\n\n┌ Info:\n│ Starting Genie in >> DEV << mode\n└\n[ Info: Logging to file at MyGenieApp/log/dev.log\n\n\n\n\n\n","category":"function"},{"location":"API/genie.html#Genie.loadapp","page":"Genie","title":"Genie.loadapp","text":"loadapp(path::String = \".\"; autostart::Bool = false) :: Nothing\n\nLoads an existing Genie app from the file system, within the current Julia REPL session.\n\nArguments\n\npath::String: the path to the Genie app on the file system.\nautostart::Bool: automatically start the app upon loading it.\n\nExamples\n\nshell> tree -L 1\n.\n├── Manifest.toml\n├── Project.toml\n├── bin\n├── bootstrap.jl\n├── config\n├── env.jl\n├── genie.jl\n├── log\n├── public\n├── routes.jl\n└── src\n\n5 directories, 6 files\n\njulia> using Genie\n\njulia> Genie.loadapp(\".\")\n _____ _\n| __|___ ___|_|___\n| | | -_| | | -_|\n|_____|___|_|_|_|___|\n\n┌ Info:\n│ Starting Genie in >> DEV << mode\n└\n[ Info: Logging to file at MyGenieApp/log/dev.log\n\n\n\n\n\n","category":"function"},{"location":"API/genie.html#Genie.run","page":"Genie","title":"Genie.run","text":"run() :: Nothing\n\nRuns the Genie app by parsing the command line args and invoking the corresponding actions. Used internally to parse command line arguments.\n\n\n\n\n\n","category":"function"},{"location":"API/genie.html#Genie.up","page":"Genie","title":"Genie.up","text":"up(port::Int = Genie.config.server_port, host::String = Genie.config.server_host;\n ws_port::Int = Genie.config.websockets_port, async::Bool = ! Genie.config.run_as_server) :: Nothing\n\nStarts the web server. Alias for Server.up\n\nArguments\n\nport::Int: the port used by the web server\nhost::String: the host used by the web server\nws_port::Int: the port used by the Web Sockets server\nasync::Bool: run the web server task asynchronously\n\nExamples\n\njulia> up(8000, \"127.0.0.1\", async = false)\n[ Info: Ready!\nWeb Server starting at http://127.0.0.1:8000\n\n\n\n\n\n","category":"function"},{"location":"index.html#Genie","page":"Home","title":"Genie","text":"","category":"section"},{"location":"index.html","page":"Home","title":"Home","text":"(Image: Genie Logo)","category":"page"},{"location":"index.html#Genie-2","page":"Home","title":"Genie","text":"","category":"section"},{"location":"index.html#The-highly-productive-Julia-web-framework","page":"Home","title":"The highly productive Julia web framework","text":"","category":"section"},{"location":"index.html","page":"Home","title":"Home","text":"Genie is a full-stack MVC web framework that provides a streamlined and efficient workflow for developing modern web applications. It builds on Julia's strengths (high-level, high-performance, dynamic, JIT compiled), exposing a rich API and a powerful toolset for productive web development.","category":"page"},{"location":"index.html#Current-status","page":"Home","title":"Current status","text":"","category":"section"},{"location":"index.html","page":"Home","title":"Home","text":"Genie is compatible with Julia v1.6 and up.","category":"page"},{"location":"index.html","page":"Home","title":"Home","text":"","category":"page"},{"location":"index.html#Documentation","page":"Home","title":"Documentation","text":"","category":"section"},{"location":"index.html","page":"Home","title":"Home","text":"https://genieframework.github.io/Genie.jl/dev/","category":"page"},{"location":"index.html","page":"Home","title":"Home","text":"","category":"page"},{"location":"index.html#Acknowledgements","page":"Home","title":"Acknowledgements","text":"","category":"section"},{"location":"index.html","page":"Home","title":"Home","text":"Genie uses a multitude of packages that have been kindly contributed by the Julia community.\nThe awesome Genie logo was designed by Alvaro Casanova.","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html#Adding-your-existing-Julia-code-into-Genie-apps","page":"Adding your libraries into Genie","title":"Adding your existing Julia code into Genie apps","text":"","category":"section"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"If you have existing Julia code (modules and files) which you'd like to quickly integrate into a web app, Genie provides an easy way to add and load your code.","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html#Adding-your-Julia-code-to-the-lib/-folder","page":"Adding your libraries into Genie","title":"Adding your Julia code to the lib/ folder","text":"","category":"section"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"If you have some Julia code which you'd like to integrate in a Genie app, the simplest thing is to add the files to the lib/ folder. The files (and folders) in the lib/ folder are automatically loaded by Genie recursively. This means that you can also add folders under lib/, and they will be recursively loaded (included) into the app. Beware though that this only happens when the Genie app is initially loaded. Hence, an app restart will be required if you add files and folders after the app is started.","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"HEADS UP","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"Genie won't create the lib/ folder by default. If the lib/ folder is not present in the root of the app, just create it yourself:","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"julia> mkdir(\"lib\")","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"Once your code is added to the lib/ folder, it will become available in your app's environment. For example, say we have a file called lib/MyLib.jl:","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"# lib/MyLib.jl\nmodule MyLib\n\nusing Dates\n\nfunction isitfriday()\n Dates.dayofweek(Dates.now()) == Dates.Friday\nend\n\nend","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"Assuming that the name of your Genie app (which is also the name of your main module in src/) is MyGenieApp, the modules loaded from lib/ will be available under the MyGenieApp namespace as MyGenieApp.MyLib.","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"HEADS UP","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"Instead of using the actual Genie app (main module) name, we can also use the alias ..Main.UserApp.","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"So we can reference and use our modules in lib/ in routes.jl as follows:","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"# routes.jl\nusing Genie\nusing MyGenieApp.MyLib # or using ..Main.UserApp.MyLib\n\nroute(\"/friday\") do\n MyLib.isitfriday() ? \"Yes, it's Friday!\" : \"No, not yet :(\"\nend","category":"page"},{"location":"tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html","page":"Adding your libraries into Genie","title":"Adding your libraries into Genie","text":"Use the lib/ folder to host your Julia code so that Genie knows where to look in order to load it and make it available throughout the application.","category":"page"},{"location":"tutorials/8--Handling_File_Uploads.html#Handling-file-uploads","page":"Uploading files","title":"Handling file uploads","text":"","category":"section"},{"location":"tutorials/8--Handling_File_Uploads.html","page":"Uploading files","title":"Uploading files","text":"Genie has built-in support for working with file uploads. The collection of uploaded files (as POST variables) can be accessed through the Requests.filespayload method. Or, we can retrieve the data corresponding to a given file form input by using Requests.filespayload(key) – where key is the name of the file input in the form.","category":"page"},{"location":"tutorials/8--Handling_File_Uploads.html","page":"Uploading files","title":"Uploading files","text":"In the following snippet we configure two routes in the root of the app (/): the first route, handling GET requests, displays an upload form. The second route, handling POST requests, processes the uploads, generating a file from the uploaded data, saving it, and displaying the file stats.","category":"page"},{"location":"tutorials/8--Handling_File_Uploads.html","page":"Uploading files","title":"Uploading files","text":"","category":"page"},{"location":"tutorials/8--Handling_File_Uploads.html","page":"Uploading files","title":"Uploading files","text":"HEADS UP","category":"page"},{"location":"tutorials/8--Handling_File_Uploads.html","page":"Uploading files","title":"Uploading files","text":"Notice that we can define multiple routes at the same URL if they have different methods, in our case GET and POST.","category":"page"},{"location":"tutorials/8--Handling_File_Uploads.html","page":"Uploading files","title":"Uploading files","text":"","category":"page"},{"location":"tutorials/8--Handling_File_Uploads.html#Example","page":"Uploading files","title":"Example","text":"","category":"section"},{"location":"tutorials/8--Handling_File_Uploads.html","page":"Uploading files","title":"Uploading files","text":"using Genie, Genie.Router, Genie.Renderer.Html, Genie.Requests\n\nform = \"\"\"\n
      \n
      \n \n
      \n\"\"\"\n\nroute(\"/\") do\n html(form)\nend\n\nroute(\"/\", method = POST) do\n if infilespayload(:yourfile)\n write(filespayload(:yourfile))\n\n stat(filename(filespayload(:yourfile)))\n else\n \"No file uploaded\"\n end\nend\n\nup()","category":"page"},{"location":"tutorials/8--Handling_File_Uploads.html","page":"Uploading files","title":"Uploading files","text":"Upon uploading a file and submitting the form, our app will display the file's stats.","category":"page"},{"location":"API/util.html","page":"Util","title":"Util","text":"CurrentModule = Util","category":"page"},{"location":"API/util.html","page":"Util","title":"Util","text":"expand_nullable\nfile_name_without_extension\nwalk_dir\ntime_to_unixtimestamp","category":"page"},{"location":"API/util.html#Genie.Util.file_name_without_extension","page":"Util","title":"Genie.Util.file_name_without_extension","text":"file_name_without_extension(file_name, extension = \".jl\") :: String\n\nRemoves the file extension extension from file_name.\n\n\n\n\n\n","category":"function"},{"location":"API/util.html#Genie.Util.walk_dir","page":"Util","title":"Genie.Util.walk_dir","text":"function walk_dir(dir, paths = String[]; only_extensions = [\"jl\"], only_files = true, only_dirs = false) :: Vector{String}\n\nRecursively walks dir and produces non directories. If only_files, directories will be skipped. If only_dirs, files will be skipped.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-js.html","page":"JS Renderer","title":"JS Renderer","text":"CurrentModule = Renderer.Js","category":"page"},{"location":"API/renderer-js.html","page":"JS Renderer","title":"JS Renderer","text":"get_template\nto_js\nrender\njs\nGenie.Router.error","category":"page"},{"location":"API/filetemplates.html","page":"FileTemplates","title":"FileTemplates","text":"CurrentModule = FileTemplates","category":"page"},{"location":"API/filetemplates.html","page":"FileTemplates","title":"FileTemplates","text":"appmodule\nnewcontroller\nnewtask\nnewtest","category":"page"},{"location":"API/filetemplates.html#Genie.FileTemplates.appmodule","page":"FileTemplates","title":"Genie.FileTemplates.appmodule","text":"appmodule(path::String)\n\nGenerates a custom app module when a new app is bootstrapped.\n\n\n\n\n\n","category":"function"},{"location":"API/filetemplates.html#Genie.FileTemplates.newcontroller","page":"FileTemplates","title":"Genie.FileTemplates.newcontroller","text":"newcontroller(controller_name::String) :: String\n\nDefault content for a new Genie controller.\n\n\n\n\n\n","category":"function"},{"location":"API/filetemplates.html#Genie.FileTemplates.newtask","page":"FileTemplates","title":"Genie.FileTemplates.newtask","text":"newtask(module_name::String) :: String\n\nDefault content for a new Genie Toolbox task.\n\n\n\n\n\n","category":"function"},{"location":"API/filetemplates.html#Genie.FileTemplates.newtest","page":"FileTemplates","title":"Genie.FileTemplates.newtest","text":"newtest(plural_name::String, singular_name::String) :: String\n\nDefault content for a new test file.\n\n\n\n\n\n","category":"function"},{"location":"tutorials/3--Getting_Started.html#Hello-world-with-Genie","page":"Getting started","title":"Hello world with Genie","text":"","category":"section"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"Here are a few examples to quickly get you started with building Genie web apps.","category":"page"},{"location":"tutorials/3--Getting_Started.html#Running-Genie-interactively-at-the-REPL-or-in-notebooks","page":"Getting started","title":"Running Genie interactively at the REPL or in notebooks","text":"","category":"section"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"The simplest use case is to configure a routing function at the REPL and start the web server. That's all that's needed to run your code on the web page:","category":"page"},{"location":"tutorials/3--Getting_Started.html#Example","page":"Getting started","title":"Example","text":"","category":"section"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"julia> using Genie\n\njulia> route(\"/hello\") do\n \"Hello World\"\n end\n\njulia> up()","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"The route function defines a mapping between a URL (\"/hello\") and a Julia function (a handler) which will be automatically invoked to send the response back to the client. In this case we're sending back the string \"Hello World\".","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"That's all! We have set up an app, a route, and started the web server. Open your favorite web browser and go to http://127.0.0.1:8000/hello to see the result.","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"HEADS UP","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"Keep in mind that Julia JIT-compiles. A function is automatically compiled the first time it is invoked. The function, in this case, is our route handler that is responding to the request. This will make the first response slower as it also includes compilation time. But once the function is compiled, for all the subsequent requests, it will be super fast!","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"","category":"page"},{"location":"tutorials/3--Getting_Started.html#Developing-a-simple-Genie-script","page":"Getting started","title":"Developing a simple Genie script","text":"","category":"section"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"Genie can also be used in custom scripts, for example when building micro-services with Julia. Let's create a simple \"Hello World\" micro-service.","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"Start by creating a new file to host our code – let's call it geniews.jl","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"julia> touch(\"geniews.jl\")","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"Now, open it in the editor:","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"julia> edit(\"geniews.jl\")","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"Add the following code:","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Renderer.Json\n\nroute(\"/hello.html\") do\n html(\"Hello World\")\nend\n\nroute(\"/hello.json\") do\n json(\"Hello World\")\nend\n\nroute(\"/hello.txt\") do\n respond(\"Hello World\", :text)\nend\n\nup(8001, async = false)","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"We begun by defining 2 routes and we used the html and json rendering functions (available in the Renderer.Html and the Renderer.Json modules). These functions are responsible for outputting the data using the correct format and document type (with the correct MIME), in our case HTML data for hello.html, and JSON data for hello.json.","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"The third route serves text responses. As Genie does not provide a specialized text() method for sending text/plain responses, we use the generic respond function, indicating the desired MIME type. In our case :text, corresponding to text/plain. Other available MIME types shortcuts are :xml, :markdown, :javascript and a few others others – and users can register their own mime types and response types as needed or can pass the full mime type as a string, ie \"text/csv\".","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"The up function will launch the web server on port 8001. This time, very important, we instructed it to start the server synchronously (that is, blocking the execution of the script), by passing the async = false argument. This way we make sure that our script stays running. Otherwise, at the end of the script, the Julia process would normally exit, killing our server.","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"In order to launch the script, run $ julia geniews.jl.","category":"page"},{"location":"tutorials/3--Getting_Started.html#Batteries-included","page":"Getting started","title":"Batteries included","text":"","category":"section"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"Genie readily makes available a rich set of features - you have already seen the rendering and the routing modules in action. But for instance, logging (to file and console) can also be easily triggered with one line of code, powerful caching can be enabled with a couple more lines, and so on.","category":"page"},{"location":"tutorials/3--Getting_Started.html","page":"Getting started","title":"Getting started","text":"The app already handles \"404 Page Not Found\" and \"500 Internal Error\" responses. If you try to access a URL which is not handled by the app, like say http://127.0.0.1:8001/not_here, you'll see Genie's default 404 page. The default error pages can be overwritten with custom ones.","category":"page"},{"location":"API/renderer-html.html","page":"HTML Renderer","title":"HTML Renderer","text":"CurrentModule = Renderer.Html","category":"page"},{"location":"API/renderer-html.html","page":"HTML Renderer","title":"HTML Renderer","text":"normal_element\nprepare_template\nattributes\nparseattr\nnormalize_element\ndenormalize_element\nvoid_element\nskip_element\ninclude_markdown\nget_template\ndoctype\ndoc\nparseview\nrender\nparsehtml\nGenie.Renderer.render\nhtml\nsafe_attr\nparsehtml\nhtml_to_julia\nstring_to_julia\nto_julia\npartial\ntemplate\nread_template_file\nparse_template\nparse_string\nparse\nparsetags\nregister_elements\nregister_element\nregister_normal_element\nregister_void_element\nattr\nfor_each\ncollection\nGenie.Router.error\nserve_error_file\n@yield\nel","category":"page"},{"location":"API/renderer-html.html#Genie.Renderer.Html.normal_element","page":"HTML Renderer","title":"Genie.Renderer.Html.normal_element","text":"normal_element(f::Function, elem::String, attrs::Vector{Pair{Symbol,Any}} = Pair{Symbol,Any}[]) :: HTMLString\n\nGenerates a HTML element in the form <...>\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.prepare_template","page":"HTML Renderer","title":"Genie.Renderer.Html.prepare_template","text":"prepare_template(s::String)\nprepare_template{T}(v::Vector{T})\n\nCleans up the template before rendering (ex by removing empty nodes).\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.attributes","page":"HTML Renderer","title":"Genie.Renderer.Html.attributes","text":"attributes(attrs::Vector{Pair{Symbol,String}} = Vector{Pair{Symbol,String}}()) :: Vector{String}\n\nParses HTML attributes.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.parseattr","page":"HTML Renderer","title":"Genie.Renderer.Html.parseattr","text":"parseattr(attr) :: String\n\nConverts Julia keyword arguments to HTML attributes with illegal Julia chars.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.normalize_element","page":"HTML Renderer","title":"Genie.Renderer.Html.normalize_element","text":"normalize_element(elem::String)\n\nCleans up problematic characters or DOM elements.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.denormalize_element","page":"HTML Renderer","title":"Genie.Renderer.Html.denormalize_element","text":"denormalize_element(elem::String)\n\nReplaces - with the char defined to replace dashes, as Julia does not support them in names.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.void_element","page":"HTML Renderer","title":"Genie.Renderer.Html.void_element","text":"void_element(elem::String, attrs::Vector{Pair{Symbol,String}} = Vector{Pair{Symbol,String}}()) :: HTMLString\n\nGenerates a void HTML element in the form <...>\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.get_template","page":"HTML Renderer","title":"Genie.Renderer.Html.get_template","text":"get_template(path::String; partial::Bool = true, context::Module = @__MODULE__, vars...) :: Function\n\nResolves the inclusion and rendering of a template file\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.doctype","page":"HTML Renderer","title":"Genie.Renderer.Html.doctype","text":"Outputs document's doctype.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.doc","page":"HTML Renderer","title":"Genie.Renderer.Html.doc","text":"Outputs document's doctype.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.parseview","page":"HTML Renderer","title":"Genie.Renderer.Html.parseview","text":"parseview(data::String; partial = false, context::Module = @__MODULE__) :: Function\n\nParses a view file, returning a rendering function. If necessary, the function is JIT-compiled, persisted and loaded into memory.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.render","page":"HTML Renderer","title":"Genie.Renderer.Html.render","text":"render(data::String; context::Module = @__MODULE__, layout::Union{String,Nothing} = nothing, vars...) :: Function\n\nRenders the string as an HTML view.\n\n\n\n\n\nrender(viewfile::Genie.Renderer.FilePath; layout::Union{Nothing,Genie.Renderer.FilePath} = nothing, context::Module = @__MODULE__, vars...) :: Function\n\nRenders the template file as an HTML view.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.parsehtml","page":"HTML Renderer","title":"Genie.Renderer.Html.parsehtml","text":"parsehtml(input::String; partial::Bool = true) :: String\n\n\n\n\n\nparsehtml(elem, output; partial = true) :: String\n\nParses a HTML tree structure into a string of Julia code.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.render","page":"HTML Renderer","title":"Genie.Renderer.render","text":"render\n\nAbstract function that needs to be specialized by individual renderers.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.html","page":"HTML Renderer","title":"Genie.Renderer.Html.html","text":"html(data::String; context::Module = @__MODULE__, status::Int = 200, headers::HTTPHeaders = HTTPHeaders(), layout::Union{String,Nothing} = nothing, vars...) :: HTTP.Response\n\nParses the data input as HTML, returning a HTML HTTP Response.\n\nArguments\n\ndata::String: the HTML string to be rendered\ncontext::Module: the module in which the variables are evaluated (in order to provide the scope for vars). Usually the controller.\nstatus::Int: status code of the response\nheaders::HTTPHeaders: HTTP response headers\nlayout::Union{String,Nothing}: layout file for rendering data\n\nExample\n\njulia> html(\"

      Welcome $(vars(:name))

      \", layout = \"
      <% @yield %>
      \", name = \"Adrian\")\nHTTP.Messages.Response:\n\"\nHTTP/1.1 200 OK\nContent-Type: text/html; charset=utf-8\n\n

      Welcome Adrian

      \n
      \"\n\n\n\n\n\nhtml(md::Markdown.MD; context::Module = @__MODULE__, status::Int = 200, headers::Genie.Renderer.HTTPHeaders = Genie.Renderer.HTTPHeaders(), layout::Union{String,Nothing} = nothing, forceparse::Bool = false, vars...) :: Genie.Renderer.HTTP.Response\n\nMarkdown view rendering\n\n\n\n\n\nhtml(viewfile::FilePath; layout::Union{Nothing,FilePath} = nothing,\n context::Module = @__MODULE__, status::Int = 200, headers::HTTPHeaders = HTTPHeaders(), vars...) :: HTTP.Response\n\nParses and renders the HTML viewfile, optionally rendering it within the layout file. Valid file format is .html.jl.\n\nArguments\n\nviewfile::FilePath: filesystem path to the view file as a Renderer.FilePath, ie Renderer.filepath(\"/path/to/file.html.jl\") or path\"/path/to/file.html.jl\"\nlayout::FilePath: filesystem path to the layout file as a Renderer.FilePath, ie Renderer.FilePath(\"/path/to/file.html.jl\") or path\"/path/to/file.html.jl\"\ncontext::Module: the module in which the variables are evaluated (in order to provide the scope for vars). Usually the controller.\nstatus::Int: status code of the response\nheaders::HTTPHeaders: HTTP response headers\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.safe_attr","page":"HTML Renderer","title":"Genie.Renderer.Html.safe_attr","text":"safe_attr(attr) :: String\n\nReplaces illegal Julia characters from HTML attributes with safe ones, to be used as keyword arguments.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.html_to_julia","page":"HTML Renderer","title":"Genie.Renderer.Html.html_to_julia","text":"html_to_julia(file_path::String; partial = true) :: String\n\nConverts a HTML document to Julia code.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.string_to_julia","page":"HTML Renderer","title":"Genie.Renderer.Html.string_to_julia","text":"string_to_julia(content::String; partial = true, f_name::Union{Symbol,Nothing} = nothing, prepend = \"\") :: String\n\nConverts string view data to Julia code\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.to_julia","page":"HTML Renderer","title":"Genie.Renderer.Html.to_julia","text":"to_julia(input::String, f::Function; partial = true, f_name::Union{Symbol,Nothing} = nothing, prepend = \"\") :: String\n\nConverts an input file to Julia code\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.partial","page":"HTML Renderer","title":"Genie.Renderer.Html.partial","text":"partial(path::String; context::Module = @__MODULE__, vars...) :: String\n\nRenders (includes) a view partial within a larger view or layout file.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.template","page":"HTML Renderer","title":"Genie.Renderer.Html.template","text":"template(path::String; partial::Bool = true, context::Module = @__MODULE__, vars...) :: String\n\nRenders a template file.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.read_template_file","page":"HTML Renderer","title":"Genie.Renderer.Html.read_template_file","text":"read_template_file(file_path::String) :: String\n\nReads file_path template from disk.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.parse_template","page":"HTML Renderer","title":"Genie.Renderer.Html.parse_template","text":"parse_template(file_path::String; partial = true) :: String\n\nParses a HTML file into Julia code.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.parse_string","page":"HTML Renderer","title":"Genie.Renderer.Html.parse_string","text":"parse_string(data::String; partial = true) :: String\n\nParses a HTML string into Julia code.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.register_elements","page":"HTML Renderer","title":"Genie.Renderer.Html.register_elements","text":"register_elements() :: Nothing\n\nGenerated functions that represent Julia functions definitions corresponding to HTML elements.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.register_element","page":"HTML Renderer","title":"Genie.Renderer.Html.register_element","text":"register_element(elem::Union{Symbol,String}, elem_type::Union{Symbol,String} = :normal; context = @__MODULE__) :: Nothing\n\nGenerates a Julia function representing an HTML element.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.register_normal_element","page":"HTML Renderer","title":"Genie.Renderer.Html.register_normal_element","text":"register_normal_element(elem::Union{Symbol,String}; context = @__MODULE__) :: Nothing\n\nGenerates a Julia function representing a \"normal\" HTML element: that is an element with a closing tag, ...\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.register_void_element","page":"HTML Renderer","title":"Genie.Renderer.Html.register_void_element","text":"register_void_element(elem::Union{Symbol,String}; context::Module = @__MODULE__) :: Nothing\n\nGenerates a Julia function representing a \"void\" HTML element: that is an element without a closing tag, \n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.for_each","page":"HTML Renderer","title":"Genie.Renderer.Html.for_each","text":"for_each(f::Function, v)\n\nIterates over the v Vector and applies function f for each element. The results of each iteration are concatenated and the final string is returned.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.collection","page":"HTML Renderer","title":"Genie.Renderer.Html.collection","text":"collection(template::Function, collection::Vector{T})::String where {T}\n\nCreates a view fragment by repeateadly applying a function to each element of the collection.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Router.error","page":"HTML Renderer","title":"Genie.Router.error","text":"error\n\nNot implemented function for error response.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.serve_error_file","page":"HTML Renderer","title":"Genie.Renderer.Html.serve_error_file","text":"serve_error_file(error_code::Int, error_message::String = \"\", params::Dict{Symbol,Any} = Dict{Symbol,Any}()) :: Response\n\nServes the error file correspoding to error_code and current environment.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer-html.html#Genie.Renderer.Html.@yield","page":"HTML Renderer","title":"Genie.Renderer.Html.@yield","text":"@yield\n\nOutputs the rendering of the view within the template.\n\n\n\n\n\n","category":"macro"},{"location":"API/renderer.html","page":"Renderer","title":"Renderer","text":"CurrentModule = Renderer","category":"page"},{"location":"API/renderer.html","page":"Renderer","title":"Renderer","text":"WebRenderable\nrender\nredirect\nhasrequested\nrespond\nregistervars\ninjectvars\nview_file_info\nvars_signature\nfunction_name\nm_name\nbuild_is_stale\nbuild_module\npreparebuilds\npurgebuilds\nchangebuilds\nset_negotiated_content\nnegotiate_content","category":"page"},{"location":"API/renderer.html#Genie.Renderer.WebRenderable","page":"Renderer","title":"Genie.Renderer.WebRenderable","text":"mutable struct WebRenderable\n\nRepresents an object that can be rendered on the web as a HTTP Response\n\n\n\n\n\n","category":"type"},{"location":"API/renderer.html#Genie.Renderer.redirect","page":"Renderer","title":"Genie.Renderer.redirect","text":"Sets redirect headers and prepares the Response. It accepts 3 parameters: 1 - Label of a Route (to learn more, see the advanced routes section) 2 - Default HTTP 302 Found Status: indicates that the provided resource will be changed to a URL provided 3 - Tuples (key, value) to define the HTTP request header\n\nExample: julia> Genie.Renderer.redirect(:index, 302, Dict(\"Content-Type\" => \"application/json; charset=UTF-8\"))\n\nHTTP.Messages.Response: HTTP/1.1 302 Moved Temporarily Content-Type: application/json; charset=UTF-8 Location: /index\n\nRedirecting you to /index\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.hasrequested","page":"Renderer","title":"Genie.Renderer.hasrequested","text":"hasrequested(content_type::Symbol) :: Bool\n\nChecks wheter or not the requested content type matches content_type.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.respond","page":"Renderer","title":"Genie.Renderer.respond","text":"Constructs a Response corresponding to the Content-Type of the request.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.registervars","page":"Renderer","title":"Genie.Renderer.registervars","text":"registervars(vs...) :: Nothing\n\nLoads the rendering vars into the task's scope\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.view_file_info","page":"Renderer","title":"Genie.Renderer.view_file_info","text":"view_file_info(path::String, supported_extensions::Vector{String}) :: Tuple{String,String}\n\nExtracts path and extension info about a file\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.vars_signature","page":"Renderer","title":"Genie.Renderer.vars_signature","text":"vars_signature() :: String\n\nCollects the names of the view vars in order to create a unique hash/salt to identify compiled views with different vars.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.function_name","page":"Renderer","title":"Genie.Renderer.function_name","text":"function_name(file_path::String)\n\nGenerates function name for generated HTML+Julia views.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.m_name","page":"Renderer","title":"Genie.Renderer.m_name","text":"m_name(file_path::String)\n\nGenerates module name for generated HTML+Julia views.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.build_is_stale","page":"Renderer","title":"Genie.Renderer.build_is_stale","text":"build_is_stale(file_path::String, build_path::String) :: Bool\n\nChecks if the view template has been changed since the last time the template was compiled.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.build_module","page":"Renderer","title":"Genie.Renderer.build_module","text":"build_module(content::String, path::String, mod_name::String) :: String\n\nPersists compiled Julia view data to file and returns the path\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.preparebuilds","page":"Renderer","title":"Genie.Renderer.preparebuilds","text":"preparebuilds() :: Bool\n\nSets up the build folder and the build module file for generating the compiled views.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.purgebuilds","page":"Renderer","title":"Genie.Renderer.purgebuilds","text":"purgebuilds(subfolder = BUILD_NAME) :: Bool\n\nRemoves the views builds folders with all the generated views.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.changebuilds","page":"Renderer","title":"Genie.Renderer.changebuilds","text":"changebuilds(subfolder = BUILD_NAME) :: Bool\n\nChanges/creates a new builds folder.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.set_negotiated_content","page":"Renderer","title":"Genie.Renderer.set_negotiated_content","text":"set_negotiated_content(req::HTTP.Request, res::HTTP.Response, params::Dict{Symbol,Any})\n\nConfigures the request, response, and params response content type based on the request and defaults.\n\n\n\n\n\n","category":"function"},{"location":"API/renderer.html#Genie.Renderer.negotiate_content","page":"Renderer","title":"Genie.Renderer.negotiate_content","text":"negotiate_content(req::Request, res::Response, params::Params) :: Response\n\nComputes the content-type of the Response, based on the information in the Request.\n\n\n\n\n\n","category":"function"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"we'# Developing Genie MVC Apps","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Here is a complete walk-through of developing a feature rich MVC app with Genie, including both user facing web pages, a REST API endpoint, and user authentication.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Getting-started-creating-the-app","page":"Developing MVC web applications","title":"Getting started - creating the app","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"First, let's create a new Genie MVC app. We'll use Genie's app generator, so first let's make sure we have Genie installed.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Let's start a Julia REPL and add Genie:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"pkg> add Genie # press ] from julia> prompt to enter Pkg mode","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Now, to create the app:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> using Genie\r\n\r\njulia> Genie.Generator.newapp_mvc(\"Watch Tonight\")","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"HEADS UP","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"The newapp_mvc function creates a new app with the given name, although spaces are not allowed in the name and Genie will automatically correct it as WatchTonight. WatchTonight is the name of the app and the name of the main module of the project, in the src/ folder. You will see it used when we will reference the various files in the project, in the using statements.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Genie will bootstrap a new application for us, creating the necessary files and installing dependencies. As we're creating a MVC app, Genie will offer to install support for SearchLight, Genie's ORM, and will ask what database backend we'll want to use:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Please choose the DB backend you want to use:\r\n1. SQLite\r\n2. MySQL\r\n3. PostgreSQL\r\nInput 1, 2 or 3 and press ENTER to confirm","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Note that if you select an option other than SQLite, you will need to manually create the database outside of Genie. Currently, Genie only automatically creates SQLite databases.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We'll use SQLite in this demo, so let's press \"1\". Once the process is completed, Genie will start the new application at http://127.0.0.1:8000. We can open it in the browser to see the default Genie home page.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#How-does-this-work?","page":"Developing MVC web applications","title":"How does this work?","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Genie uses the concept of routes and routing in order to map a URL to a request handler (a Julia function) within the app. If we edit the routes.jl file we will see that is has defined a route that states that for any requests to the / URL, the app will display a static file called welcome.html (which can be found in the public/ folder):","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"route(\"/\") do\r\n serve_static_file(\"welcome.html\")\r\nend","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Connecting-to-the-database","page":"Developing MVC web applications","title":"Connecting to the database","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"In order to configure the database connection we need to edit the db/connection.yml file, to make it look like this:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"env: ENV[\"GENIE_ENV\"]\r\n\r\ndev:\r\n adapter: SQLite\r\n database: db/netflix_catalog.sqlite","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Now let's manually load the database configuration:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> include(joinpath(\"config\", \"initializers\", \"searchlight.jl\"))","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Creating-a-Movie-resource","page":"Developing MVC web applications","title":"Creating a Movie resource","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"A resource is an entity exposed by the application at a URL. In a Genie MVC app it represents a bundle of Model, views, and Controller files - as well as possible additional files such as migration files for creating a database table, tests, a model data validator, etc.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"In the REPL run:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> Genie.Generator.newresource(\"movie\")\r\n\r\njulia> using SearchLight\r\n\r\njulia> SearchLight.Generator.newresource(\"movie\")","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"This should create a series of files to represent the Movie resource - just take a look at the output to see what and where was created.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Creating-the-DB-table-using-the-database-migration","page":"Developing MVC web applications","title":"Creating the DB table using the database migration","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We need to edit the migrations file that was just created in db/migrations/. Look for a file that ends in _create_table_movies.jl and make it look like this:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"module CreateTableMovies\r\n\r\nimport SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table\r\n\r\nfunction up()\r\n create_table(:movies) do\r\n [\r\n primary_key()\r\n column(:type, :string, limit = 10)\r\n column(:title, :string, limit = 100)\r\n column(:directors, :string, limit = 100)\r\n column(:actors, :string, limit = 250)\r\n column(:country, :string, limit = 100)\r\n column(:year, :integer)\r\n column(:rating, :string, limit = 10)\r\n column(:categories, :string, limit = 100)\r\n column(:description, :string, limit = 1_000)\r\n ]\r\n end\r\n\r\n add_index(:movies, :title)\r\n add_index(:movies, :actors)\r\n add_index(:movies, :categories)\r\n add_index(:movies, :description)\r\nend\r\n\r\nfunction down()\r\n drop_table(:movies)\r\nend\r\n\r\nend","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Creating-the-migrations-table","page":"Developing MVC web applications","title":"Creating the migrations table","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"In order to be able to manage the app's migrations, we need to create the DB table used by SearchLight's migration system. This is easily done using SearchLight's generators:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> SearchLight.Migration.init()","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Running-the-migration","page":"Developing MVC web applications","title":"Running the migration","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We can now check the status of the migrations:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> SearchLight.Migration.status()","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We should see that we have one migration that is DOWN (meaning that we need to run the migration because it has not been executed yet).","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We execute the migration by running the last migration UP:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> SearchLight.Migration.lastup()","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"If you now recheck the status of the migrations, you should see that the migration is now UP.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Creating-the-Movie-model","page":"Developing MVC web applications","title":"Creating the Movie model","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Now that we have the database table, we need to create the model file which allows us manage the data. The file has already been created for us in app/resources/movies/Movies.jl. Edit it and make it look like this:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"module Movies\r\n\r\nimport SearchLight: AbstractModel, DbId\r\nimport Base: @kwdef\r\n\r\nexport Movie\r\n\r\n@kwdef mutable struct Movie <: AbstractModel\r\n id::DbId = DbId()\r\n type::String = \"Movie\"\r\n title::String = \"\"\r\n directors::String = \"\"\r\n actors::String = \"\"\r\n country::String = \"\"\r\n year::Int = 0\r\n rating::String = \"\"\r\n categories::String = \"\"\r\n description::String = \"\"\r\nend\r\n\r\nend","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Interacting-with-the-movies-data","page":"Developing MVC web applications","title":"Interacting with the movies data","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Once our model is created, we can interact with the database:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> using Movies\r\n\r\njulia> m = Movie(title = \"Test movie\", actors = \"John Doe, Jane Doe\")","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We can check if our movie object is persisted (saved to the db):","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> ispersisted(m)\r\nfalse","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"And we can save it:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> save(m)\r\ntrue","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Now we can run various methods against our data:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> count(Movie)\r\n\r\njulia> all(Movie)","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Seeding-the-data","page":"Developing MVC web applications","title":"Seeding the data","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We're now ready to load the movie data into our database - we'll use a short seeding script. First make sure to place the CVS file into the /db/seeds/ folder. Create the seeds file:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> touch(joinpath(\"db\", \"seeds\", \"seed_movies.jl\"))","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"And edit it to look like this:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"using SearchLight, WatchTonight.Movies\r\nusing CSV\r\n\r\nBase.convert(::Type{String}, _::Missing) = \"\"\r\nBase.convert(::Type{Int}, _::Missing) = 0\r\nBase.convert(::Type{Int}, s::String) = parse(Int, s)\r\n\r\nfunction seed()\r\n for row in CSV.Rows(joinpath(@__DIR__, \"netflix_titles.csv\"), limit = 1_000)\r\n m = Movie()\r\n\r\n m.type = row.type\r\n m.title = row.title\r\n m.directors = row.director\r\n m.actors = row.cast\r\n m.country = row.country\r\n m.year = parse(Int, row.release_year)\r\n m.rating = row.rating\r\n m.categories = row.listed_in\r\n m.description = row.description\r\n\r\n save(m)\r\n end\r\nend","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Add CSV.jl as a dependency of the project:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"pkg> add CSV","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"And download the dataset:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> download(\"https://raw.githubusercontent.com/essenciary/genie-watch-tonight/main/db/seeds/netflix_titles.csv\", joinpath(\"db\", \"seeds\", \"netflix_titles.csv\"))","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Now, to seed the db:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> include(joinpath(\"db\", \"seeds\", \"seed_movies.jl\"))\r\njulia> seed()","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Setting-up-the-web-page","page":"Developing MVC web applications","title":"Setting up the web page","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We'll start by adding the route to our handler function. Let's open the routes.jl file and add:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"# routes.jl\r\nusing WatchTonight.MoviesController\r\n\r\nroute(\"/movies\", MoviesController.index)","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"This route declares that the /movies URL will be handled by the MoviesController.index index function. Let's put it in by editing /app/resources/movies/MoviesController.jl:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"module MoviesController\r\n\r\nfunction index()\r\n \"Welcome to movies list!\"\r\nend\r\n\r\nend","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"If we navigate to http://127.0.0.1:8000/movies we should see the welcome.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Let's make this more useful though and display a random movie upon landing here:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"module MoviesController\r\n\r\nusing Genie.Renderer.Html, SearchLight, WatchTonight.Movies\r\n\r\nfunction index()\r\n html(:movies, :index, movies = rand(Movie))\r\nend\r\n\r\nend","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"The index function renders the /app/resources/movies/views/index.jl.html view file as HTML, passing it a random movie into the movies instance. Since we don't have the view file yet, let's add it:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> touch(joinpath(\"app\", \"resources\", \"movies\", \"views\", \"index.jl.html\"))","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Make it look like this:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"

      Watch tonight

      \r\n<%\r\nif ! isempty(movies)\r\n for_each(movies) do movie\r\n partial(joinpath(Genie.config.path_resources, \"movies\", \"views\", \"_movie.jl.html\"), movie = movie)\r\n end\r\nelse\r\n partial(joinpath(Genie.config.path_resources, \"movies\", \"views\", \"_no_results.jl.html\"))\r\nend\r\n%>","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Now to create the _movie.jl.html partial file to render a movie object:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> touch(joinpath(\"app\", \"resources\", \"movies\", \"views\", \"_movie.jl.html\"))","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Edit it like this:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"
      \r\n

      <% movie.title %>

      \r\n\r\n
      \r\n <% movie.year %> |\r\n <% movie.type %> |\r\n <% movie.rating %>\r\n
      \r\n\r\n

      <% movie.description %>

      \r\n\r\n
      Directed by: <% movie.directors %>
      \r\n
      Cast: <% movie.actors %>
      \r\n
      Country: <% movie.country %>
      \r\n
      Categories: <% movie.categories %>
      \r\n
      ","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"And finally, the _no_results.jl.html partial:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> touch(joinpath(\"app\", \"resources\", \"movies\", \"views\", \"_no_results.jl.html\"))","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Which must look like this:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"

      \r\n Sorry, no results were found for \"$(params(:search_movies))\"\r\n

      ","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Using-the-layout-file","page":"Developing MVC web applications","title":"Using the layout file","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Let's make the web page nicer by loading the Twitter Bootstrap CSS library. As it will be used across all the pages of the website, we'll load it in the main layout file. Edit /app/layouts/app.jl.html to look like this:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"\r\n\r\n \r\n \r\n Genie :: The Highly Productive Julia Web Framework\r\n \r\n \r\n \r\n
      \r\n <%\r\n @yield\r\n %>\r\n
      \r\n \r\n","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Adding-the-search-feature","page":"Developing MVC web applications","title":"Adding the search feature","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Now that we can display titles, it's time to implement the search feature. We'll add a search form onto our page. Edit /app/resources/movies/views/index.jl.html to look like this:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"

      Watch tonight

      \r\n\r\n
      \r\n
      \r\n \r\n
      \r\n
      \r\n\r\n<%\r\nif ! isempty(movies)\r\n for_each(movies) do movie\r\n partial(joinpath(Genie.config.path_resources, \"movies\", \"views\", \"_movie.jl.html\"), movie = movie)\r\n end\r\nelse\r\n partial(joinpath(Genie.config.path_resources, \"movies\", \"views\", \"_no_results.jl.html\"))\r\nend\r\n%>","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We have added a HTML
      which submits a query term over GET.","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Next, add the route:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"route(\"/movies/search\", MoviesController.search, named = :search_movies)","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"And the MoviesController.search function after updating the using section:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"using Genie, Genie.Renderer, Genie.Renderer.Html, SearchLight, WatchTonight.Movies\r\n\r\nfunction search()\r\n isempty(strip(params(:search_movies))) && redirect(:get_movies)\r\n\r\n movies = find(Movie,\r\n SQLWhereExpression(\"title LIKE ? OR categories LIKE ? OR description LIKE ? OR actors LIKE ?\",\r\n repeat(['%' * params(:search_movies) * '%'], 4)))\r\n\r\n html(:movies, :index, movies = movies)\r\nend","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Time to check our progress: http://127.0.0.1:8000/movies","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Building-the-REST-API","page":"Developing MVC web applications","title":"Building the REST API","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Let's start by adding a new route for the API search:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"route(\"/movies/search_api\", MoviesController.search_api)","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"With the corresponding search_api method in the MoviesController model:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"using Genie, Genie.Renderer, Genie.Renderer.Html, SearchLight, WatchTonight.Movies, Genie.Renderer.Json\r\n\r\nfunction search_api()\r\n movies = find(Movie,\r\n SQLWhereExpression(\"title LIKE ? OR categories LIKE ? OR description LIKE ? OR actors LIKE ?\",\r\n repeat(['%' * params(:search_movies) * '%'], 4)))\r\n\r\n json(Dict(\"movies\" => movies))\r\nend","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html#Bonus","page":"Developing MVC web applications","title":"Bonus","text":"","category":"section"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Genie makes it easy to add database backed authentication for restricted area of a website, by using the GenieAuthentication plugin. Start by adding package:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"pkg> add GenieAuthentication\r\n\r\njulia> using GenieAuthentication","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Now, to install the plugin files:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> GenieAuthentication.install(@__DIR__)","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"The plugin has created a create table migration that we need to run UP:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> SearchLight.Migration.up(\"CreateTableUsers\")","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Let's generate an Admin controller that we'll want to protect by login:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> Genie.Generator.newcontroller(\"Admin\", pluralize = false)","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Let's load the plugin into the app manually to avoid restarting the app. Upon restarting the application next time, the plugin will be automatically loaded by Genie:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> include(joinpath(\"plugins\", \"genie_authentication.jl\"))","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"Time to create an admin user for logging in:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"julia> using WatchTonight.Users\r\n\r\njulia> u = User(email = \"admin@admin\", name = \"Admin\", password = Users.hash_password(\"admin\"), username = \"admin\")\r\n\r\njulia> save!(u)","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"We'll also need a route for the admin area:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"using WatchTonight.AdminController\r\n\r\nroute(\"/admin/movies\", AdminController.index, named = :get_home)","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"And finally, the controller code:","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"module AdminController\r\n\r\nusing GenieAuthentication, Genie.Renderer, Genie.Exceptions, Genie.Renderer.Html\r\n\r\nfunction index()\r\n authenticated!()\r\n h1(\"Welcome Admin\") |> html\r\nend\r\n\r\nend","category":"page"},{"location":"tutorials/4-1--Developing_MVC_Web_Apps.html","page":"Developing MVC web applications","title":"Developing MVC web applications","text":"If we navigate to http://127.0.0.1:8000/admin/movies we'll be asked to logged in. Using admin for the user and admin for the password will allow us to access the password protected section.","category":"page"},{"location":"API/commands.html","page":"Commands","title":"Commands","text":"CurrentModule = Commands","category":"page"},{"location":"API/commands.html","page":"Commands","title":"Commands","text":"called_command\nexecute\nparse_commandline_args","category":"page"},{"location":"API/commands.html#Genie.Commands.called_command","page":"Commands","title":"Genie.Commands.called_command","text":"called_command(args::Dict, key::String) :: Bool\n\nChecks whether or not a certain command was invoked by looking at the command line args.\n\n\n\n\n\n","category":"function"},{"location":"API/commands.html#Genie.Commands.execute","page":"Commands","title":"Genie.Commands.execute","text":"execute(config::Settings) :: Nothing\n\nRuns the requested Genie app command, based on the args passed to the script.\n\n\n\n\n\n","category":"function"},{"location":"API/commands.html#Genie.Commands.parse_commandline_args","page":"Commands","title":"Genie.Commands.parse_commandline_args","text":"parse_commandline_args() :: Dict{String,Any}\n\nExtracts the command line args passed into the app and returns them as a Dict, possibly setting up defaults. Also, it is used by the ArgParse module to populate the command line help for the app -h.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html","page":"Assets","title":"Assets","text":"CurrentModule = Assets","category":"page"},{"location":"API/assets.html","page":"Assets","title":"Assets","text":"add_fileroute\nAssetsConfig\nassets_config!\nassets_endpoint\nasset_file\nasset_path\nasset_route\nchannels\nchannels_route\nchannels_script\nchannels_script_tag\nchannels_subscribe\nchannels_support\ncss_asset\nembedded\nembedded_path\nexternal_assets\nfavicon_support\ninclude_asset\njs_asset\njsliteral\njs_settings\nwebthreads\nwebthreads_endpoint\nwebthreads_push_pull\nwebthreads_route\nwebthreads_script\nwebthreads_script_tag\nwebthreads_subscribe\nwebthreads_support","category":"page"},{"location":"API/assets.html#Genie.Assets.add_fileroute","page":"Assets","title":"Genie.Assets.add_fileroute","text":"addfileroute(assetsconfig::Genie.Assets.AssetsConfig, filename::AbstractString; basedir = pwd(), type::Union{Nothing, String} = nothing, content_type::Union{Nothing, Symbol} = nothing, ext::Union{Nothing, String} = nothing, kwargs...)\n\nHelper function to add a file route to the assets based on asset_config and filename.\n\nExample\n\nadd_fileroute(StippleUI.assets_config, \"Sortable.min.js\")\nadd_fileroute(StippleUI.assets_config, \"vuedraggable.umd.min.js\")\nadd_fileroute(StippleUI.assets_config, \"vuedraggable.umd.min.js.map\", type = \"js\")\nadd_fileroute(StippleUI.assets_config, \"QSortableTree.js\")\n\ndraggabletree_deps() = [\n script(src = \"/stippleui.jl/master/assets/js/sortable.min.js\")\n script(src = \"/stippleui.jl/master/assets/js/vuedraggable.umd.min.js\")\n script(src = \"/stippleui.jl/master/assets/js/qsortabletree.js\")\n]\nStipple.DEPS[:qdraggabletree] = draggabletree_deps\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.AssetsConfig","page":"Assets","title":"Genie.Assets.AssetsConfig","text":"mutable struct AssetsConfig\n\nManages the assets configuration for the current package. Define your own instance of AssetsConfig if you want to add support for asset management for your package through Genie.Assets.\n\n\n\n\n\n","category":"type"},{"location":"API/assets.html#Genie.Assets.assets_config!","page":"Assets","title":"Genie.Assets.assets_config!","text":"assets_config!(packages::Vector{Module}; config...) :: Nothing\nassets_config!(package::Module; config...) :: Nothing\n\nUtility function which allows bulk configuration of the assets.\n\nExample\n\nGenie.Assets.assets_config!([Genie, Stipple, StippleUI], host = \"https://cdn.statically.io/gh/GenieFramework\")\n\n\n\n\n\nassets_config!(; config...) :: Nothing\n\nUpdates the assets configuration for the current package.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.asset_file","page":"Assets","title":"Genie.Assets.asset_file","text":"asset_file(; cwd = \"\", file::String, path::String = \"\", type::String = \"\", prefix::String = \"assets\",\n ext::String = \"\", min::Bool = false, skip_ext::Bool = false) :: String\n\nGenerates the file system path to an asset file.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.asset_path","page":"Assets","title":"Genie.Assets.asset_path","text":"asset_path(; file::String, host::String = Genie.config.base_path, package::String = \"\", version::String = \"\",\n prefix::String = \"assets\", type::String = \"\", path::String = \"\", min::Bool = false,\n ext::String = \"\", skip_ext::Bool = false, query::String = \"\") :: String\nasset_path(file::String; kwargs...) :: String\nasset_path(ac::AssetsConfig, tp::Union{Symbol,String}; type::String = string(tp), path::String = \"\",\n file::String = \"\", ext::String = \"\", skip_ext::Bool = false, query::String = \"\") :: String\n\nGenerates the path to an asset file.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.asset_route","page":"Assets","title":"Genie.Assets.asset_route","text":"asset_route(; file::String, package::String = \"\", version::String = \"\", prefix::String = \"assets\",\n type::String = \"\", path::String = \"\", min::Bool = false,\n ext::String = \"\", skip_ext::Bool = false, query::String = \"\") :: String\nasset_route(file::String; kwargs...) :: String\nasset_route(ac::AssetsConfig, tp::Union{Symbol,String}; type::String = string(tp), path::String = \"\",\n file::String = \"\", ext::String = \"\", skip_ext::Bool = false, query::String = \"\") :: String\n\nGenerates the route to an asset file.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.channels","page":"Assets","title":"Genie.Assets.channels","text":"channels(channel::AbstractString = Genie.config.webchannels_default_route) :: String\n\nOutputs the channels.js file included with the Genie package.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.channels_script","page":"Assets","title":"Genie.Assets.channels_script","text":"channels_script(channel::AbstractString = Genie.config.webchannels_default_route) :: String\n\nOutputs the channels JavaScript content within tags, for embedding into the page.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.channels_subscribe","page":"Assets","title":"Genie.Assets.channels_subscribe","text":"channels_subscribe(channel::AbstractString = Genie.config.webchannels_default_route) :: Nothing\n\nRegisters subscription and unsubscription channels for channel.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.channels_support","page":"Assets","title":"Genie.Assets.channels_support","text":"channels_support(channel = Genie.config.webchannels_default_route) :: String\n\nProvides full web channels support, setting up routes for loading support JS files, web sockets subscription and returning the tags, for embedding into the page.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.webthreads_subscribe","page":"Assets","title":"Genie.Assets.webthreads_subscribe","text":"function webthreads_subscribe(channel) :: Nothing\n\nRegisters subscription and unsubscription routes for channel.\n\n\n\n\n\n","category":"function"},{"location":"API/assets.html#Genie.Assets.webthreads_support","page":"Assets","title":"Genie.Assets.webthreads_support","text":"webthreads_support(channel = Genie.config.webthreads_default_route) :: String\n\nProvides full web channels support, setting up routes for loading support JS files, web sockets subscription and returning the \n \"\"\"\nend","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"Now if you reload the page and broadcast the message, it will be picked up by our custom payload handler.","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"We can remove clients that are no longer reachable (for instance, if the browser tab is closed) with:","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"julia> Genie.WebChannels.unsubscribe_disconnected_clients()","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"The output of unsubscribe_disconnected_clients() is the collection of remaining (connected) clients.","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"Heads up!","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"You should routinely unsubscribe_disconnected_clients() to free memory.","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"At any time, we can check the connected clients with Genie.WebChannels.connected_clients() and the disconnected ones with Genie.WebChannels.disconnected_clients().","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html#Pushing-messages-from-the-client","page":"Working with WebSockets","title":"Pushing messages from the client","text":"","category":"section"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"We can also push messages from client to server. As we don't have a UI, we'll use the browser's console and Genie's JavaScript API to send the messages. But first, we need to set up the channel which will receive our message. Run this in the active Julia REPL:","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"channel(\"/____/echo\") do\n @info \"Received: $(params(:payload))\"\nend","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"Now that our endpoint is up, go to the browser's console and run:","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"Genie.WebChannels.sendMessageTo('____', 'echo', 'Hello!')","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"The julia terminal and console will both immediately display the response from the server:","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"Received: Hello!\nGot this payload: Received: Hello!","category":"page"},{"location":"tutorials/17--Working_with_Web_Sockets.html#Summary","page":"Working with WebSockets","title":"Summary","text":"","category":"section"},{"location":"tutorials/17--Working_with_Web_Sockets.html","page":"Working with WebSockets","title":"Working with WebSockets","text":"This concludes our intro to working with WebSockets in Genie. You now have the knowledge to set up the communication between client and server, send messages from both server and clients, and perform various tasks using the WebChannels API.","category":"page"},{"location":"API/sessions.html","page":"-","title":"-","text":"CurrentModule = Sessions","category":"page"},{"location":"API/sessions.html","page":"-","title":"-","text":"Session\nid\nstart\nset!\nget\nunset!\nisset\npersist\nload\nsession\ninit","category":"page"},{"location":"API/sessions.html#Base.get","page":"-","title":"Base.get","text":"get(collection, key, default)\n\nReturn the value stored for the given key, or the given default value if no mapping for the key is present.\n\ncompat: Julia 1.7\nFor tuples and numbers, this function requires at least Julia 1.7.\n\nExamples\n\njulia> d = Dict(\"a\"=>1, \"b\"=>2);\n\njulia> get(d, \"a\", 3)\n1\n\njulia> get(d, \"c\", 3)\n3\n\n\n\n\n\nget(f::Function, collection, key)\n\nReturn the value stored for the given key, or if no mapping for the key is present, return f(). Use get! to also store the default value in the dictionary.\n\nThis is intended to be called using do block syntax\n\nget(dict, key) do\n # default value calculated here\n time()\nend\n\n\n\n\n\nget(x::Nullable[, y])\n\nAttempt to access the value of x. Returns the value if it is present; otherwise, returns y if provided, or throws a NullException if not.\n\nExamples\n\njulia> get(Nullable(5))\n5\n\njulia> get(Nullable())\nERROR: NullException()\nStacktrace:\n [1] get(::Nullable{Union{}}) at ./nullable.jl:102\n\n\n\n\n\nget(sd,k,v)\n\nReturns the value associated with key k where sd is a SortedDict, or else returns v if k is not in sd. Time: O(c log n)\n\n\n\n\n\nget(collection, key, default)\n\nReturn the value stored for the given key, or the given default value if no mapping for the key is present.\n\nExamples\n\njulia> d = RobinDict(\"a\"=>1, \"b\"=>2);\n\njulia> get(d, \"a\", 3)\n1\n\njulia> get(d, \"c\", 3)\n3\n\n\n\n\n\nget(f::Function, collection, key)\n\nReturn the value stored for the given key, or if no mapping for the key is present, return f(). Use get! to also store the default value in the dictionary.\n\nThis is intended to be called using do block syntax\n\nget(dict, key) do\n # default value calculated here\n time()\nend\n\n\n\n\n\nget(collection, key, default)\n\nReturn the value stored for the given key, or the given default value if no mapping for the key is present.\n\nExamples\n\njulia> d = OrderedRobinDict(\"a\"=>1, \"b\"=>2);\n\njulia> get(d, \"a\", 3)\n1\n\njulia> get(d, \"c\", 3)\n3\n\n\n\n\n\nget(f::Function, collection, key)\n\nReturn the value stored for the given key, or if no mapping for the key is present, return f(). Use get! to also store the default value in the dictionary.\n\nThis is intended to be called using do block syntax\n\nget(dict, key) do\n # default value calculated here\n time()\nend\n\n\n\n\n\nget(collection, key, default)\n\nReturn the value stored for the given key, or the given default value if no mapping for the key is present.\n\nExamples\n\njulia> d = SwissDict(\"a\"=>1, \"b\"=>2);\n\njulia> get(d, \"a\", 3)\n1\n\njulia> get(d, \"c\", 3)\n3\n\n\n\n\n\nget(f::Function, collection, key)\n\nReturn the value stored for the given key, or if no mapping for the key is present, return f(). Use get! to also store the default value in the dictionary.\n\nThis is intended to be called using do block syntax\n\nget(dict, key) do\n # default value calculated here\n time()\nend\n\n\n\n\n\n","category":"function"}] } diff --git a/dev/tutorials/1--Overview.html b/dev/tutorials/1--Overview.html index 6f637115b..b04b9aa4f 100644 --- a/dev/tutorials/1--Overview.html +++ b/dev/tutorials/1--Overview.html @@ -1,2 +1,2 @@ -Welcome to Genie · Genie - The Highly Productive Julia Web Framework

      Welcome to Genie

      The Highly Productive Web Framework for Julia

      Genie is a full stack web framework for the Julia programming language. Genie's goals are: excellent developer productivity, great run-time performance, and best practices and security by default.

      The Genie web framework follows in the footsteps of mainstream full stack web frameworks like Ruby on Rails and Django, while staying 100% true to its Julia roots. Genie's architecture and development is inspired by the best features present in other frameworks, but not by their design. Genie takes a no-magic no-nonsense approach by doing things the Julia way: Controllers are plain Julia modules, Models leverage types and multiple dispatch, Genie apps are nothing but Julia projects, versioning and dependency management is provided by Julia's own Pkg, and code loading and reloading is automatically set up with Revise.

      Genie also takes inspiration from Julia's "start simple, grow as needed" philosophy, by allowing developers to bootstrap an app in the REPL or in a notebook, or easily create web services and APIs with just a few lines of code.

      As the projects grow more complex, Genie allows adding progressively more structure, by exposing a micro-framework which offers features like powerful routing, flexible logging, support for environments, view templates, etc.

      If database persistence is needed, support for Genie's ORM, SearchLight, can be added at any time. Finally, the full MVC structure can be used in order to develop and maintain more complex, end-to-end, web applications.

      +Welcome to Genie · Genie - The Highly Productive Julia Web Framework

      Welcome to Genie

      The Highly Productive Web Framework for Julia

      Genie is a full stack web framework for the Julia programming language. Genie's goals are: excellent developer productivity, great run-time performance, and best practices and security by default.

      The Genie web framework follows in the footsteps of mainstream full stack web frameworks like Ruby on Rails and Django, while staying 100% true to its Julia roots. Genie's architecture and development is inspired by the best features present in other frameworks, but not by their design. Genie takes a no-magic no-nonsense approach by doing things the Julia way: Controllers are plain Julia modules, Models leverage types and multiple dispatch, Genie apps are nothing but Julia projects, versioning and dependency management is provided by Julia's own Pkg, and code loading and reloading is automatically set up with Revise.

      Genie also takes inspiration from Julia's "start simple, grow as needed" philosophy, by allowing developers to bootstrap an app in the REPL or in a notebook, or easily create web services and APIs with just a few lines of code.

      As the projects grow more complex, Genie allows adding progressively more structure, by exposing a micro-framework which offers features like powerful routing, flexible logging, support for environments, view templates, etc.

      If database persistence is needed, support for Genie's ORM, SearchLight, can be added at any time. Finally, the full MVC structure can be used in order to develop and maintain more complex, end-to-end, web applications.

      diff --git a/dev/tutorials/10--Loading_Genie_Apps.html b/dev/tutorials/10--Loading_Genie_Apps.html index b8f6e6b73..21b3ba486 100644 --- a/dev/tutorials/10--Loading_Genie_Apps.html +++ b/dev/tutorials/10--Loading_Genie_Apps.html @@ -8,4 +8,4 @@ pkg> activate .

      Then, back to the julian prompt, run the following to load the Genie app:

      julia> using Genie
       
      -julia> Genie.loadapp()

      The app's environment will now be loaded.

      In order to start the web server execute

      julia> up()
      +julia> Genie.loadapp()

      The app's environment will now be loaded.

      In order to start the web server execute

      julia> up()
      diff --git a/dev/tutorials/11--Managing_External_Packages.html b/dev/tutorials/11--Managing_External_Packages.html index 9182c620f..df81987d1 100644 --- a/dev/tutorials/11--Managing_External_Packages.html +++ b/dev/tutorials/11--Managing_External_Packages.html @@ -1,2 +1,2 @@ -Managing Genie app's dependencies · Genie - The Highly Productive Julia Web Framework

      Managing external packages for your Genie app

      Genie fully takes advantage of Julia's excellent package manager, Pkg – while allowing Genie developers to use any third party package available in Julia's ecosystem. This is achieved by taking a common sense approach: Genie apps are just plain Julia projects.

      In order to add extra packages to your Genie app, thus, we need to use Julia's Pkg features:

      1. start a Genie REPL with your app: $ bin/repl. This will automatically load the package environment of the app.
      2. switch to Pkg mode: julia> ]
      3. add the package you want, for example OhMyREPL: (MyGenieApp) pkg> add OhMyREPL

      That's all! Now you can use the packages at the Genie REPL or anywhere in your app via using or import.

      Use the same approach to update the packages in your app, via: pkg> up and apply all available updates, or pkg> up OhMyREPL to update a single package.

      +Managing Genie app's dependencies · Genie - The Highly Productive Julia Web Framework

      Managing external packages for your Genie app

      Genie fully takes advantage of Julia's excellent package manager, Pkg – while allowing Genie developers to use any third party package available in Julia's ecosystem. This is achieved by taking a common sense approach: Genie apps are just plain Julia projects.

      In order to add extra packages to your Genie app, thus, we need to use Julia's Pkg features:

      1. start a Genie REPL with your app: $ bin/repl. This will automatically load the package environment of the app.
      2. switch to Pkg mode: julia> ]
      3. add the package you want, for example OhMyREPL: (MyGenieApp) pkg> add OhMyREPL

      That's all! Now you can use the packages at the Genie REPL or anywhere in your app via using or import.

      Use the same approach to update the packages in your app, via: pkg> up and apply all available updates, or pkg> up OhMyREPL to update a single package.

      diff --git a/dev/tutorials/12--Advanced_Routing_Techniques.html b/dev/tutorials/12--Advanced_Routing_Techniques.html index df9e7bdb6..54a58dfdd 100644 --- a/dev/tutorials/12--Advanced_Routing_Techniques.html +++ b/dev/tutorials/12--Advanced_Routing_Techniques.html @@ -58,4 +58,4 @@ MethodError(convert, (Int64, "10"), 0x00000000000063fe) /customers/10/orders/20 404

      As you can see, Genie attempts to convert the types from the default SubString{String} to Int – but doesn't know how. It fails, can't find other matching routes and returns a 404 Not Found response.

      Type conversion in routes

      The error is easy to address though: we need to provide a type converter from SubString{String} to Int.

      Base.convert(::Type{Int}, v::SubString{String}) = parse(Int, v)

      Once we register the converter our request will be correctly handled, resulting in Order ID has type Int64 // Customer ID has type Int64

      Matching individual URI segments

      Besides matching the full route, Genie also allows matching individual URI segments. That is, enforcing that the various route parameters obey a certain pattern. In order to introduce constraints for route parameters we append #pattern at the end of the route parameter.

      Example

      For instance, let's assume that we want to implement a localized website where we have a URL structure like: mywebsite.com/en, mywebsite.com/es and mywebsite.com/de. We can define a dynamic route and extract the locale variable to serve localized content:

      route(":locale", TranslationsController.index)

      This will work very well, matching requests and passing the locale into our code within the payload(:locale) variable. However, it will also be too greedy, virtually matching all the requests, including things like static files (ie mywebsite.com/favicon.ico). We can constrain what the :locale variable can match, by appending the pattern (a regex pattern):

      route(":locale#(en|es|de)", TranslationsController.index)

      The refactored route only allows :locale to match one of en, es, and de strings.


      HEADS UP

      Keep in mind not to duplicate application logic. For instance, if you have an array of supported locales, you can use that to dynamically generate the pattern – routes can be fully dynamically generated!

      const LOCALE = ":locale#($(join(TranslationsController.AVAILABLE_LOCALES, '|')))"
       
      -route("/$LOCALE", TranslationsController.index, named = :get_index)

      The params collection

      It's good to know that the router bundles all the parameters of the current request into the params collection (a Dict{Symbol,Any}). This contains valuable information, such as route parameters, query params, POST payload, the original HTTP.Request and HTTP.Response objects, etcetera. In general it's recommended not to access the params collection directly but through the utility methods defined by Genie.Requests and Genie.Responses – but knowing about params might come in handy for advanced users.

      +route("/$LOCALE", TranslationsController.index, named = :get_index)

      The params collection

      It's good to know that the router bundles all the parameters of the current request into the params collection (a Dict{Symbol,Any}). This contains valuable information, such as route parameters, query params, POST payload, the original HTTP.Request and HTTP.Response objects, etcetera. In general it's recommended not to access the params collection directly but through the utility methods defined by Genie.Requests and Genie.Responses – but knowing about params might come in handy for advanced users.

      diff --git a/dev/tutorials/13--Initializers.html b/dev/tutorials/13--Initializers.html index a041dd5dd..33270f0b2 100644 --- a/dev/tutorials/13--Initializers.html +++ b/dev/tutorials/13--Initializers.html @@ -1,2 +1,2 @@ -Auto-loading configuration code with initializers · Genie - The Highly Productive Julia Web Framework

      Customized application configuration with initializers

      Initializers are plain Julia files which are loaded early in the application life-cycle (before routes, controller, or models). They are designed to implement configuration code which is used by other parts of the application (like database connections, logging settings, etc).

      Initializers should be placed within the config/initializers/ folder and they will be automatically loaded by Genie into the app.

      If your configuration is environment dependent (like a database connection which is different between dev and prod environments), it should be added to the corresponding config/env/*.jl file.

      Best practices

      • You can name the initializers as you wish (ideally a descriptive name, like redis.jl for connecting to a Redis DB).
      • Don't use uppercase names unless you define a module (in order to respect Julia's naming practices).
      • Keep your initializer files small and focused, so they serve only one purpose.
      • You can add as many initializers as you need.
      • Do not abuse them, they are not meant to host complex code - app logic should be in models and controllers.

      Load order

      The initializers are loaded in the order they are read from the file system. If you have initializers which depend on other initializers, this is most likely a sign that you need to refactor using a model or a library file.


      HEADS UP

      Library files are Julia files which provide distinct functionality and can be placed in the lib/ folder where they are also automatically loaded by Genie. If the lib/ folder does not exist, you can create it yourself.


      Scope

      All the definitions (variables, constants, functions, modules, etc) added to initializer files are loaded into your app's module. So if your app is called MyGenieApp, the definitions will be available under the MyGenieApp module.


      HEADS UP

      Given that your app's name is variable, you can also access your app's module through the Main.UserApp constant. So all the definitions added to initializers can also be accessed through the Main.UserApp module.


      +Auto-loading configuration code with initializers · Genie - The Highly Productive Julia Web Framework

      Customized application configuration with initializers

      Initializers are plain Julia files which are loaded early in the application life-cycle (before routes, controller, or models). They are designed to implement configuration code which is used by other parts of the application (like database connections, logging settings, etc).

      Initializers should be placed within the config/initializers/ folder and they will be automatically loaded by Genie into the app.

      If your configuration is environment dependent (like a database connection which is different between dev and prod environments), it should be added to the corresponding config/env/*.jl file.

      Best practices

      • You can name the initializers as you wish (ideally a descriptive name, like redis.jl for connecting to a Redis DB).
      • Don't use uppercase names unless you define a module (in order to respect Julia's naming practices).
      • Keep your initializer files small and focused, so they serve only one purpose.
      • You can add as many initializers as you need.
      • Do not abuse them, they are not meant to host complex code - app logic should be in models and controllers.

      Load order

      The initializers are loaded in the order they are read from the file system. If you have initializers which depend on other initializers, this is most likely a sign that you need to refactor using a model or a library file.


      HEADS UP

      Library files are Julia files which provide distinct functionality and can be placed in the lib/ folder where they are also automatically loaded by Genie. If the lib/ folder does not exist, you can create it yourself.


      Scope

      All the definitions (variables, constants, functions, modules, etc) added to initializer files are loaded into your app's module. So if your app is called MyGenieApp, the definitions will be available under the MyGenieApp module.


      HEADS UP

      Given that your app's name is variable, you can also access your app's module through the Main.UserApp constant. So all the definitions added to initializers can also be accessed through the Main.UserApp module.


      diff --git a/dev/tutorials/14--The_Secrets_File.html b/dev/tutorials/14--The_Secrets_File.html index 8e628df33..11ff8b452 100644 --- a/dev/tutorials/14--The_Secrets_File.html +++ b/dev/tutorials/14--The_Secrets_File.html @@ -1,2 +1,2 @@ -The secrets file · Genie - The Highly Productive Julia Web Framework

      The secrets (config/secrets.jl) file

      Confidential configuration data (like API keys, usernames, passwords, etc) should be added to the config/secrets.jl file. This file is by default added to .gitignore when creating a Genie app, so it won't be added to source control – to avoid that it is accidentally exposed.

      Scope

      All the definitions (variables, constants, functions, modules, etc) added to the secrets.jl file are loaded into your app's module. So if your app (and its main module) is called MyGenieApp, the definitions will be available under the MyGenieApp namespace.


      HEADS UP

      Given the your app's name is variable, you can also access your app's module through the Main.UserApp constant. So all the definitions added to secrets.jl can also be accessed through the Mani.UserApp module.


      +The secrets file · Genie - The Highly Productive Julia Web Framework

      The secrets (config/secrets.jl) file

      Confidential configuration data (like API keys, usernames, passwords, etc) should be added to the config/secrets.jl file. This file is by default added to .gitignore when creating a Genie app, so it won't be added to source control – to avoid that it is accidentally exposed.

      Scope

      All the definitions (variables, constants, functions, modules, etc) added to the secrets.jl file are loaded into your app's module. So if your app (and its main module) is called MyGenieApp, the definitions will be available under the MyGenieApp namespace.


      HEADS UP

      Given the your app's name is variable, you can also access your app's module through the Main.UserApp constant. So all the definitions added to secrets.jl can also be accessed through the Mani.UserApp module.


      diff --git a/dev/tutorials/15--The_Lib_Folder.html b/dev/tutorials/15--The_Lib_Folder.html index 000fdc2fc..7531ff4e3 100644 --- a/dev/tutorials/15--The_Lib_Folder.html +++ b/dev/tutorials/15--The_Lib_Folder.html @@ -1,2 +1,2 @@ -Auto-loading user libraries · Genie - The Highly Productive Julia Web Framework

      The lib/ folder

      Genie makes it easy to automatically load Julia code (modules, files, etc) into an app, outside of the standard Genie MVC app structure. You simply need to add your files and folders into the lib/ folder.


      HEADS UP

      • If the lib/ folder does not exist, just create it yourself: julia> mkdir("lib")
      • Genie includes the files placed within the lib/ folder and subfolders recursively
      • Files within lib/ are loaded using Revise and are automatically reloaded if changed.

      +Auto-loading user libraries · Genie - The Highly Productive Julia Web Framework

      The lib/ folder

      Genie makes it easy to automatically load Julia code (modules, files, etc) into an app, outside of the standard Genie MVC app structure. You simply need to add your files and folders into the lib/ folder.


      HEADS UP

      • If the lib/ folder does not exist, just create it yourself: julia> mkdir("lib")
      • Genie includes the files placed within the lib/ folder and subfolders recursively
      • Files within lib/ are loaded using Revise and are automatically reloaded if changed.

      diff --git a/dev/tutorials/16--Using_Genie_With_Docker.html b/dev/tutorials/16--Using_Genie_With_Docker.html index c0742f374..d848c0ed0 100644 --- a/dev/tutorials/16--Using_Genie_With_Docker.html +++ b/dev/tutorials/16--Using_Genie_With_Docker.html @@ -33,4 +33,4 @@ PACKAGES, sysimage_path = "compiled/sysimg.so", cpu_target = PackageCompiler.default_app_cpu_target() -)

      Using the precompiled image

      The result of these changes is that PackageCompiler will create a new Julia sysimage that will be stored inside the compiled/sysimg.so file. The last step is to instruct our bin/server script to use the image.

      Edit the bin/server file and make it look like this:

      julia --color=yes --depwarn=no --project=@. --sysimage=compiled/sysimg.so -q -i -- $(dirname $0)/../bootstrap.jl -s=true "$@"

      With this change we're passing the additional --sysimage flag, indicating our new Julia sys image.

      +)

      Using the precompiled image

      The result of these changes is that PackageCompiler will create a new Julia sysimage that will be stored inside the compiled/sysimg.so file. The last step is to instruct our bin/server script to use the image.

      Edit the bin/server file and make it look like this:

      julia --color=yes --depwarn=no --project=@. --sysimage=compiled/sysimg.so -q -i -- $(dirname $0)/../bootstrap.jl -s=true "$@"

      With this change we're passing the additional --sysimage flag, indicating our new Julia sys image.

      diff --git a/dev/tutorials/17--Working_with_Web_Sockets.html b/dev/tutorials/17--Working_with_Web_Sockets.html index 01e698425..a7f45ce00 100644 --- a/dev/tutorials/17--Working_with_Web_Sockets.html +++ b/dev/tutorials/17--Working_with_Web_Sockets.html @@ -32,4 +32,4 @@ end

      Now if you reload the page and broadcast the message, it will be picked up by our custom payload handler.

      We can remove clients that are no longer reachable (for instance, if the browser tab is closed) with:

      julia> Genie.WebChannels.unsubscribe_disconnected_clients()

      The output of unsubscribe_disconnected_clients() is the collection of remaining (connected) clients.


      Heads up!

      You should routinely unsubscribe_disconnected_clients() to free memory.


      At any time, we can check the connected clients with Genie.WebChannels.connected_clients() and the disconnected ones with Genie.WebChannels.disconnected_clients().

      Pushing messages from the client

      We can also push messages from client to server. As we don't have a UI, we'll use the browser's console and Genie's JavaScript API to send the messages. But first, we need to set up the channel which will receive our message. Run this in the active Julia REPL:

      channel("/____/echo") do
         @info "Received: $(params(:payload))"
       end

      Now that our endpoint is up, go to the browser's console and run:

      Genie.WebChannels.sendMessageTo('____', 'echo', 'Hello!')

      The julia terminal and console will both immediately display the response from the server:

      Received: Hello!
      -Got this payload: Received: Hello!

      Summary

      This concludes our intro to working with WebSockets in Genie. You now have the knowledge to set up the communication between client and server, send messages from both server and clients, and perform various tasks using the WebChannels API.

      +Got this payload: Received: Hello!

      Summary

      This concludes our intro to working with WebSockets in Genie. You now have the knowledge to set up the communication between client and server, send messages from both server and clients, and perform various tasks using the WebChannels API.

      diff --git a/dev/tutorials/2--Installing_Genie.html b/dev/tutorials/2--Installing_Genie.html index 13860906d..87fb3c20a 100644 --- a/dev/tutorials/2--Installing_Genie.html +++ b/dev/tutorials/2--Installing_Genie.html @@ -1,2 +1,2 @@ -Installing Genie · Genie - The Highly Productive Julia Web Framework

      How to Install Genie

      Install Genie from Julia's registry – for example the latest version (currently version 5):

      pkg> add Genie

      Genie, just like Julia, uses semantic versioning in the form vX.Y.Z to designate:

      • X : major version, introducing breaking changes
      • Y : minor version, brings new features, no breaking changes
      • Z : patch version, fixes bugs, no new features or breaking changes
      +Installing Genie · Genie - The Highly Productive Julia Web Framework

      How to Install Genie

      Install Genie from Julia's registry – for example the latest version (currently version 5):

      pkg> add Genie

      Genie, just like Julia, uses semantic versioning in the form vX.Y.Z to designate:

      • X : major version, introducing breaking changes
      • Y : minor version, brings new features, no breaking changes
      • Z : patch version, fixes bugs, no new features or breaking changes
      diff --git a/dev/tutorials/3--Getting_Started.html b/dev/tutorials/3--Getting_Started.html index 00caeb7fe..007378f84 100644 --- a/dev/tutorials/3--Getting_Started.html +++ b/dev/tutorials/3--Getting_Started.html @@ -19,4 +19,4 @@ respond("Hello World", :text) end -up(8001, async = false)

      We begun by defining 2 routes and we used the html and json rendering functions (available in the Renderer.Html and the Renderer.Json modules). These functions are responsible for outputting the data using the correct format and document type (with the correct MIME), in our case HTML data for hello.html, and JSON data for hello.json.

      The third route serves text responses. As Genie does not provide a specialized text() method for sending text/plain responses, we use the generic respond function, indicating the desired MIME type. In our case :text, corresponding to text/plain. Other available MIME types shortcuts are :xml, :markdown, :javascript and a few others others – and users can register their own mime types and response types as needed or can pass the full mime type as a string, ie "text/csv".

      The up function will launch the web server on port 8001. This time, very important, we instructed it to start the server synchronously (that is, blocking the execution of the script), by passing the async = false argument. This way we make sure that our script stays running. Otherwise, at the end of the script, the Julia process would normally exit, killing our server.

      In order to launch the script, run $ julia geniews.jl.

      Batteries included

      Genie readily makes available a rich set of features - you have already seen the rendering and the routing modules in action. But for instance, logging (to file and console) can also be easily triggered with one line of code, powerful caching can be enabled with a couple more lines, and so on.

      The app already handles "404 Page Not Found" and "500 Internal Error" responses. If you try to access a URL which is not handled by the app, like say http://127.0.0.1:8001/not_here, you'll see Genie's default 404 page. The default error pages can be overwritten with custom ones.

      +up(8001, async = false)

      We begun by defining 2 routes and we used the html and json rendering functions (available in the Renderer.Html and the Renderer.Json modules). These functions are responsible for outputting the data using the correct format and document type (with the correct MIME), in our case HTML data for hello.html, and JSON data for hello.json.

      The third route serves text responses. As Genie does not provide a specialized text() method for sending text/plain responses, we use the generic respond function, indicating the desired MIME type. In our case :text, corresponding to text/plain. Other available MIME types shortcuts are :xml, :markdown, :javascript and a few others others – and users can register their own mime types and response types as needed or can pass the full mime type as a string, ie "text/csv".

      The up function will launch the web server on port 8001. This time, very important, we instructed it to start the server synchronously (that is, blocking the execution of the script), by passing the async = false argument. This way we make sure that our script stays running. Otherwise, at the end of the script, the Julia process would normally exit, killing our server.

      In order to launch the script, run $ julia geniews.jl.

      Batteries included

      Genie readily makes available a rich set of features - you have already seen the rendering and the routing modules in action. But for instance, logging (to file and console) can also be easily triggered with one line of code, powerful caching can be enabled with a couple more lines, and so on.

      The app already handles "404 Page Not Found" and "500 Internal Error" responses. If you try to access a URL which is not handled by the app, like say http://127.0.0.1:8001/not_here, you'll see Genie's default 404 page. The default error pages can be overwritten with custom ones.

      diff --git a/dev/tutorials/4--Developing_Web_Services.html b/dev/tutorials/4--Developing_Web_Services.html index 15b92bccc..b4f2a8f68 100644 --- a/dev/tutorials/4--Developing_Web_Services.html +++ b/dev/tutorials/4--Developing_Web_Services.html @@ -13,4 +13,4 @@ ├── src └── test

      These are the roles of each of the files and folders:

      • Manifest.toml and Project.toml are used by Julia and Pkg to manage the app's dependencies.
      • bin/ includes scripts for starting up a Genie REPL or a Genie server.
      • bootstrap.jl and the files within src/ are used by Genie to load the application and should not be modified unless you know what you're doing.
      • config/ includes the per-environment configuration files.
      • public/ is the document root, which includes static files exposed by the app on the network/internet.
      • routes.jl is the dedicated file for registering Genie routes.
      • the test/ folder is set up to store the unit and integration tests for the app.
      • .gitattributes and .gitignore are used by Git to manage the project's files.

      HEADS UP

      After creating a new app you might need to change the file permissions to allow editing/saving the files such as routes.jl.


      Adding logic

      You can now edit the routes.jl file to add some logic, at the bottom of the file:

      route("/hello") do
         "Welcome to Genie!"
      -end

      If you now visit http://127.0.0.1:8000/hello you'll see a warm greeting.

      Extending the app

      Genie apps are just plain Julia projects. This means that routes.jl will behave like any other Julia script - you can reference extra packages, you can switch into pkg> mode to manage per project dependencies, include other files, etcetera.

      If you have existing Julia code that you want to quickly load into a Genie app, you can add a lib/ folder in the root of the app and place your Julia files there. If the folder exists, lib/ and all its subfolders are automatically loaded by Genie, recursively.


      WARNING

      If you add the lib/ folder while the Genie app is running, you will need to restart the app to load the files.


      If you need to add database support, you can always add the SearchLight ORM by running julia> Genie.Generator.db_support() in the app's REPL.

      However, if your app grows in complexity and you develop it from scratch, it is more efficient to take advantage of Genie's resource-oriented MVC structure.

      +end

      If you now visit http://127.0.0.1:8000/hello you'll see a warm greeting.

      Extending the app

      Genie apps are just plain Julia projects. This means that routes.jl will behave like any other Julia script - you can reference extra packages, you can switch into pkg> mode to manage per project dependencies, include other files, etcetera.

      If you have existing Julia code that you want to quickly load into a Genie app, you can add a lib/ folder in the root of the app and place your Julia files there. If the folder exists, lib/ and all its subfolders are automatically loaded by Genie, recursively.


      WARNING

      If you add the lib/ folder while the Genie app is running, you will need to restart the app to load the files.


      If you need to add database support, you can always add the SearchLight ORM by running julia> Genie.Generator.db_support() in the app's REPL.

      However, if your app grows in complexity and you develop it from scratch, it is more efficient to take advantage of Genie's resource-oriented MVC structure.

      diff --git a/dev/tutorials/4-1--Developing_MVC_Web_Apps.html b/dev/tutorials/4-1--Developing_MVC_Web_Apps.html index 616a53478..4b9245aa0 100644 --- a/dev/tutorials/4-1--Developing_MVC_Web_Apps.html +++ b/dev/tutorials/4-1--Developing_MVC_Web_Apps.html @@ -203,4 +203,4 @@ h1("Welcome Admin") |> html end -end

      If we navigate to http://127.0.0.1:8000/admin/movies we'll be asked to logged in. Using admin for the user and admin for the password will allow us to access the password protected section.

      +end

      If we navigate to http://127.0.0.1:8000/admin/movies we'll be asked to logged in. Using admin for the user and admin for the password will allow us to access the password protected section.

      diff --git a/dev/tutorials/5--Handling_Query_Params.html b/dev/tutorials/5--Handling_Query_Params.html index 218cf627d..e3d871111 100644 --- a/dev/tutorials/5--Handling_Query_Params.html +++ b/dev/tutorials/5--Handling_Query_Params.html @@ -9,4 +9,4 @@ route("/hi") do "Hello $(getpayload(:name, "Anon"))" -end

      The getpayload function has a few specializations, and one of them accepts the key and a default value. The default value is returned if the key variable is not defined. You can see the various implementations for getpayload using the API docs or Julia's help> mode.

      +end

      The getpayload function has a few specializations, and one of them accepts the key and a default value. The default value is returned if the key variable is not defined. You can see the various implementations for getpayload using the API docs or Julia's help> mode.

      diff --git a/dev/tutorials/6--Working_with_POST_Payloads.html b/dev/tutorials/6--Working_with_POST_Payloads.html index d63757b3c..062e42068 100644 --- a/dev/tutorials/6--Working_with_POST_Payloads.html +++ b/dev/tutorials/6--Working_with_POST_Payloads.html @@ -16,4 +16,4 @@ "Hello $(postpayload(:name, "Anon"))" end -up()

      The postpayload function has a few specializations, and one of them accepts the key and the default value. The default value is returned if the key variable is not defined. You can see the various implementations for postpayload using the API docs or Julia's help> mode.

      +up()

      The postpayload function has a few specializations, and one of them accepts the key and the default value. The default value is returned if the key variable is not defined. You can see the various implementations for postpayload using the API docs or Julia's help> mode.

      diff --git a/dev/tutorials/7--Using_JSON_Payloads.html b/dev/tutorials/7--Using_JSON_Payloads.html index f2bab6550..c5c7f9f80 100644 --- a/dev/tutorials/7--Using_JSON_Payloads.html +++ b/dev/tutorials/7--Using_JSON_Payloads.html @@ -21,4 +21,4 @@ Content-Type: application/json Transfer-Encoding: chunked -"Hello Adrian""""

      First, for the two @show calls, notice how jsonpayload had successfully converted the POST data to a Dict. While the rawpayload returns the POST data as a String, exactly as received. Finally, our route handler returns a JSON response, greeting the user by extracting the name from within the jsonpayload Dict.

      +"Hello Adrian""""

      First, for the two @show calls, notice how jsonpayload had successfully converted the POST data to a Dict. While the rawpayload returns the POST data as a String, exactly as received. Finally, our route handler returns a JSON response, greeting the user by extracting the name from within the jsonpayload Dict.

      diff --git a/dev/tutorials/8--Handling_File_Uploads.html b/dev/tutorials/8--Handling_File_Uploads.html index 592281557..b6809b2a8 100644 --- a/dev/tutorials/8--Handling_File_Uploads.html +++ b/dev/tutorials/8--Handling_File_Uploads.html @@ -22,4 +22,4 @@ end end -up()

      Upon uploading a file and submitting the form, our app will display the file's stats.

      +up()

      Upon uploading a file and submitting the form, our app will display the file's stats.

      diff --git a/dev/tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html b/dev/tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html index 1a4a3091f..66bb452a0 100644 --- a/dev/tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html +++ b/dev/tutorials/9--Publishing_Your_Julia_Code_Online_With_Genie_Apps.html @@ -14,4 +14,4 @@ route("/friday") do MyLib.isitfriday() ? "Yes, it's Friday!" : "No, not yet :(" -end

      Use the lib/ folder to host your Julia code so that Genie knows where to look in order to load it and make it available throughout the application.

      +end

      Use the lib/ folder to host your Julia code so that Genie knows where to look in order to load it and make it available throughout the application.

      diff --git a/dev/tutorials/90--Deploying_With_Heroku_Buildpacks.html b/dev/tutorials/90--Deploying_With_Heroku_Buildpacks.html index 86a8ada48..cae569c69 100644 --- a/dev/tutorials/90--Deploying_With_Heroku_Buildpacks.html +++ b/dev/tutorials/90--Deploying_With_Heroku_Buildpacks.html @@ -6,4 +6,4 @@ origin https://github.com/milesfrain/GenieOnHeroku.git (fetch) origin https://github.com/milesfrain/GenieOnHeroku.git (push)

      We are using a buildpack for Julia. This runs many of the common deployment operations required for Julia projects. It relies on the directory layout found in the example project, with Project.toml, Manifest.toml in the root, and all Julia code in the src directory.

      Deploy your app

      git push heroku master

      This pushes your current branch of your local repo to the heroku remote repo's master branch.

      Heroku will automatically execute the commands described in the Julia buildpack and Procfile of this latest push.

      You must push to the heroku master branch to trigger an automated deploy.

      Open your app's webpage

      heroku open -a $HEROKU_APP_NAME

      This is a convenience command to open your app's webpage in your browser.

      The webpage is: https://$HEROKU_APP_NAME.herokuapp.com/

      For example: https://my-app-name.herokuapp.com/

      View app logs

      heroku logs -tail -a $HEROKU_APP_NAME

      This is another convenience command to launch a log viewer that remains open to show the latest status of your app.

      The println statements from Julia will also appear here.

      Exit this viewer with Ctrl-C.

      Logs can also be viewed from the Heroku web dashboard. For example: https://dashboard.heroku.com/apps/my-app-name/logs

      Deploy app updates changes

      To deploy any changes made to your app, simply commit those changes locally, and re-push to heroku.

      <make changes>
       git commit -am "my commit message"
      -git push heroku master
      +git push heroku master diff --git a/dev/tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html b/dev/tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html index 8cb7556f6..dbf463659 100644 --- a/dev/tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html +++ b/dev/tutorials/92--Deploying_Genie_Server_Apps_with_Nginx.html @@ -29,4 +29,4 @@ } }
      • server_name: refers to the web domain to be used. It can be put to an arbitrary name if the app is only to be served

      directly from the server public IP.

      • root: points to the public subfolder where the genie app was cloned.
      • index: refers to the site index (the landing page).
      • The various location following the initial proxy to the genie app are used to indicate static content folders to be

      served by nginx. These are needed when the server_handle_static_file is set to false in the Genie app settings.

      To make that config effective, it needs to be present in sites-enabled. The default config can be removed.

      sudo ln -s /etc/nginx/sites-available/my-genie-app /etc/nginx/sites-enabled/my-genie-app

      Then restart the server to make changes effective:

      sudo systemctl restart nginx

      Enable HTTPS

      To enable HTTPS, a site-certificate will be needed for the domain on which the site will be served. A practical approach is to use the utilities provided by certbot.

      Following provided instructions for nginx on Ubuntu 20.04:

      sudo snap install core; sudo snap refresh core
       sudo snap install --classic certbot
      -sudo ln -s /snap/bin/certbot /usr/bin/certbot

      Then, using certbot utility, a certificate will be generated and appropriate modification to nginx config will be brought to handle support for HTTPS:

      sudo certbot --nginx

      Note that this step will check for ownernship of the test.com domain mentionned in the nginx config file. For that validation to succeed, it requires to have the A record for the domain set to 123.123.123.123.

      +sudo ln -s /snap/bin/certbot /usr/bin/certbot

      Then, using certbot utility, a certificate will be generated and appropriate modification to nginx config will be brought to handle support for HTTPS:

      sudo certbot --nginx

      Note that this step will check for ownernship of the test.com domain mentionned in the nginx config file. For that validation to succeed, it requires to have the A record for the domain set to 123.123.123.123.

      diff --git a/dev/tutorials/93--Deploying_Genie_Using_A_Process_Control_System.html b/dev/tutorials/93--Deploying_Genie_Using_A_Process_Control_System.html new file mode 100644 index 000000000..c8a3c2801 --- /dev/null +++ b/dev/tutorials/93--Deploying_Genie_Using_A_Process_Control_System.html @@ -0,0 +1,19 @@ + +Deploying Genie apps using: A Process Control System · Genie - The Highly Productive Julia Web Framework

      Deploying Genie apps using: A Process Control System

      This tutorial shows how to host a Julia/Genie app using process control system. We using supervisor for this tutorial.

      Prerequisites

      Install supervisor in system. If you not sure how to install you can find the suitable command from here or you can refer to official website

      The application

      We assume that a Genie app has been developed and is ready for deployment and that it is hosted as a project on a git repository.

      For example, the app MyGenieApp generated through Genie.Generator.newapp("MyGenieApp") being hosted at github.com/user/MyGenieApp.

      The scripts presented in this tutorial are for Ubuntu 20.04.

      Install and run the Genie app on the server

      Access the server:

      ssh -i "ssh-key-for-instance.pem" user@123.123.123.123

      Install Julia if not present. Then make the clone:

      git clone github.com/user/MyGenieApp
      +cd MyGenieAp

      Install the app as any other Julia project:

      julia
      +] activate .
      +pkg> instantiate
      +exit()

      In order to launch the app using supervisor you have to create genie-supervisor.conf file or you can name it something else with file name ending .conf as extension at project directory path.

      [program:genie-application]
      +process_name=%(program_name)s_%(process_num)02d
      +command= ./bin/server
      +autostart=true
      +autorestart=true
      +stopasgroup=true
      +killasgroup=true
      +numprocs=1
      +redirect_stderr=true
      +stdout_logfile=/var/log/genie-application.log
      +stopwaitsecs=3600

      Now the application is almost ready to start just need to configure the secret token. Go into the project directory and execute the following command. It will generate secrets.jl inside config/secrets.jl file and if it exists then it will update with a new token string.

      julia --project=. --banner=no --eval="using Pkg; using Genie; Genie.Generator.write_secrets_file()"

      Then set the GENIE_ENV environment variable to prod:

      export GENIE_ENV=prod

      To launch the app few things need to do before starting process control.

      Create symbolic link to supervisor config directory

      cd /etc/supervisor/conf.d/
      +sudo ln -s /PATH TO THE SUPERVISOR.CONF FILE
      +sudo /etc/init.d/supervisor reload

      Few assumption about installation instruction

      • In this tutorial above configuration is done at Debian or Ubutu server and tested well
      • GENIE_ENV=prod should be exported before you run the application else it will take default GENIE_ENV environment

      If thing not working then you may check following things.

      (1) check the sudo systemctl status supervisor.service service is working or not. if not working then you can start and enable for the start up using following command

      sudo systemctl enable supervisor.service
      +sudo systemctl start supervisor.service

      and then check if status is active or still has any issue.

      (2) You can check the application log by tail to log file as bellow

      tail -f /var/log/genie-application.log

      in this log you will find the genie logs.

      (3) Make sure whatever port you used inside GENIE_ENV config file at server_port must be open in at firewall and no other process is bind to it.

      Check weather the any process is running at port 80 for an example use following command

      sudo lsof -t -i:80

      If you want to kill the process which is running at port 80 you can use single liner magic command

      if sudo lsof -t -i:80; then sudo kill -9 $(sudo lsof -t -i:80); fi