DevJoy.Scene behaviour (DevJoy v2.0.0)
View SourceThe Scene module provides a DSL (Domain Specific Language)
for creating a scene, managing it's parts and items.
Each scene has at least one navigable part.
The items associated with the part are represented as structs
that are fetched from the callback implementations.
Usage
In order to use the scene DSL simply add use DevJoy.Scene at the top of your module:
defmodule MyApp.SceneName do
use DevJoy.Scene
# DSL calls goes here
endWhen you use DevJoy.Scene, the @behaviour attribute will be set to
this module as well as
implement callbacks generated by DSL.
The remaining callbacks would be generated by the DSL imported to scene module.
Accumulation
Any scene item could be accumulated instead of
generating an implementation of the get_part_item/2
as long as there is a matching accumulator/0.
defmodule MyApp.SceneName do
use DevJoy.Scene
use MyApp.MyItem
alias MyApp.MyItem
part :main do
accumulate MyItem, {:type, true, true}
end
endHowever the DevJoy.Scene.Choice is always accumulated with group set to nil
to make it work with condition/3, menu/3 and question/5.
:part_name
|> SceneModule.get_part_acc(1)
|> Map.get({Choice, nil}, [])I18n
DevJoy automatically creates a portable object (.po) file for each scene.
By default the files are written to the i18n directory
in the current application's priv directory.
You can configure a path to the target directory using a priv option.
defmodule MyApp.SceneName do
priv_dir = :code.priv_dir(:my_app)
use DevJoy.Scene, priv: Path.join(priv_dir, "gettext")
endGroups
Struct DSL
A group of macros for the part and its items.
See this section for a complete list of macros.
Generated by DSL
A group of callbacks for which implementations are generated.
See this section for a complete list of macros.
Session DSL
A group of macros for a session navigation.
See this section for a complete list of callbacks.
Summary
Generated using DSL
Returns a list of the locales available for the scene.
Returns a main part of the scene using a current locale.
Returns a part of the scene using a specified name and current locale.
Returns a part of the scene using a specified name and locale.
Returns an accumulated items for first item of main part using the current locale.
Returns an accumulated items for first item of a part using a specified name and current locale.
Returns an accumulated items for item of a part using a specified name, index and current locale.
Returns an accumulated items for item of a part using a specified name, index and locale.
Returns a first item of main part using a current locale.
Returns a first item of a part using a specified name and current locale.
Returns an item of a part using a specified name, index and current locale.
Returns an item of a part using a specified name, index and locale.
Main
Accumulated data returned by the generated callback implementation. The data is grouped by a tuple with item module as the first element and a group for a particular type of item.
Instructs DSL to accumulate specific item using accumulator/0.
The 3-element tuple used by the generators.
A do … end block of code containing choice/2 calls.
When the code is compiled, there is no data validation for the macro argument.
This means the macro argument can be passed directly or as a result of any Elixir expression.
The type that has been passed determines what kind of data is expected.
The type representing the scene's module name.
Session DSL
The shorthand version of an anonymous function with a call to the DevJoy.Session.next/1.
The shorthand version of an anonymous function with a call to the DevJoy.Session.drop/2.
The shorthand version of an anonymous function with a call to the DevJoy.Session.get/2.
The shorthand version of an anonymous function with a call to the DevJoy.Session.get/2.
The shorthand version of an anonymous function with a call to the DevJoy.Session.get/2.
Struct DSL
A callback implementations generator for the DevJoy.Scene.Asset struct.
A callback implementations generator for the DevJoy.Scene.Asset struct.
A callback implementations generator for the DevJoy.Scene.Asset struct.
A callback implementations generator for the DevJoy.Scene.Challenge struct.
A callback implementations generator for the DevJoy.Scene.Choice struct.
A callback implementations generator for the DevJoy.Scene.Condition struct.
A callback implementations generator for the DevJoy.Scene.Dialog struct.
A callback implementations generator for the DevJoy.Scene.Menu struct.
A callback implementations generator for the DevJoy.Scene.Note struct.
A callback implementations generator for the DevJoy.Scene.Part struct.
A callback implementations generator for the DevJoy.Scene.Dialog struct.
Generated using DSL
@callback get_locales() :: [DevJoy.API.Storage.locale()]
Returns a list of the locales available for the scene.
Usage
defmodule MyApp.SceneName do
use DevJoy.Scene
part :main do
# DSL for part items goes here
end
end
MyApp.SceneName.get_locales() == ["en"]
# => true@callback get_part() :: DevJoy.Scene.Part.t()
Returns a main part of the scene using a current locale.
Usage
Same as get_part(:main, DevJoy.API.Storage.get_locale()).
defmodule MyApp.SceneName do
use DevJoy.Scene
part :main do
# DSL for part items goes here
end
end
DevJoy.Session.Storage.set_locale("en")
MyApp.SceneName.get_part() == MyApp.SceneName.get_part(:main, "en")
# => true@callback get_part(DevJoy.Scene.Part.name()) :: DevJoy.Scene.Part.t()
Returns a part of the scene using a specified name and current locale.
Usage
Same as get_part(name, DevJoy.API.Storage.get_locale()).
defmodule MyApp.SceneName do
use DevJoy.Scene
part :part_name do
# DSL for part items goes here
end
end
DevJoy.Session.Storage.set_locale("en")
MyApp.SceneName.get_part(:part_name) == MyApp.SceneName.get_part(:part_name, "en")
# => true@callback get_part(DevJoy.Scene.Part.name(), DevJoy.API.Storage.locale()) :: DevJoy.Scene.Part.t()
Returns a part of the scene using a specified name and locale.
Usage
defmodule MyApp.SceneName do
use DevJoy.Scene
part :part_name, "Page Title" do
# DSL for part items goes here
end
end
DevJoy.Session.Storage.set_locale("en")
part = MyApp.SceneName.get_part(:part_name)
part == %DevJoy.Scene.Part{name: :part_name, page_title: "Page Title", scene: MyApp.SceneName}
# => true@callback get_part_acc() :: acc()
Returns an accumulated items for first item of main part using the current locale.
Usage
Same as get_part_acc(:main, 1, DevJoy.API.Storage.get_locale()).
defmodule MyApp.SceneName do
use DevJoy.Scene
part :main do
menu "Menu title" do
choice "Menu item content", continue()
end
end
end
DevJoy.Session.Storage.set_locale("en")
MyApp.SceneName.get_part_acc() == MyApp.SceneName.get_part_acc(:main, 1, "en")
# => true@callback get_part_acc(DevJoy.Scene.Part.name()) :: acc()
Returns an accumulated items for first item of a part using a specified name and current locale.
Usage
Same as get_part_acc(name, 1, DevJoy.API.Storage.get_locale()).
defmodule MyApp.SceneName do
use DevJoy.Scene
part :part_name do
menu "Menu title" do
choice "Menu item content", continue()
end
end
end
DevJoy.Session.Storage.set_locale("en")
MyApp.SceneName.get_part_acc(:part_name) == MyApp.SceneName.get_part_acc(:part_name, 1, "en")
# => true@callback get_part_acc(DevJoy.Scene.Part.name(), DevJoy.API.Item.index()) :: acc()
Returns an accumulated items for item of a part using a specified name, index and current locale.
Usage
Same as get_part_acc(name, index, DevJoy.API.Storage.get_locale()).
defmodule MyApp.SceneName do
use DevJoy.Scene
part :part_name do
menu "Menu title" do
choice "Menu item content", continue()
end
end
end
DevJoy.Session.Storage.set_locale("en")
MyApp.SceneName.get_part_acc(:part_name, 1) == MyApp.SceneName.get_part_acc(:part_name, 1, "en")
# => true@callback get_part_acc( DevJoy.Scene.Part.name(), DevJoy.API.Item.index(), DevJoy.API.Storage.locale() ) :: acc()
Returns an accumulated items for item of a part using a specified name, index and locale.
Usage
Same as get_part_acc(name, index, DevJoy.API.Storage.get_locale()).
defmodule MyApp.SceneName do
use DevJoy.Scene
part :part_name do
menu "Menu title" do
choice "Menu item content", continue()
end
end
end
DevJoy.Session.Storage.set_locale("en")
acc = MyApp.SceneName.get_part_acc(:part_name, 1, "en")
match?(%{{DevJoy.Scene.Choice, nil} => [%DevJoy.Scene.Choice{content: "Menu item content"}]}, acc)
# => true@callback get_part_item() :: DevJoy.API.Item.t() | nil
Returns a first item of main part using a current locale.
Usage
Same as get_part_item(:main, 1, DevJoy.API.Storage.get_locale()).
defmodule MyApp.SceneName do
use DevJoy.Scene
part :main do
menu "Menu title" do
choice "Menu item content", continue()
end
end
end
DevJoy.Session.Storage.set_locale("en")
MyApp.SceneName.get_part_item() == MyApp.SceneName.get_part_item(:main, 1, "en")
# => true@callback get_part_item(DevJoy.Scene.Part.name()) :: DevJoy.API.Item.t() | nil
Returns a first item of a part using a specified name and current locale.
Usage
Same as get_part_item(name, 1, DevJoy.API.Storage.get_locale()).
defmodule MyApp.SceneName do
use DevJoy.Scene
part :part_name do
menu "Menu title" do
choice "Menu item content", continue()
end
end
end
DevJoy.Session.Storage.set_locale("en")
MyApp.SceneName.get_part_item(:part_name) == MyApp.SceneName.get_part_item(:part_name, 1, "en")
# => true@callback get_part_item(DevJoy.Scene.Part.name(), DevJoy.API.Item.index()) :: DevJoy.API.Item.t() | nil
Returns an item of a part using a specified name, index and current locale.
Usage
Same as get_part_item(name, index, DevJoy.API.Storage.get_locale()).
defmodule MyApp.SceneName do
use DevJoy.Scene
part :part_name do
menu "Menu title" do
choice "Menu item content", continue()
end
end
end
DevJoy.Session.Storage.set_locale("en")
MyApp.SceneName.get_part_item(:part_name, 1) == MyApp.SceneName.get_part_item(:part_name, 1, "en")
# => true@callback get_part_item( DevJoy.Scene.Part.name(), DevJoy.API.Item.index(), DevJoy.API.Storage.locale() ) :: DevJoy.API.Item.t() | nil
Returns an item of a part using a specified name, index and locale.
Usage
defmodule MyApp.SceneName do
use DevJoy.Scene
part :part_name do
menu "Menu title" do
choice "Menu item content", continue()
end
end
end
DevJoy.Session.Storage.set_locale("en")
%DevJoy.Scene.Menu{title: "Menu title"} == MyApp.SceneName.get_part_item(:part_name, 1, "en")
# => trueMain
@type acc() :: %{ required({DevJoy.API.Item.struct_module(), acc_group :: any()}) => [ DevJoy.API.Item.t_acc() ] }
Accumulated data returned by the generated callback implementation. The data is grouped by a tuple with item module as the first element and a group for a particular type of item.
@spec accumulate( macro_input(DevJoy.API.Item.struct_module()), macro_input(accumulator()) ) :: Macro.output()
Instructs DSL to accumulate specific item using accumulator/0.
Usage
defmodule MyApp.SceneName do
use DevJoy.Scene
part :main do
accumulate Asset, {:type, &(&1[:type] == :bg_image), true}
asset :bg_image, "/path/to/background.jpg"
asset :bg_music, "/path/to/background.mp3"
end
end
acc = MyApp.SceneName.get_part_acc()
item = MyApp.SceneName.get_part_item()
image_asset = %DevJoy.Scene.Asset{path: "/path/to/background.jpg", type: :bg_image}
%{{DevJoy.Scene.Asset, :bg_image} => [image_asset]} == acc
# => true
%DevJoy.Scene.Asset{path: "/path/to/background.mp3", type: :bg_music} == acc
# => true@type accumulator() :: {field :: atom(), (DevJoy.API.Item.fields() -> boolean()) | true, (any() -> boolean()) | boolean()}
The 3-element tuple used by the generators.
The first element is a field name of the specific item structure.
The field value is used as a group (see acc_group in acc/0).
If the field doesn't exist, it's value is a nil or the nil is passed as the field name,
then the nil value is used as the group which is useful for using it as a default
or matches-all group.
The second element is a 1-arity function or a true value
and is used to decide whether the item should be accumulated.
The function takes a keyword list of element fields and returns a boolean value.
The last element is a 1-arity function or a boolean value and is used to decide whether to overwrite the items accumulated for the previous item. The function takes a group and returns a boolean value.
Usage
# match-all, accumulate all, always override
{nil, true, true}
# match by `:type` field, accumulate all, always override
{:type, true, true}
# match by `:type` field, accumulate only if type is :bg_image, always override
{:type, &(&1[:type] == :bg_image), true}
# match-all, accumulate all, never override
{nil, true, false}
# match-all, accumulate all, override only if type is :bg_image
{nil, true, &(&1[:type] == :bg_image)}@type choices_block() :: Macro.input()
A do … end block of code containing choice/2 calls.
@type macro_input(type) :: type | Macro.input()
When the code is compiled, there is no data validation for the macro argument.
This means the macro argument can be passed directly or as a result of any Elixir expression.
The type that has been passed determines what kind of data is expected.
Usage
# specification for the example macro which accepts non negative integer
# directly or as a result of any `Elixir` expression
@spec example(DevJoy.Scene.macro_input(non_neg_integer)) :: Macro.output()@type t() :: module()
The type representing the scene's module name.
Usage
defmodule MyApp.SceneName do
use DevJoy.Scene
part :main do
# DSL for part items goes here
end
end
part = MyApp.SceneName.get_part()
part.scene == MyApp.SceneName
# => trueSession DSL
@spec continue() :: (Keyword.t() -> :ok)
The shorthand version of an anonymous function with a call to the DevJoy.Session.next/1.
Usage
defmodule MyApp.SceneName do
use DevJoy.Scene
part :continue do
menu :system, "Choose carefully" do
choice "Short action", continue()
choice "Long action", fn opts -> DevJoy.Session.next(opts) end
end
end
end@spec go_back(pos_integer()) :: (Keyword.t() -> :ok)
The shorthand version of an anonymous function with a call to the DevJoy.Session.drop/2.
Usage
defmodule MyApp.SceneName do
use DevJoy.Scene
part :continue do
menu :system, "Choose carefully" do
choice "Short action", go_back()
choice "Long action", fn opts -> DevJoy.Session.drop(opts) end
end
# or
menu :system, "Choose carefully" do
choice "Short action", go_back(2)
choice "Long action", fn opts -> DevJoy.Session.drop(2, opts) end
end
end
end@spec goto() :: Macro.output()
The shorthand version of an anonymous function with a call to the DevJoy.Session.get/2.
Usage
The same as goto/2, but defaults to current scene and :main part.
defmodule MyApp.SceneName do
use DevJoy.Scene
part :continue do
menu :system, "Choose carefully" do
choice "Short action", goto()
choice "Long action", fn opts -> DevJoy.Session.get({__MODULE__, :main, 1}, opts) end
end
end
end@spec goto(macro_input(DevJoy.Scene.Part.name())) :: Macro.output()
The shorthand version of an anonymous function with a call to the DevJoy.Session.get/2.
Usage
The same as goto/2, but defaults to current scene.
defmodule MyApp.SceneName do
use DevJoy.Scene
part :continue do
menu :system, "Choose carefully" do
choice "Short action", goto(:part_name)
choice "Long action", fn opts -> DevJoy.Session.get({__MODULE__, :part_name, 1}, opts) end
end
end
end@spec goto(t(), DevJoy.Scene.Part.name()) :: (Keyword.t() -> :ok)
The shorthand version of an anonymous function with a call to the DevJoy.Session.get/2.
Usage
defmodule MyApp.SceneName do
use DevJoy.Scene
part :continue do
menu :system, "Choose carefully" do
choice "Short action", goto(AnotherModule, :part_name)
choice "Long action", fn opts -> DevJoy.Session.get({AnotherModule, :part_name, 1}, opts) end
end
end
endStruct DSL
@spec asset( macro_input(DevJoy.Scene.Asset.type()), macro_input(DevJoy.Scene.Asset.path()) ) :: Macro.output()
A callback implementations generator for the DevJoy.Scene.Asset struct.
Same as asset(type, path, [], nil).
Usage
defmodule MyApp.SceneName do
use DevJoy.Scene
part :asset do
asset :background, "/path/to/background.jpg"
end
end@spec asset( macro_input(DevJoy.Scene.Asset.type()), macro_input(DevJoy.Scene.Asset.path()), macro_input(DevJoy.Scene.Asset.data()) ) :: Macro.output()
A callback implementations generator for the DevJoy.Scene.Asset struct.
Same as asset(type, path, data, nil).
Usage
defmodule MyApp.SceneName do
use DevJoy.Scene
part :asset do
asset :background, "/path/to/background.jpg", aspect_ratio: "1:1"
end
end@spec asset( macro_input(DevJoy.Scene.Asset.type()), macro_input(DevJoy.Scene.Asset.path()), macro_input(DevJoy.Scene.Asset.data()), [{:do, macro_input(DevJoy.Scene.Asset.content())}] ) :: Macro.output()
@spec asset( macro_input(DevJoy.Scene.Asset.type()), macro_input(DevJoy.Scene.Asset.path()), macro_input(DevJoy.Scene.Asset.data()), macro_input(DevJoy.Scene.Asset.content()) ) :: Macro.output()
A callback implementations generator for the DevJoy.Scene.Asset struct.
Usage
defmodule MyApp.SceneName do
use DevJoy.Scene
part :asset do
asset :background, "/path/to/background.svg", [], "…"
# or
asset :background, "/path/to/background.svg", [] do
"…"
end
end
end@spec challenge( macro_input(DevJoy.Scene.Challenge.type()), macro_input(DevJoy.Scene.Challenge.data()) ) :: Macro.output()
A callback implementations generator for the DevJoy.Scene.Challenge struct.
Usage
defmodule MyApp.SceneName do
use DevJoy.Scene
part :challenge do
challenge :challenge_type
# or
challenge :challenge_type, some: :data
end
end@spec choice( macro_input(DevJoy.Scene.Choice.content()), macro_input(DevJoy.Scene.Choice.action()) ) :: Macro.output()
A callback implementations generator for the DevJoy.Scene.Choice struct.
Condition
defmodule MyApp.SceneName do
use DevJoy.Scene
part :condition do
condition :john_doe, &MyApp.some_func/1 do
choice :choiceA, goto(:partA)
choice :choiceB, goto(:partB)
end
end
endMenu
defmodule MyApp.SceneName do
use DevJoy.Scene
part :menu do
menu "Menu title" do
choice "Menu item content", goto(:part_name)
end
end
endQuestion
defmodule MyApp.SceneName do
use DevJoy.Scene
part :dialog do
question :john_doe, "Question content" do
choice "Answer content", goto(:part_name)
end
# or
question :john_doe, [some: :data], "Question content" do
choice "Answer content", goto(:part_name)
end
end
end
@spec condition( macro_input(DevJoy.Character.id()), macro_input(DevJoy.Scene.Condition.action()), [ {:do, choices_block()} ] ) :: Macro.output()
A callback implementations generator for the DevJoy.Scene.Condition struct.
Usage
defmodule MyApp.SceneName do
use DevJoy.Scene
part :condition do
condition :john_doe, &MyApp.some_func/1 do
choice :choiceA, goto(:partA)
choice :choiceB, goto(:partB)
end
end
end@spec dialog( macro_input(DevJoy.Character.id()), macro_input(DevJoy.Character.position()), macro_input(DevJoy.Scene.Dialog.data()), macro_input(DevJoy.Scene.Dialog.content()) ) :: Macro.output()
A callback implementations generator for the DevJoy.Scene.Dialog struct.
Usage
defmodule MyApp.SceneName do
use DevJoy.Scene
part :dialog do
dialog :john_doe, "Dialog content"
# or
dialog :john_doe, [some: :data], "Dialog content"
end
end@spec note( macro_input(DevJoy.Scene.Note.title()), macro_input(DevJoy.Scene.Note.data()), [ {:do, macro_input(DevJoy.Scene.Note.content())} ] ) :: Macro.output()
@spec note( macro_input(DevJoy.Scene.Note.title()), macro_input(DevJoy.Scene.Note.data()), macro_input(DevJoy.Scene.Note.content()) ) :: Macro.output()
A callback implementations generator for the DevJoy.Scene.Note struct.
Usage
defmodule MyApp.SceneName do
use DevJoy.Scene
part :note do
note "Note title", "Note content"
# or
note "Note title", [some: :data], "Note content"
# or
note "Note title", [some: :data] do
"Note content"
end
end
end@spec part( macro_input(DevJoy.Scene.Part.name()), macro_input(DevJoy.Scene.Part.page_title()), macro_input(DevJoy.Scene.Part.data()), [{:do, Macro.input()}] ) :: Macro.output()
A callback implementations generator for the DevJoy.Scene.Part struct.
Usage
defmodule MyApp.SceneName do
use DevJoy.Scene
part :part_name do
# DSL for part items goes here
end
end@spec question( macro_input(DevJoy.Character.id()), macro_input(DevJoy.Character.position()), macro_input(DevJoy.Scene.Dialog.data()), macro_input(DevJoy.Scene.Dialog.content()), [{:do, choices_block()}] ) :: Macro.output()
A callback implementations generator for the DevJoy.Scene.Dialog struct.
Usage
defmodule MyApp.SceneName do
use DevJoy.Scene
part :dialog do
question :john_doe, "Question content" do
choice "Answer content", goto(:part_name)
end
# or
question :john_doe, [some: :data], "Question content" do
choice "Answer content", goto(:part_name)
end
end
end