flok 0.0.102 → 0.0.103

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +3 -0
  3. data/app/drivers/chrome/pipe.rb +66 -51
  4. data/app/drivers/chrome/src/controller.js +20 -1
  5. data/app/drivers/chrome/src/hook.js +10 -12
  6. data/app/drivers/chrome/src/net.js +4 -4
  7. data/app/drivers/chrome/src/sockio.js +2 -2
  8. data/app/drivers/chrome/src/ui.js +18 -15
  9. data/app/kern/mod/event.js +3 -1
  10. data/app/kern/mod/hook.js +13 -1
  11. data/app/kern/mod/ui.js +71 -0
  12. data/docs/controllers.md +14 -0
  13. data/docs/dispatch.md +4 -2
  14. data/docs/kernel_api.md +1 -0
  15. data/docs/kernel_handbook/find_view.md +34 -0
  16. data/docs/kernel_handbook/hooks.md +12 -1
  17. data/docs/mod/controller.md +5 -2
  18. data/docs/mod/hook.md +4 -3
  19. data/docs/mod/persist.md +1 -1
  20. data/docs/mod/ui.md +1 -0
  21. data/docs/user_handbook/hooks.md +38 -14
  22. data/flok.gemspec +2 -0
  23. data/lib/flok/user_compiler.rb +18 -2
  24. data/lib/flok/user_compiler_templates/ctable.js.erb +1 -0
  25. data/lib/flok/user_hook_generators/goto.rb +32 -4
  26. data/lib/flok/version.rb +1 -1
  27. data/spec/env/iface.rb +38 -0
  28. data/spec/env/kern.rb +4 -0
  29. data/spec/iface/driver/controller_spec.rb +25 -6
  30. data/spec/iface/driver/dispatch_spec.rb +7 -0
  31. data/spec/iface/driver/hook_spec.rb +83 -7
  32. data/spec/iface/driver/net_spec.rb +50 -41
  33. data/spec/iface/driver/persist_spec.rb +47 -4
  34. data/spec/iface/driver/ping_spec.rb +1 -1
  35. data/spec/iface/driver/rtc_spec.rb +2 -2
  36. data/spec/iface/driver/sockio_spec.rb +4 -3
  37. data/spec/iface/driver/timer_spec.rb +20 -0
  38. data/spec/iface/driver/ui_spec.rb +27 -4
  39. data/spec/kern/assets/find_view/controller0.rb +24 -0
  40. data/spec/kern/assets/find_view/controller1.rb +35 -0
  41. data/spec/kern/assets/find_view/controller1a.rb +37 -0
  42. data/spec/kern/assets/find_view/controller2.rb +23 -0
  43. data/spec/kern/assets/find_view/controller3.rb +38 -0
  44. data/spec/kern/assets/find_view/controller4.rb +38 -0
  45. data/spec/kern/assets/find_view/controller5.rb +52 -0
  46. data/spec/kern/assets/find_view/controller6.rb +37 -0
  47. data/spec/kern/assets/find_view/controller7.rb +37 -0
  48. data/spec/kern/assets/find_view/controller8.rb +53 -0
  49. data/spec/kern/assets/find_view/controller9.rb +67 -0
  50. data/spec/kern/assets/hook_entry_points/controller0.rb +13 -1
  51. data/spec/kern/assets/push_count.rb +42 -0
  52. data/spec/kern/controller_spec.rb +34 -0
  53. data/spec/kern/find_view_spec.rb +269 -0
  54. data/spec/kern/hook_entry_points_and_manifest_spec.rb +60 -0
  55. data/spec/kern/hook_user_generators_spec.rb +100 -0
  56. data/spec/lib/helpers.rb +1 -2
  57. metadata +58 -2
