Revisions
-
timruffles revised this gist
Jun 10, 2014 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -5,7 +5,7 @@ Here are the approaches I can see. The two points that seem to divide the approa ## Passing modules Modules are first class, so you can pass them in. Used in [EEx](https://github.com/elixir-lang/elixir/blob/master/lib/eex/lib/eex/compiler.ex#L16), where passed module must implement a [behaviour](https://github.com/elixir-lang/elixir/blob/master/lib/eex/lib/eex/engine.ex#L27-L31). -
timruffles revised this gist
Jun 10, 2014 . 1 changed file with 57 additions and 19 deletions.There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,24 +2,18 @@ In many production systems you'll want to have one module capable of talking to Here are the approaches I can see. The two points that seem to divide the approaches are their tool-ability (dialyzer) and their ability to handle stateful implementations (which need a `pid`). ## Behaviours + passed Modules are first class, so you can pass them in. Used in [EEx](https://github.com/elixir-lang/elixir/blob/master/lib/eex/lib/eex/compiler.ex#L16), where passed module must implement a [behaviour](https://github.com/elixir-lang/elixir/blob/master/lib/eex/lib/eex/engine.ex#L27-L31). ```elixir defmodule Cache do use Behaviour defcallback cached?(any,any) :: boolean defcallback put(any,any) :: nil end defmodule Cache.Memory do @@ -28,12 +22,24 @@ defmodule Cache.Memory do end defmodule Cache.Redis do def put(redis_pid,x) do {:ok,1} = Redis.set redis_pid, x, 1 end def cached?(redis_pid,x) do {:ok,x} = Redis.get(redis_pid,x) x != nil end end # usage defmodule UsesCache do def start(cache,cache_pid) do cache.put(cache_pid,:hello) true = cache.cached?(cache_pid,:hello) end end UsesCache.start(Cache.Memory,HashSet.new) ``` Similar idea to duck-typing. @@ -51,23 +57,43 @@ Write a `Protocol` for the functionality. You can then pass in an opaque value t - requires stub implementations for testing ```elixir defprotocol Cache do def cached?(id,item) def put(id,item) end defmodule Cache.Memory do defstruct set: nil alias __MODULE__, as: Mod defimpl Cache, for: Mod do def put(%Mod{set: set},x) do: Set.add set, x def cached?(%Mod{set: set},x) do: Set.member? map, x end end defmodule Cache.Redis do defstruct redis: nil alias __MODULE__, as: Mod defimpl Cache, for: Mod do def put(%Mod{redis: redis},x) do {:ok,1} = Redis.set redis, x, 1 end def cached?(%Mod{redis: redis},x) do {:ok,x} = Redis.get(redis,x) x != nil end end end # usage defmodule UsesCache do def start(cache) do Cache.put(cache,:hello) true = Cache.cached?(cache,:hello) end end UsesCache.start(%CacheMemory{set:HashSet.new}) ``` ## Passing callbacks @@ -86,6 +112,18 @@ end Now the `callback` field of state can be used by functions of this `gen_server` module. ```elixir # usage defmodule UsesCache do def start(put,cached) do put.(:hello) true = cached.(:hello) end end # can create callbacks from anything: stateful, stateless etc ``` + works with any implementation, even ad-hoc + dialyze-able - pass many values for large APIs -
timruffles revised this gist
Jun 10, 2014 . 1 changed file with 80 additions and 7 deletions.There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,12 +1,74 @@ In many production systems you'll want to have one module capable of talking to many potential implementations of a collaborator module (e.g a in memory cache, a redis-based cache etc). While testing it's useful to control which module the module under test is talking to. Here are the approaches I can see. The two points that seem to divide the approaches are their tool-ability (dialyzer) and their ability to handle stateful implementations (which need a `pid`). ```exlixir defmodule UsesCache do def start(cache,cache_pid) do cache.put(cache_pid,:hello) true = cache.cached?(cache_pid,:hello) end end ``` ## Behaviours Modules are first class, so you can pass them in. Used in [EEx](https://github.com/elixir-lang/elixir/blob/master/lib/eex/lib/eex/compiler.ex#L16), where passed module must implement a [behaviour](https://github.com/elixir-lang/elixir/blob/master/lib/eex/lib/eex/engine.ex#L27-L31). ```elixir defmodule Cache do use Behaviour defcallback cached?(any,any) :: boolean end defmodule Cache.Memory do def put(set,x) do: Set.add set, x def cached?(set,x) do: Set.member? map, x end defmodule Cache.Redis do def put(redis_pid,x) do {:ok,1} = Redis.set x, 1 def cached?(redis_pid,x) do {:ok,x} = Redis.get(redis_pid,x) x != nil end end ``` Similar idea to duck-typing. + simple - dializer(?) - modules with state - you'd have to pass a `pid` too, e.g `{module,pid}` (eugh) ## Protocols + values Write a `Protocol` for the functionality. You can then pass in an opaque value to collaborators, and the implementation will be decided at runtime. + handles stateful and stateless implementations easily + dialyze-able - requires stub implementations for testing ```elixir defmodule Cache do use Behaviour defcallback cached?(any,any) :: boolean end defmodule Cache.Memory do def put(set,x) do: Set.add set, x def cached?(set,x) do: Set.member? map, x end defmodule Cache.Redis do def put(redis_pid,x) do {:ok,1} = Redis.set x, 1 def cached?(redis_pid,x) do {:ok,x} = Redis.get(redis_pid,x) x != nil end end ``` ## Passing callbacks @@ -24,10 +86,21 @@ end Now the `callback` field of state can be used by functions of this `gen_server` module. + works with any implementation, even ad-hoc + dialyze-able - pass many values for large APIs ## Stateful Create a stateful module that holds the module, [refer to that](https://github.com/ericmj/hex_web/blob/84516f0320aa4c4840054831666a3f5d5958a9d6/lib/hex_web/router.ex#L29). - doesn't work for use-cases with many instances (pass-the-pid) + dialyzer - need a Behaviour for the return type of the getter ## Module generation You could [generate](https://gist.github.com/alco/c08256ae72d31c76e767) a module based on a run-time config. - moving parts - doesn't work for use-cases with many instances (pass-the-pid) - dialyzer - seems likely to throw it off -
timruffles revised this gist
Jun 10, 2014 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,4 @@ Some notes on the related goals of DI/dynamic dispatch. In many production systems you'll want to have one module capable of talking to many potential implementations of collaborator modules (e.g a in memory cache, a redis-based cache etc). While testing it's useful to control which module the module under test is talking to. I'm still pondering the pros/cons of the approaches; my least favourite is the last as it feels icky to rely on state. -
timruffles revised this gist
May 20, 2014 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,7 @@ Some notes on the related goals of DI/dynamic dispatch. While testing it's useful to control which module the module under test is talking to. In many production systems you'll want to have one module capable of talking to many potential implementations of collaborator modules. I'm still pondering the pros/cons of the approaches; my least favourite is the last as it feels icky to rely on state. ## Passing modules Modules are first class, so you can pass them in. Used in [EEx](https://github.com/elixir-lang/elixir/blob/master/lib/eex/lib/eex/compiler.ex#L16), where passed module must implement a [behaviour](https://github.com/elixir-lang/elixir/blob/master/lib/eex/lib/eex/engine.ex#L27-L31). -
timruffles revised this gist
May 20, 2014 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,7 +4,7 @@ Some notes on the related goals of DI/dynamic dispatch. While testing it's usefu Modules are first class, so you can pass them in. Used in [EEx](https://github.com/elixir-lang/elixir/blob/master/lib/eex/lib/eex/compiler.ex#L16), where passed module must implement a [behaviour](https://github.com/elixir-lang/elixir/blob/master/lib/eex/lib/eex/engine.ex#L27-L31). Similar idea to duck-typing. A question arises when you have a module with state - do you pass in a `pid` too? If that's the case, perhaps encapsulating it with a `Protocol` is a better angle, as it'd allow non-stateful implementations to pass something else in. ## Passing callbacks -
timruffles revised this gist
May 20, 2014 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,6 +4,8 @@ Some notes on the related goals of DI/dynamic dispatch. While testing it's usefu Modules are first class, so you can pass them in. Used in [EEx](https://github.com/elixir-lang/elixir/blob/master/lib/eex/lib/eex/compiler.ex#L16), where passed module must implement a [behaviour](https://github.com/elixir-lang/elixir/blob/master/lib/eex/lib/eex/engine.ex#L27-L31). Similar idea to duck-typing. ## Passing callbacks For a single method, you could just pass a function. Then in tests you pass a stub method, and in production you can wrap up the real module behind it. -
timruffles revised this gist
May 20, 2014 . 1 changed file with 7 additions and 4 deletions.There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,8 +1,10 @@ Some notes on the related goals of DI/dynamic dispatch. While testing it's useful to control which module the module under test is talking to. In many production systems you'll want to have one module capable of talking to many potential implementations of collaborator modules. ## Passing modules Modules are first class, so you can pass them in. Used in [EEx](https://github.com/elixir-lang/elixir/blob/master/lib/eex/lib/eex/compiler.ex#L16), where passed module must implement a [behaviour](https://github.com/elixir-lang/elixir/blob/master/lib/eex/lib/eex/engine.ex#L27-L31). ## Passing callbacks For a single method, you could just pass a function. Then in tests you pass a stub method, and in production you can wrap up the real module behind it. @@ -17,10 +19,11 @@ end ``` Now the `callback` field of state can be used by functions of this `gen_server` module. ## Module generation You could [generate](https://gist.github.com/alco/c08256ae72d31c76e767) a module based on a run-time config. ## Stateful Create a stateful module that holds the module, [refer to that](https://github.com/ericmj/hex_web/blob/84516f0320aa4c4840054831666a3f5d5958a9d6/lib/hex_web/router.ex#L29). -
timruffles revised this gist
May 20, 2014 . 1 changed file with 8 additions and 6 deletions.There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -6,13 +6,15 @@ Modules are first class, so you can pass them in. Used in [EEx](https://github.c For a single method, you could just pass a function. Then in tests you pass a stub method, and in production you can wrap up the real module behind it. ```elixir def start_link({some_callback}) do :gen_server.start_link(@name,SomeModule,{some_callback},[]) end def init({some_callback}) do {:ok,%State{callback: some_callback}) end ``` Now the `callback` field of state can be used by functions of this `gen_server` module. # Module generation -
timruffles renamed this gist
May 20, 2014 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
timruffles created this gist
May 20, 2014 .There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,24 @@ # Passing modules Modules are first class, so you can pass them in. Used in [EEx](https://github.com/elixir-lang/elixir/blob/master/lib/eex/lib/eex/compiler.ex#L16), where passed module must implement a [behaviour](https://github.com/elixir-lang/elixir/blob/master/lib/eex/lib/eex/engine.ex#L27-L31). # Passing callbacks For a single method, you could just pass a function. Then in tests you pass a stub method, and in production you can wrap up the real module behind it. def start_link({some_callback}) do :gen_server.start_link(@name,SomeModule,{some_callback},[]) end def init({some_callback}) do {:ok,%State{callback: some_callback}) end Now the `callback` field of state can be used by functions of this `gen_server` module. # Module generation You could [generate](https://gist.github.com/alco/c08256ae72d31c76e767) a module based on a run-time config. # Stateful Create a stateful module that holds the module, [refer to that](https://github.com/ericmj/hex_web/blob/84516f0320aa4c4840054831666a3f5d5958a9d6/lib/hex_web/router.ex#L29).