DevJoy.Scene behaviour (DevJoy v2.0.0)

View Source

The 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
end

When 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
end

However 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")
end

Groups

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.

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.

t()

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.Menu struct.

A callback implementations generator for the DevJoy.Scene.Note struct.

A callback implementations generator for the DevJoy.Scene.Part struct.

Generated using DSL

get_locales()

@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

get_part()

@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

get_part(name)

@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

get_part(name, locale)

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

get_part_acc()

@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

get_part_acc(name)

@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

get_part_acc(name, index)

@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

get_part_acc(name, index, locale)

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

get_part_item()

@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

get_part_item(name)

@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

get_part_item(name, index)

@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

get_part_item(name, index, locale)

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")
# => true

Main

acc()

@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.

accumulate(struct_module, accumulator \\ quote do {:type, true, true} end)

(macro)

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

accumulator()

@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)}

choices_block()

@type choices_block() :: Macro.input()

A do … end block of code containing choice/2 calls.

macro_input(type)

@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()

t()

@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
# => true

Session DSL

continue()

@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

go_back(amount \\ 1)

@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

goto()

(macro)
@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

goto(part_name)

(macro)

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

goto(scene, part_name)

@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
end

Struct DSL

asset(type, path)

(macro)

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

asset(type, path, data)

(macro)

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

asset(type, path, data, content)

(macro)

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

challenge(type, data \\ [])

(macro)

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

choice(content, action)

(macro)

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
end

Menu

defmodule MyApp.SceneName do
  use DevJoy.Scene

  part :menu do
    menu "Menu title" do
      choice "Menu item content", goto(:part_name)
    end
  end
end

Question

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

condition(character_id \\ nil, action, list)

(macro)

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

dialog(character_id \\ nil, position \\ :left, data \\ [], content)

(macro)

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

note(title, data \\ [], content)

(macro)

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

part(name, page_title \\ nil, data \\ [], list)

(macro)

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

question(character_id \\ nil, position \\ :left, data \\ [], content, list)

(macro)

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