@@ -0,0 +1,34 @@
1
+ #find_view
2
+ `find_view(bp, spider_payload)` is a way to grab a set of sub-views of a **controller** and return a listing of base-pointers to the **views**. Originally
3
+ designed to allow selection of sub-views during transition interceptors (hooks). This would usually upset the abstraction barrier between controllers, which
4
+ are not supposed to know about their children, but for practical and performance reasons, we have decided to go with this style of implementation.
5
+
6
+ A spider payload is a specially crafted cascading hash (usually created statically by macros) that represents a multi-branch view-hierarchy *regular* expression
7
+ that is able to capture views on it's way through traversal. It supports common operations of regular expressions such as *any* (`.`) and *one or more* (`+`) operator.
8
+ Thus, the spider payload is also able to select groups of views which is useful if you are doing a pushed view stack and need to know all except the top two views.
9
+
10
+ The following expressions are supported:
11
+
12
+ * `.+` - This means *at least one* view. It returns a group.
13
+ * `.` - This means *exactly one view*. It returns a single view.
14
+ * `my_view` - This is simply a named view.
15
+
16
+ Additionally, expressions may contain a *named* capture by adding a `:$NAME` to the expression:
17
+
18
+ * `.+:my_group`
19
+ * `.:my_view`
20
+ * `my_view:my_view`
21
+
22
+ An expression is then a selection tree:
23
+
24
+ ```ruby
25
+ .
26
+ my_view
27
+ +
28
+ my_other_sub_view:content
29
+ my_other_view
30
+ ```
31
+
32
+ > It is important to note that a selection will fail unless both all views in the selector are satisfied and that all views are satisfied have matching super-trees. In the above example, if there were two `my_view` descendents of our current controller we are selecting off, then `my_view` could not match half the selector on one descedent and then the other half on another descendent.
33
+ >
34
+ >
@@ -1,4 +1,15 @@
1
1
  # Hooks in the kernel
2
2
 
3
3
  ## How user-generators work
4
- As mentioned in the [User Handbook | Hooks](../user_handbook/hooks.md), user-generators are compiled into the kernel when the
4
+ As mentioned in the [User Handbook | Hooks](../user_handbook/hooks.md), user-generators are compiled into the kernel when the user
5
+ defines hooks in `./config/hooks.rb`. These hooks are then interpreted when the `flok` user binary runs through build process
6
+ via `UserHooksToManifestOrchestrator`. The orchestrator then evaluates the `./config/hooks.rb` through the `UserHooksDSL` which
7
+ captures each hook request as an unvalidated expression; that expression is then run through a generator block which yields
8
+ a hook manifest entry that can be added to the hooks manifest. The hooks compiler then takes the manifest and runs over the source
9
+ code with the manifest to inject code where needed.
10
+
11
+ ## How hook entry points get placed in source
12
+ Hook entry points are not magical. They are hand placed, typically via ERB, into source as they require some JSON encoded data.
13
+
14
+ ## Hook events
15
+ Hooks are emitted into user-space via the `hook` module [Hook](../mod/hooks.md).
@@ -6,6 +6,7 @@
6
6
  ### Functions
7
7
  `if_controller_init(bp, rvp, name, context)` - Initialize a controller that manages the view at `rvp` and contains the address `bp`. All events are sent to `bp` should be handled appropriatlely.
8
8
  The `rvp` pointer will always be an initialized (but not attached) view. When receiving an action, the view is guaranteed to be attached at that time. Context is what the controller was initialized with.
9
+ **All controllers should be the same name as their views, there is a 1-1 relationship between views and controllers that was not originally true in the first version. Now views and contollers act as one**
9
10
 
10
11
  ### Spec helpers
11
12
 
@@ -28,8 +29,10 @@ special meanings based on the `name` field of the event:
28
29
  but have not been destroyed.
29
30
 
30
31
  ### Spec fixtures (driver side)
31
- There should be a special controller called `__test__` that can be initialized and should respond in the following ways when it receives an action and
32
+ There should be two identical controllers called `spec_one_spot` and `spec_blank` that can be initialized and should respond in the following ways when it receives an action and
32
33
  event:
33
- Note: an event is **not** a raw message, it is through the message of `int_event`, i.e. `[3, "int_event", "event_name", info]`
34
+ Note: an event is **not** a raw message, it is through the message of `int_event`, i.e. `[3, $NAME, bp, "custom_event", info]`. The event name may either be `foo` or `bar`
34
35
  Sends a **event** back called `action_rcv` containing `{from: from, to: to, info: info}` when an action is received
35
36
  Sends a **event** back called `custom_rcv` containing a hash that looks like `{name: name, info: event}` when a custom event is received
37
+
38
+ Additionally, add a controller named `spec_blank_sends_context` that sends back the initialization context when it is created via the event named `context` with the initialization (context) parameters.
@@ -4,6 +4,7 @@ The hook module allows the client to receive hook_event messages. This is a requ
4
4
  ###Driver messages
5
5
  `if_hook_event(name, info)` - The name of the hook event and the information received.
6
6
 
7
- ####Driver spec functions (only required in debug mode)
8
- `if_hook_spec_dump_rcvd_events` - Schedules the event `if_hook_spec_dump_res` to be sent out with a message in the form of `[1, "if_hook_spec_dump_rcvd_events_res", [{name:name, info:info}, ...]}` where the array contains
9
- the parameters to all the calls to `if_hook_event` with the example hash and in sequential order where the oldest is the greatest index.
7
+ ###Spec requirements
8
+ When built for spec testing, the client should have two hook handlers for the hook events named `test` and `test2`.
9
+ These hooks should both respond with a interrupt named `hook_dump_res` with the info `{name: "XXXX", info: info}`
10
+ where the name is either `test` or `test2` and the info is the received payload.
@@ -1,5 +1,5 @@
1
1
  #Persist (persist.js)
2
- Persistance management. Loosely based on redis.
2
+ Persistance management. Loosely based on redis. Value's can be any javascript object.
3
3
 
4
4
  ###Driver messages
5
5
  `if_per_set(ns, key, value)` - Set a key and value
@@ -22,6 +22,7 @@
22
22
  `spec_one_spot` - "A blank view with with one spot named `content` that takes up the entire view"
23
23
  `spec_two_spot` - "A blank view with with one spot named `a` and one spot named `b`.
24
24
 
25
+
25
26
  ------
26
27
 
27
28
  ## Overview
@@ -4,7 +4,7 @@
4
4
  ##### There are two questions that *Hooks* were designed to solve.
5
5
 
6
6
  1. Animation segues. When a controller switches actions, often time it is swapping out view controllers. Adding animations at this time would be the optimal time to do so. However, different clients, take blackberry and iOS, may either have fully-automated-animations, gesture controlled animations, cancelable-animations, synchronous-blocking, and the list goes on-and-on. Clearly, animations are handled differently on each platform, so how do we allow each platform to manage animation-segues?
7
- 2. There are times when a client, e.g. chrome, need to support semantics that are platform dependent. *For example*, you may need the back button in your web client to trigger a view controller to go back a page. However, this semantic does not make much sense when you have multiple view controllers; e.g. What view controller receives the back clicked event?
7
+ 2. There are times when a client, e.g. android, need to support semantics that are platform dependent. *For example*, you may need the back button on your mobile phone to trigger a view controller to go back a page. However, this semantic does not make much sense when you have multiple view controllers; e.g. *(What view controller receives the back clicked event?)*
8
8
 
9
9
  ##### Additionally, these questions must be solved with these constraints
10
10
  1. Allowing the client to choose interception points at runtime would be prohibitively slow. Therefore, care should be taken to make sure hooks are as static as possible and do not add un-necessary runtime overhead.
@@ -12,12 +12,13 @@
12
12
  -------
13
13
 
14
14
  ##### How hooks solves these problems:
15
- *Hooks* solves these questions and constraitns by allowing the user to define triggers in their project under `./config/hooks.rb` using a *DSL* which in-turn notifies the client. The client then handles each hook in an appropriate fashion.
16
-
15
+ *Hooks* solves these questions and constraitns by allowing the user to define triggers in their project under `./config/hooks.rb` using a *DSL* which is used to create hook emitters at compile-time. These hook emitters send the hook notification to the client.
17
16
  ## Hooking by example with `./config/hooks.rb`
18
17
 
19
18
  **Here is an example of a `./config/hooks.rb` which tells flok to notify the client that the `supports_back_clicked` hook triggered whenever a controller instance of a `"my_controller"` controller switches to an action via Goto that has an event handler defined with `back_clicked`:**
20
19
 
20
+ Here is an example of a hook configuration file with *one* hook. The hook generator is called `goto`, you may think of the generators as a set of helper DSL functions that have there own unique parameters. The first line, `hook :goto => :supports_back_clicked` denotes that we are using the `goto` generator and the emitted event is called `:supports_back_clicked`.
21
+
21
22
  ```ruby
22
23
  # $USER_POJECT_ROOT/config/hooks.rb
23
24
  hook :goto => :supports_back_clicked do
@@ -25,21 +26,31 @@ hook :goto => :supports_back_clicked do
25
26
  to_action_responds_to? "back_clicked"
26
27
  controller_name "my_controller"
27
28
 
28
- #Configuration methods
29
- is_sync
29
+ #View hierarchy selectors
30
+ before_views({
31
+ "." => {
32
+ "__leaf__" => "foo"
33
+ }
34
+ })
30
35
  end
31
36
  ```
32
37
 
33
- **The basic format of each hook looks like:**
38
+ A break down of this code is:
39
+
34
40
  ```ruby
35
- hook :hook_generator_name => :hook_notification_name do
36
- #Selector methods & Configuration specific to the hook generator
41
+ hook :generator_name => :my_hook_name do
42
+ #Selectors To Hook on
43
+
44
+ #Various configuration
45
+
46
+ #View hierarchy to forward to hook
37
47
  end
38
48
  ```
39
49
 
40
50
  Selectors for each hook generator create more specific requirements for a trigger. By convention, all hook generators, if supplied no selector arguments will select all possible. **For example, this would hook all Goto statements in all controllers**:
51
+
41
52
  ```ruby
42
- hook :hook_generator_name => :hook_notification_name do
53
+ hook :goto => :all_goto do
43
54
  end
44
55
  ```
45
56
 
@@ -47,7 +58,8 @@ end
47
58
  ---
48
59
  The client, in-turn must handle the hook notification, in this example, the hook notification is named `supports_back_clicked`. The hook notifications typically includes a set of parameters, but the parameters are dependent on the *hook generator* used. In the above example, we are using the `goto` hook generator. See below for the different `hook generators` specifics.
49
60
 
50
- ** See client docs for how to handle hook notifications, here is an example in pseudo code: **
61
+ See client docs for how to handle hook notifications, here is an example in pseudo code:
62
+
51
63
  ```ruby
52
64
  handleHook("supports_back_clicked", function(hookInfo) {
53
65
  var to_action_name = hookInfo.to_action_name;
@@ -61,6 +73,14 @@ handleHook("supports_back_clicked", function(hookInfo) {
61
73
  * `to_action_responds_to? "event_name"` - Whenever a controller switches **to** an action that contains an `on "event_name", %{...}` handler
62
74
  * `from_action_responds_to? "event_name"` - Whenever a controller switches **from** an action that contains an `on "event_name", %{...}` handler
63
75
  * `controller "controller_name"` - Only applies to controllers with the name `"controller_name"`
76
+ * `Completion`
77
+ * The `completion` event is expected to be raised via `int_event` for all goto events. This is because the old view is not destroyed until the `completion` event is called
78
+ to allow you to use the view in animations.
79
+ * `Cancellation`:
80
+ * Unlike other semantics, `Goto` has issues with *cancellation* because the old views are destroyed. In order to work around this, you must call, after the completion
81
+ event, the `back` segue that you have described checking whether or not a transition was in progress (interactive) based on the view-controller itself.
82
+ * `info`:
83
+ * `ctp` - Completion telepointer, you should raise an `int_event` with this base-pointer.
64
84
 
65
85
  ## How the hook generators are defined & hooking internals
66
86
  See [Kernel Handbook | Hooks](../kernel_handbook/hooks.md)
@@ -70,7 +90,7 @@ The user's `./config/hooks.rb` file is compiled and then evaluated statically to
70
90
  and inserts code at particular hook detection points; the nature of the insertions is up to the particular hooking context.
71
91
 
72
92
  ##Synchronous vs Asynchronous Hooks
73
- Whether a hook is synchronous or not synchronous depends entirely on the type of hook in question. An example of a possible synchronous hook is a hook jkkkkkkkkkk..j
93
+ There are situations where you may want a synchronous hook. Examples include
74
94
 
75
95
  ##Hooking check points
76
96
  Each controller has a plethora of functions that determine it's lifetime and behaviours. These functions include the actions of creation, destruction, embedding, pushing actions, talking to services, etc.
@@ -83,14 +103,18 @@ the following JSON format:
83
103
  The name is the hook name and the params is context specific inforamtion the compiler has embedded. Live variables that are in the context of the hook detection point are described in each hook detection point below.
84
104
 
85
105
  * `controller_will_goto` - The controller is about to invoke the Goto macro and switch actions or it has just entered the first action from choose_action (which is a Goto).
86
- * params (static generated)
106
+ * params (static generated in the hook info)
87
107
  * `controller_name` - The controller name that this entry effects
88
108
  * `from_action` - The name of the action we are coming from
89
109
  * `to_action` - The name of the action we are going to
90
- * Useful (dynamic) variables
110
+ * Useful (dynamic/local JS) variables
91
111
  * `old_action` - The previous action, equal to `from_action` but in dynamic form. If there is no action, this is set to `choose_action`. Not sure why you would use this
92
112
  * `__info__.action` - The name of the new action
93
- * `${controller_name}_did_destroy` - The controller has *just* been destroyed
113
+ * `controller_did_goto` - The controller has completed the goto switch
114
+ * params (static)
115
+ * `controller_name` - The controller name that this entry effects
116
+ * `from_action` - The name of the action we are coming from
117
+ * `to_action` - The name of the action we are going to
94
118
 
95
119
  ##Hooks Compiler
96
120
  The hooks compiler is able to take the hook entry points and inject code into them via a set of `HooksManifestEntries` which are bound togeather via a `HooksManifest`. The actual
@@ -34,6 +34,8 @@ Gem::Specification.new do |spec|
34
34
  spec.add_runtime_dependency "rake", "~> 10.3"
35
35
  spec.add_runtime_dependency 'naturalsort', "~> 1.0"
36
36
  spec.add_runtime_dependency 'awesome_print', "~> 1.6"
37
+ spec.add_runtime_dependency 'file-tail', "~> 1.1"
37
38
  spec.add_development_dependency "therubyracer", "~> 0.12"
39
+ spec.add_runtime_dependency "xcpretty", "~> 0.2"
38
40
  spec.executables << 'flok'
39
41
  end
@@ -183,15 +183,29 @@ module Flok
183
183
  var old_action = __info__.action;
184
184
  __info__.action = "#{action_name}";
185
185
 
186
+ var __free_asap = true;
186
187
  //HOOK_ENTRY[controller_will_goto] #{{"controller_name" => @controller.name, "might_respond_to" => @ctx.might_respond_to, "actions_responds_to" => @ctx.actions_respond_to, "from_action" => @name, "to_action" => action_name}.to_json}
187
188
 
189
+ //If views are configured to not free right away, set up a new stack of views to free
190
+ //This is usually picked up by the hook GOTO
191
+ if (__free_asap === false) {
192
+ var views_to_free_id = gen_id();
193
+ views_to_free[views_to_free_id] = views_to_free[views_to_free_id] || [];
194
+ }
195
+
188
196
  //Remove all views, we don't have to recurse because removal of a view
189
197
  //is supposed to remove *all* view controllers of that tree as well.
190
198
  var embeds = __info__.embeds;
191
199
  for (var i = 0; i < __info__.embeds.length; ++i) {
192
200
  for (var j = 0; j < __info__.embeds[i].length; ++j) {
193
201
  //Free +1 because that will be the 'main' view
194
- main_q.push([1, "if_free_view", embeds[i][j]+1]);
202
+
203
+ //Free if 'free_asap' is not set, this is usually configured via the 'goto' hook
204
+ if (__free_asap === true) {
205
+ main_q.push([1, "if_free_view", embeds[i][j]+1]);
206
+ } else {
207
+ views_to_free[views_to_free_id].push(embeds[i][j]+1);
208
+ }
195
209
 
196
210
  //Call dealloc on the controller
197
211
  tel_deref(embeds[i][j]).cte.__dealloc__(embeds[i][j]);
@@ -223,6 +237,7 @@ module Flok
223
237
  //located in ctable
224
238
  __info__.cte.actions[__info__.action].on_entry(__base__)
225
239
 
240
+ //HOOK_ENTRY[controller_did_goto] #{{"controller_name" => @controller.name, "might_respond_to" => @ctx.might_respond_to, "actions_responds_to" => @ctx.actions_respond_to, "from_action" => @name, "to_action" => action_name}.to_json}
226
241
  //'choose_action' pseudo-action will be sent as 'null' as it's the initial state
227
242
  if (old_action === "choose_action") {
228
243
  old_action = null;
@@ -233,6 +248,7 @@ module Flok
233
248
  from: old_action,
234
249
  to: "#{action_name}"
235
250
  }]);
251
+
236
252
  }
237
253
  out.puts res
238
254
  elsif l =~ /Push/
@@ -589,7 +605,7 @@ module Flok
589
605
  self.instance_eval(&macro)
590
606
  @current_action = nil
591
607
  else
592
- raise "No macro found named: #{method}"
608
+ raise "No macro found named: #{method} for controller #{@controller.name} in action #{@name}"
593
609
  end
594
610
  end
595
611
  end
@@ -76,6 +76,7 @@ ctable = {
76
76
  var __info__ = tel_deref(__base__);
77
77
  var context = __info__.context;
78
78
  var current_action = __info__.action;
79
+ var push_count = __info__.stack.length;
79
80
 
80
81
  <%= e[:src] %>
81
82
  },
@@ -2,10 +2,12 @@ require_relative 'helpers'
2
2
 
3
3
  module Flok
4
4
  class GotoHooksDSLEnv
5
- attr_accessor :selectors
5
+ attr_accessor :selectors, :before_view_spider, :after_view_spider
6
6
 
7
7
  def initialize
8
8
  @selectors = []
9
+ @before_view_spider = {}
10
+ @after_view_spider = {}
9
11
  end
10
12
 
11
13
  def controller name
@@ -50,6 +52,14 @@ module Flok
50
52
  end
51
53
  end
52
54
  #################################################################################
55
+
56
+ def before_views spider
57
+ @before_view_spider = spider
58
+ end
59
+
60
+ def after_views spider
61
+ @after_view_spider = spider
62
+ end
53
63
  end
54
64
 
55
65
  UserHooksToManifestOrchestrator.register_hook_gen :goto do |manifest, params|
@@ -61,14 +71,32 @@ module Flok
61
71
  dsl_env = GotoHooksDSLEnv.new
62
72
  dsl_env.instance_eval(&block)
63
73
 
74
+ ns = "_#{SecureRandom.hex[0..5]}"
75
+
64
76
  #Inject into HOOK_ENTRY[controller_will_goto] that match the given selectors from the DSL
65
77
  #based on the hook entry static parameters
66
- entry = HooksManifestEntry.new("controller_will_goto", dsl_env.selectors) do |entry_hook_params|
78
+ manifest << HooksManifestEntry.new("controller_will_goto", dsl_env.selectors) do |entry_hook_params|
67
79
  next %{
68
- SEND("main", "if_hook_event", "#{hook_event_name}", {});
80
+ var #{ns}_before_views = find_view(__base__, #{dsl_env.before_view_spider.to_json});
81
+ __free_asap = false;
69
82
  }
70
83
  end
71
84
 
72
- manifest << entry
85
+ manifest << HooksManifestEntry.new("controller_did_goto", dsl_env.selectors) do |entry_hook_params|
86
+ next %{
87
+ //The completion callback will share a pointer to the views_to_free key index
88
+ reg_evt(views_to_free_id, hook_goto_completion_cb);
89
+
90
+ var #{ns}_after_views = find_view(__base__, #{dsl_env.after_view_spider.to_json});
91
+ var #{ns}_info = {
92
+ views: #{ns}_after_views,
93
+ cep: views_to_free_id
94
+ };
95
+ for (var k in #{ns}_before_views) {
96
+ #{ns}_info.views[k] = #{ns}_before_views[k];
97
+ }
98
+ SEND("main", "if_hook_event", "#{hook_event_name}", #{ns}_info);
99
+ }
100
+ end
73
101
  end
74
102
  end
@@ -1,3 +1,3 @@
1
1
  module Flok
2
- VERSION = "0.0.102"
2
+ VERSION = "0.0.103"
3
3
  end
@@ -27,6 +27,10 @@ shared_context "iface:driver" do
27
27
  include SpecHelpers
28
28
  before(:each) do
29
29
  @pipe = IO.popen("rake pipe:driver", "r+")
30
+ wait_for_load = @pipe.readline
31
+ $stderr.puts "waiting for LOADED from pipe"
32
+ raise "Your pipe should have emitted LOADED at the start when it was ready, instead got #{wait_for_load.inspect}" if wait_for_load.strip != "LOADED"
33
+ $stderr.puts "got LOADED from pipe"
30
34
  @pid = @pipe.pid
31
35
  @mods = Flok::Platform.mods ENV['FLOK_ENV']
32
36
  end
@@ -47,6 +51,10 @@ def mods
47
51
  Flok::Platform.mods ENV['FLOK_ENV']
48
52
  end
49
53
 
54
+ def defines
55
+ Flok::Platform.defines ENV['FLOK_ENV']
56
+ end
57
+
50
58
  def config_yml
51
59
  Flok::Platform.config_yml ENV['FLOK_ENV']
52
60
  end
@@ -58,6 +66,20 @@ def module_dep name
58
66
  end
59
67
  end
60
68
 
69
+ #Similar to module_dep this checks for a defines as well, useful for optional specs
70
+ def module_dep_defines(name:, defines:)
71
+ before(:each) do
72
+ if mods.include? name
73
+ unless (Flok::Platform.defines ENV["FLOK_ENV"])[defines]
74
+ skip "#{ENV["PLATFORM"].inspect} does support #{name.inspect} module but does not define #{defines} in config.yml"
75
+ end
76
+ else
77
+ skip "#{ENV["PLATFORM"].inspect} does not support #{name.inspect} module in config.yml"
78
+ end
79
+ end
80
+ end
81
+
82
+
61
83
  #Require a key value to be a apart of the config yml
62
84
  def settings_dep key, value
63
85
  raise "#{ENV["PLATFORM"].inspect} does not support #{key.inspect} configuration in config.yml" unless config_yml.include? key
@@ -78,3 +100,19 @@ def restart_driver_but_persist
78
100
  raise "Tried to restart driver but timed out waiting for 'RESTART OK'"
79
101
  end
80
102
  end
103
+
104
+ #Do not persist
105
+ def hard_restart_driver
106
+ #Ensure the pipe is fully drained before sending RESTART
107
+ @pipe.puts [[0, 0, "ping"]].to_json; @pipe.readline_timeout
108
+
109
+ @pipe.puts "HARD_RESTART"
110
+ begin
111
+ Timeout::timeout(10) do
112
+ expect(@pipe.readline).to eq("RESTART OK\n")
113
+ end
114
+ rescue Timeout::Error
115
+ raise "Tried to HARD restart driver but timed out waiting for 'RESTART OK'"
116
+ end
117
+ end
118
+
@@ -177,9 +177,13 @@ shared_context "kern" do
177
177
  #for non-existant messages
178
178
  def ignore_up_to msg_name, priority=nil, &block
179
179
  @did_get = []
180
+ @original_q = @q == nil ? nil : JSON.parse(@q.to_json)
181
+ @original_cq = @cq == nil ? nil : JSON.parse(@cq.to_json)
180
182
 
181
183
  loop do
182
184
  if @q.count == 0 and @cq.count == 0
185
+ @q = @original_q
186
+ @cq = @original_cq
183
187
  raise "Waited for the message #{msg_name.inspect} but never got it... did get: \n * #{@did_get.join("\n * ")}"
184
188
  end
185
189
  #Dequeue from multi-priority queue if possible
@@ -16,18 +16,18 @@ RSpec.describe "iface:driver:controller" do
16
16
  end
17
17
 
18
18
  #Create a controller with the given base pointer and attach view to a spot pointer (rspace)
19
- def init_controller(bp, view_name="spec_blank", rspace=0)
19
+ def init_controller(bp, view_name="spec_blank", rspace=0, context={})
20
20
  #Views are always bp+1, the bp points to an array with ['vc', 'main', spots...]
21
21
  # bp bp+1 bp+n
22
22
  #Create a new view 'spec_blank' at bp+1
23
- if view_name == "spec_blank"
23
+ if view_name == "spec_blank" || view_name == "spec_blank_sends_context"
24
24
  @pipe.puts [[0, 4, "if_init_view", view_name, {}, bp+1, ["main"]]].to_json
25
25
  elsif view_name == "spec_one_spot"
26
26
  @pipe.puts [[0, 4, "if_init_view", view_name, {}, bp+1, ["main", "content"]]].to_json
27
27
  end
28
28
 
29
29
  #Initilaize the view controller
30
- @pipe.puts [[0, 4, "if_controller_init", bp, bp+1, "__test__", {}]].to_json
30
+ @pipe.puts [[0, 4, "if_controller_init", bp, bp+1, view_name, context]].to_json
31
31
 
32
32
  #Attach that view to the root
33
33
  @pipe.puts [[0, 2, "if_attach_view", bp+1, rspace]].to_json
@@ -57,17 +57,36 @@ RSpec.describe "iface:driver:controller" do
57
57
  expect(@pipe).to readline_and_equal_json_x_within_y_seconds([3, "int_event", bp, "action_rcv", {"from" => nil, "to" => to_action}], 6.seconds)
58
58
  end
59
59
 
60
- it "Does send back a message for the test controller custom action" do
60
+ it "Does send back a message for the test controller custom action named 'foo'" do
61
61
  bp = 332
62
62
  init_controller(bp)
63
63
 
64
64
  #Send an action event
65
- custom_name = SecureRandom.hex
65
+ custom_name = "foo"
66
66
  custom_info = {"info" => SecureRandom.hex}
67
67
  send_event(bp, custom_name, custom_info)
68
68
  expect(@pipe).to readline_and_equal_json_x_within_y_seconds([3, "int_event", bp, "custom_rcv", {"name" => custom_name, "info" => custom_info}], 6.seconds)
69
69
  end
70
70
 
71
+ it "Does send back a message for the test controller custom action named 'bar'" do
72
+ bp = 332
73
+ init_controller(bp)
74
+
75
+ #Send an action event
76
+ custom_name = "bar"
77
+ custom_info = {"info" => SecureRandom.hex}
78
+ send_event(bp, custom_name, custom_info)
79
+ expect(@pipe).to readline_and_equal_json_x_within_y_seconds([3, "int_event", bp, "custom_rcv", {"name" => custom_name, "info" => custom_info}], 6.seconds)
80
+ end
81
+
82
+ it "Does receive the context on initialization" do
83
+ bp = 332
84
+ init_controller(bp, "spec_blank_sends_context", 0, {"foo" => "bar"})
85
+
86
+ expect(@pipe).to readline_and_equal_json_x_within_y_seconds([3, "int_event", bp, "context", {"foo" => "bar"}], 6.seconds)
87
+ end
88
+
89
+
71
90
  it "When a view is deleted that was associated with a view controller, the view controller should no longer exist" do
72
91
  bp = 332
73
92
  init_controller(bp)
@@ -91,7 +110,7 @@ RSpec.describe "iface:driver:controller" do
91
110
  expect(@pipe).to readline_and_equal_json_x_within_y_seconds([1, "spec", [bp0, bp1]], 6.seconds)
92
111
  end
93
112
 
94
- it "Can initialize a controller that embeds a controller and delete all supb-controllers when a parent view is destroyed" do
113
+ it "Can initialize a controller that embeds a controller and delete all sub-controllers when a parent view is destroyed" do
95
114
  bp0 = 332
96
115
  bp1 = 335
97
116
  init_controller(bp0, "spec_one_spot")