bluesky 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/bluesky/application.rb +44 -40
- data/lib/bluesky/{dom_helper.rb → helpers.rb} +27 -0
- data/lib/bluesky/navigation_controller.rb +42 -14
- data/lib/bluesky/pure_component.rb +3 -35
- data/lib/bluesky/version.rb +1 -1
- data/lib/bluesky/view_controller.rb +14 -7
- data/lib/bluesky.rb +1 -2
- data/test/navigation_controller_test.rb +161 -0
- data/test/test_helper.rb +74 -0
- data/test/view_controller_test.rb +29 -0
- metadata +15 -42
- data/lib/bluesky/try.rb +0 -100
- data/spec/bluesky_spec.rb +0 -34
- data/spec/spec_helper.rb +0 -109
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7e338bf2cf158cd50ad10a1b536b79a7353ac3f8
|
4
|
+
data.tar.gz: 3b58b50347ebcdc8372823f589e477865766e0ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 98722b63ffc517414e6d89f23802861a0d6665ed752e0130ceb1c8ad349cb8a1d6fd2396f7e2863849d2650b4eaff1c56dd578fb292fc95d0555aff518f534d0
|
7
|
+
data.tar.gz: dd6b122929fefd65889d58d75ad7106191c21a8338b7f2b1d6249f099a7d72aeab4cfd1293e200dc81e3df202ddf8c83ba4d5098e45e2358d0786c7f8601298c
|
data/lib/bluesky/application.rb
CHANGED
@@ -1,11 +1,16 @@
|
|
1
1
|
module Bluesky
|
2
2
|
|
3
|
-
#
|
3
|
+
# The application entry point
|
4
4
|
class Application
|
5
5
|
|
6
6
|
include DOMHelper
|
7
|
+
include TryHelper
|
7
8
|
|
8
|
-
|
9
|
+
# Top level ViewController
|
10
|
+
attr_accessor :root_view_controller
|
11
|
+
|
12
|
+
# Receives all notifications
|
13
|
+
attr_accessor :delegate
|
9
14
|
|
10
15
|
def initialize
|
11
16
|
@dispatch_queue = []
|
@@ -16,84 +21,83 @@ module Bluesky
|
|
16
21
|
@debug
|
17
22
|
end
|
18
23
|
|
24
|
+
# Turns on debug logging
|
19
25
|
def debug!
|
20
26
|
@debug = true
|
21
|
-
@clearwater.debug! if @clearwater
|
22
|
-
@delegate = DebugDelegate.new(@delegate)
|
23
27
|
self
|
24
28
|
end
|
25
29
|
|
30
|
+
# Dispatches an action on target
|
31
|
+
#
|
32
|
+
# Attributes:
|
33
|
+
# target: (Object) The object that receives the send action
|
34
|
+
# action (Symbol) The method symbol on target
|
35
|
+
# payload: (Array) The arguments passed to send if any
|
36
|
+
# block: (Block) Optional block that will be passed as argument to send
|
26
37
|
def dispatch(target, action, *payload, &block)
|
27
38
|
promise = Promise.new
|
28
|
-
|
39
|
+
notify(self, :dispatch_requested, target, action, *payload)
|
29
40
|
@dispatch_queue << lambda do
|
30
41
|
begin
|
31
42
|
result = target.send(action, *payload, &block)
|
32
43
|
promise.resolve(result).then { refresh }
|
33
|
-
|
44
|
+
notify(self, :dispatch_resolved, target, action, *payload, result)
|
34
45
|
rescue => err
|
35
46
|
promise.reject(err)
|
36
|
-
|
47
|
+
notify(self, :dispatch_rejected, target, action, *payload, err)
|
37
48
|
end
|
38
49
|
end
|
39
50
|
defer { process_dispatch_queue }
|
40
51
|
promise
|
41
52
|
end
|
42
53
|
|
43
|
-
def process_dispatch_queue
|
44
|
-
return if @dispatch_queue.empty?
|
45
|
-
@dispatch_queue.delete_if do |task|
|
46
|
-
task.call
|
47
|
-
true
|
48
|
-
end
|
49
|
-
end
|
50
54
|
|
55
|
+
# Notifies the delegate about an event
|
56
|
+
#
|
57
|
+
# Attributes:
|
58
|
+
# source: (Object) The object that send the event
|
59
|
+
# event: (Symbol) The event symbol
|
60
|
+
# payload: (Array) Additional arguments to pass along
|
51
61
|
def notify(source, event, *payload)
|
52
|
-
@delegate
|
62
|
+
try(@delegate, source, event, *payload)
|
63
|
+
puts "#{event} #{payload}" if debug?
|
53
64
|
end
|
54
65
|
|
66
|
+
# Refreshes (runs render) on the root_view_controller and invokes the block
|
67
|
+
# (if any) when the render is complete.
|
55
68
|
def refresh(&block)
|
56
69
|
promise = Promise.new
|
57
70
|
@clearwater.call { promise.resolve }
|
58
71
|
block ? promise.then(&block) : promise
|
59
72
|
end
|
60
73
|
|
74
|
+
# Does the required wiring and runs the initial render
|
61
75
|
def run
|
62
76
|
raise 'root_view_controller must be defined in Application' unless root_view_controller
|
63
77
|
PureComponent.install_hooks(debug?)
|
64
78
|
root_view_controller.parent = self
|
65
|
-
|
79
|
+
router = RUBY_ENGINE != 'opal' ?
|
80
|
+
Clearwater::Router.new(location: 'http://localhost:9292/') :
|
81
|
+
Clearwater::Router.new
|
82
|
+
@clearwater = Clearwater::Application.new(
|
83
|
+
component: root_view_controller,
|
84
|
+
router: router
|
85
|
+
)
|
66
86
|
@clearwater.debug! if debug?
|
67
87
|
root_view_controller.begin_appearance_transition(true)
|
68
88
|
refresh { root_view_controller.end_appearance_transition() }
|
69
89
|
self
|
70
90
|
end
|
71
91
|
|
72
|
-
|
73
|
-
|
74
|
-
class DebugDelegate
|
75
|
-
|
76
|
-
def initialize(delegate = nil)
|
77
|
-
@delegate = delegate
|
78
|
-
end
|
79
|
-
def dispatch(target, action, *payload)
|
80
|
-
@delegate.try(:dispatch, target, action, *payload)
|
81
|
-
puts "[DISPATCH] #{action} on #{target}"
|
82
|
-
end
|
83
|
-
|
84
|
-
def dispatch_resolved(target, action, *payload, result)
|
85
|
-
@delegate.try(:dispatch_resolved, result, target, action, *payload)
|
86
|
-
puts "[RESOLVED] #{action} on #{target} yielding #{result}"
|
87
|
-
end
|
88
|
-
|
89
|
-
def dispatch_rejected(target, action, *payload, error)
|
90
|
-
@delegate.try(:dispatch_rejected, target, action, *payload, error)
|
91
|
-
puts "[REJECTED] #{action} on #{target}"
|
92
|
-
warn error
|
93
|
-
end
|
92
|
+
private
|
94
93
|
|
95
|
-
|
96
|
-
|
94
|
+
# Processes queued dispatches
|
95
|
+
def process_dispatch_queue
|
96
|
+
return if @dispatch_queue.empty?
|
97
|
+
@dispatch_queue.delete_if do |task|
|
98
|
+
task.call
|
99
|
+
true
|
100
|
+
end
|
97
101
|
end
|
98
102
|
|
99
103
|
end
|
@@ -34,4 +34,31 @@ module Bluesky
|
|
34
34
|
end
|
35
35
|
|
36
36
|
end
|
37
|
+
|
38
|
+
module ConditionHelper
|
39
|
+
|
40
|
+
extend self
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def pre(&block)
|
45
|
+
raise 'Precondition is violated' unless block.call
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
module TryHelper
|
51
|
+
|
52
|
+
extend self
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
def try(target, *a, &b)
|
57
|
+
return nil if target.respond_to?(:nil?) && target.nil?
|
58
|
+
return target.public_send(*a, &b) if target.respond_to?(:public_send)
|
59
|
+
return nil
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
37
64
|
end
|
@@ -1,14 +1,14 @@
|
|
1
|
+
require_relative './view_controller'
|
2
|
+
|
1
3
|
module Bluesky
|
2
4
|
|
3
5
|
# Bluesky::NavigationController
|
4
6
|
class NavigationController < ViewController
|
5
7
|
|
6
|
-
attr_accessor :root_view_contrller
|
7
|
-
|
8
8
|
def initialize(root_view_controller)
|
9
9
|
raise 'NavigationController requires a root_view_controller' unless root_view_controller
|
10
10
|
super
|
11
|
-
|
11
|
+
@children = [root_view_controller]
|
12
12
|
end
|
13
13
|
|
14
14
|
def view
|
@@ -16,18 +16,22 @@ module Bluesky
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def view_will_appear
|
19
|
+
super
|
19
20
|
top_view_controller.begin_appearance_transition(true)
|
20
21
|
end
|
21
22
|
|
22
23
|
def view_did_appear
|
24
|
+
super
|
23
25
|
top_view_controller.end_appearance_transition
|
24
26
|
end
|
25
27
|
|
26
28
|
def view_will_disappear
|
27
|
-
|
29
|
+
super
|
30
|
+
top_view_controller.begin_appearance_transition(false)
|
28
31
|
end
|
29
32
|
|
30
33
|
def view_did_disappear
|
34
|
+
super
|
31
35
|
top_view_controller.end_appearance_transition
|
32
36
|
end
|
33
37
|
|
@@ -43,32 +47,56 @@ module Bluesky
|
|
43
47
|
top_view_controller
|
44
48
|
end
|
45
49
|
|
50
|
+
def view_controllers
|
51
|
+
@children
|
52
|
+
end
|
53
|
+
|
54
|
+
def view_controllers=(controllers)
|
55
|
+
index = @children.index(controllers.last)
|
56
|
+
pop_to_root_view_controller(controllers.last) unless index.nil?
|
57
|
+
@children.replace(controllers)
|
58
|
+
end
|
59
|
+
|
46
60
|
def push_view_controller(view_controller)
|
47
61
|
old_view_controller = top_view_controller
|
48
62
|
old_view_controller.begin_appearance_transition(false)
|
49
|
-
view_controller.begin_appearance_transition(
|
63
|
+
view_controller.begin_appearance_transition(@appearance == :appeared)
|
50
64
|
add_child_view_controller(view_controller)
|
51
65
|
force_update do
|
52
66
|
view_controller.end_appearance_transition
|
53
67
|
old_view_controller.end_appearance_transition
|
54
68
|
end
|
69
|
+
return
|
55
70
|
end
|
56
71
|
|
57
72
|
def pop_view_controller
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
top_view_controller.begin_appearance_transition(
|
73
|
+
pre { top_view_controller != root_view_controller }
|
74
|
+
popped_view_controller = top_view_controller
|
75
|
+
popped_view_controller.begin_appearance_transition(false)
|
76
|
+
popped_view_controller.remove_from_parent_view_controller()
|
77
|
+
top_view_controller.begin_appearance_transition(@appearance == :appeared)
|
63
78
|
force_update do
|
64
|
-
top_view_controller.end_appearance_transition
|
65
|
-
|
79
|
+
top_view_controller.end_appearance_transition()
|
80
|
+
popped_view_controller.end_appearance_transition()
|
66
81
|
end
|
82
|
+
return popped_view_controller
|
67
83
|
end
|
68
84
|
|
69
85
|
def pop_to_view_controller(view_controller)
|
70
|
-
|
71
|
-
|
86
|
+
index = @children.index(view_controller)
|
87
|
+
count = index.nil? ? 0 : index + 1
|
88
|
+
removed = @children[count..-1]
|
89
|
+
@children = @children[0...count]
|
90
|
+
removed.each { |child| child.begin_appearance_transition(false) }
|
91
|
+
top_view_controller.begin_appearance_transition(@appearance == :appeared)
|
92
|
+
force_update do
|
93
|
+
top_view_controller.end_appearance_transition()
|
94
|
+
removed.each do |child|
|
95
|
+
child.parent = nil
|
96
|
+
child.end_appearance_transition()
|
97
|
+
end
|
98
|
+
end
|
99
|
+
return removed
|
72
100
|
end
|
73
101
|
|
74
102
|
def pop_to_root_view_controller
|
@@ -1,40 +1,8 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
|
4
|
-
module DSL
|
5
|
-
|
6
|
-
module_function
|
7
|
-
|
8
|
-
Clearwater::Component::HTML_TAGS.each do |tag_name|
|
9
|
-
define_method(tag_name) do |attributes, content|
|
10
|
-
%x{
|
11
|
-
if(!(attributes === nil || attributes.$$is_hash)) {
|
12
|
-
content = attributes;
|
13
|
-
attributes = nil;
|
14
|
-
}
|
15
|
-
}
|
16
|
-
|
17
|
-
tag(tag_name, attributes, content)
|
18
|
-
end
|
19
|
-
end
|
2
|
+
require 'clearwater'
|
3
|
+
require_relative './dsl'
|
20
4
|
|
21
|
-
|
22
|
-
|
23
|
-
if block
|
24
|
-
attributes ||= {}
|
25
|
-
content ||= []
|
26
|
-
block.call(NodeBuilder.new(tag_name, attributes, content))
|
27
|
-
end
|
28
|
-
|
29
|
-
Clearwater::VirtualDOM.node(
|
30
|
-
tag_name,
|
31
|
-
Clearwater::Component.sanitize_attributes(attributes),
|
32
|
-
Clearwater::Component.sanitize_content(content)
|
33
|
-
)
|
34
|
-
end
|
35
|
-
|
36
|
-
|
37
|
-
end
|
5
|
+
module Bluesky
|
38
6
|
|
39
7
|
# A presentation `Component`
|
40
8
|
class PureComponent
|
data/lib/bluesky/version.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
require 'clearwater'
|
2
|
+
require_relative './helpers'
|
3
|
+
require_relative './dsl'
|
4
|
+
|
1
5
|
module Bluesky
|
2
6
|
|
3
7
|
class ViewController
|
@@ -5,6 +9,7 @@ module Bluesky
|
|
5
9
|
include Clearwater::Component
|
6
10
|
include DOMHelper
|
7
11
|
include DSL
|
12
|
+
include ConditionHelper
|
8
13
|
|
9
14
|
def self.attribute(name, *args, &block)
|
10
15
|
case args.length
|
@@ -29,7 +34,7 @@ module Bluesky
|
|
29
34
|
end
|
30
35
|
end
|
31
36
|
|
32
|
-
attr_accessor :children, :parent, :data
|
37
|
+
attr_accessor :children, :parent, :data, :appearance
|
33
38
|
|
34
39
|
def initialize(*_, children: [], parent: nil, data: {})
|
35
40
|
@children = children
|
@@ -53,20 +58,22 @@ module Bluesky
|
|
53
58
|
end
|
54
59
|
|
55
60
|
def dispatch(target, action, *payload, &block)
|
56
|
-
|
61
|
+
try(parent, :dispatch, target, action, *payload, &block)
|
57
62
|
end
|
58
63
|
|
59
64
|
def notify(source, event, *payload)
|
60
|
-
|
65
|
+
try(parent, :notify, source, event, *payload)
|
61
66
|
end
|
62
67
|
|
63
68
|
def begin_appearance_transition(appearing)
|
64
69
|
if appearing
|
65
|
-
|
70
|
+
return unless @appearance == :disappeared
|
71
|
+
# raise "Invalid appearance #{@appearance} when appearing" if @appearance != :disappeared
|
66
72
|
@appearance = :appearing
|
67
73
|
view_will_appear()
|
68
74
|
else
|
69
|
-
|
75
|
+
return unless @appearance == :appeared
|
76
|
+
# raise "Invalid appearance #{@appearance} when disappearing" if @appearance != :appeared
|
70
77
|
@appearance = :disappearing
|
71
78
|
view_will_disappear()
|
72
79
|
end
|
@@ -81,7 +88,7 @@ module Bluesky
|
|
81
88
|
@appearance = :disappeared
|
82
89
|
view_did_disappear()
|
83
90
|
else
|
84
|
-
raise "Invalid appearance #{@appearance} when transitioning"
|
91
|
+
# raise "Invalid appearance #{@appearance} when transitioning"
|
85
92
|
end
|
86
93
|
end
|
87
94
|
|
@@ -112,7 +119,7 @@ module Bluesky
|
|
112
119
|
|
113
120
|
def navigation_controller
|
114
121
|
parent.is_a?(NavigationController) ? parent :
|
115
|
-
|
122
|
+
try(parent, :navigation_controller)
|
116
123
|
end
|
117
124
|
|
118
125
|
# Callbacks
|
data/lib/bluesky.rb
CHANGED
@@ -35,8 +35,7 @@ module Clearwater
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
require_relative 'bluesky/
|
39
|
-
require_relative 'bluesky/dom_helper.rb'
|
38
|
+
require_relative 'bluesky/helpers.rb'
|
40
39
|
require_relative 'bluesky/node_builder.rb'
|
41
40
|
require_relative 'bluesky/pure_component.rb'
|
42
41
|
require_relative 'bluesky/view_controller.rb'
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
# See test_helper for #appeared?, #disappeared? and #reset!
|
4
|
+
# test_helper also defines a mock application as parent for all new instances
|
5
|
+
module TestNavigationController
|
6
|
+
|
7
|
+
class Constructed < Minitest::Test
|
8
|
+
|
9
|
+
include Bluesky
|
10
|
+
|
11
|
+
def setup
|
12
|
+
@subject = NavigationController.new(ViewController.new)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_appearing_it_appears
|
16
|
+
refute @subject.appeared?
|
17
|
+
@subject.begin_appearance_transition(true)
|
18
|
+
@subject.end_appearance_transition()
|
19
|
+
assert @subject.appeared?
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_adding_child_updates_top_view_controller
|
23
|
+
child = ViewController.new
|
24
|
+
@subject.push_view_controller(child)
|
25
|
+
assert_equal child, @subject.top_view_controller
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_pop_view_controller_throws
|
29
|
+
assert_raises { @subject.pop_view_controller() }
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
class Appeared < Minitest::Test
|
35
|
+
|
36
|
+
include Bluesky
|
37
|
+
|
38
|
+
def setup
|
39
|
+
@subject = NavigationController.new(ViewController.new)
|
40
|
+
@subject.begin_appearance_transition(true)
|
41
|
+
@subject.end_appearance_transition()
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_when_disappearing_it_disappears
|
45
|
+
refute @subject.disappeared?
|
46
|
+
@subject.begin_appearance_transition(false)
|
47
|
+
@subject.end_appearance_transition()
|
48
|
+
assert @subject.disappeared?
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_when_appearing_nothing_happens
|
52
|
+
@subject.reset!
|
53
|
+
@subject.begin_appearance_transition(true)
|
54
|
+
@subject.end_appearance_transition()
|
55
|
+
refute @subject.appeared?
|
56
|
+
refute @subject.disappeared?
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_adding_child_then_child_appears
|
60
|
+
child = ViewController.new
|
61
|
+
@subject.push_view_controller(child)
|
62
|
+
assert child.appeared?
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
class WithChild < Minitest::Test
|
68
|
+
|
69
|
+
include Bluesky
|
70
|
+
|
71
|
+
def setup
|
72
|
+
@subject = NavigationController.new(ViewController.new)
|
73
|
+
@child = ViewController.new
|
74
|
+
@subject.push_view_controller(@child)
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_pop_view_controller_returns_child
|
78
|
+
assert_equal @child, @subject.pop_view_controller()
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_appearing_shows_both_subject_and_child_but_not_root_view_controller
|
82
|
+
@subject.begin_appearance_transition(true)
|
83
|
+
@subject.end_appearance_transition()
|
84
|
+
|
85
|
+
assert @subject.appeared?
|
86
|
+
assert @child.appeared?
|
87
|
+
refute @subject.root_view_controller.appeared?
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_moveing_reparents_child
|
91
|
+
target = NavigationController.new(ViewController.new)
|
92
|
+
target.push_view_controller(@child)
|
93
|
+
assert_equal target, @child.parent
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
class WithChildren < Minitest::Test
|
100
|
+
|
101
|
+
include Bluesky
|
102
|
+
|
103
|
+
def setup
|
104
|
+
@subject = NavigationController.new(ViewController.new)
|
105
|
+
@children = [ViewController.new, ViewController.new, ViewController.new]
|
106
|
+
@children.each { |child| @subject.push_view_controller(child) }
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_pop_view_controller_returns_last_child
|
110
|
+
last = @children.last
|
111
|
+
assert_equal last, @subject.pop_view_controller()
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_appearing_only_shows_last_child
|
115
|
+
@subject.begin_appearance_transition(true)
|
116
|
+
@subject.end_appearance_transition()
|
117
|
+
last = @children.pop
|
118
|
+
assert last.appeared?
|
119
|
+
refute @subject.root_view_controller.appeared?
|
120
|
+
refute @children.any? &:appeared?
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_pop_to_root_removes_children
|
124
|
+
assert_equal @children, @subject.pop_to_root_view_controller()
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
class WithChildrenAndAppeared < Minitest::Test
|
130
|
+
|
131
|
+
include Bluesky
|
132
|
+
|
133
|
+
def setup
|
134
|
+
@subject = NavigationController.new(ViewController.new)
|
135
|
+
@children = [ViewController.new, ViewController.new, ViewController.new]
|
136
|
+
@children.each { |child| @subject.push_view_controller(child) }
|
137
|
+
@subject.begin_appearance_transition(true)
|
138
|
+
@subject.end_appearance_transition()
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_pop_to_root_hides_last_child
|
142
|
+
@subject.pop_to_root_view_controller()
|
143
|
+
assert @children.last.disappeared?
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_pop_to_root_shows_root_view_controller
|
147
|
+
@subject.pop_to_root_view_controller()
|
148
|
+
assert @subject.root_view_controller.appeared?
|
149
|
+
end
|
150
|
+
|
151
|
+
def test_move_top_view_controller_to_hidden_parent_hides_it
|
152
|
+
child = @subject.top_view_controller
|
153
|
+
target = NavigationController.new(ViewController.new)
|
154
|
+
target.push_view_controller(child)
|
155
|
+
assert child.disappeared?
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'minitest/spec'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'bluesky/view_controller'
|
4
|
+
require 'bluesky/navigation_controller'
|
5
|
+
|
6
|
+
class MockApplication < Bluesky::ViewController
|
7
|
+
|
8
|
+
def refresh
|
9
|
+
yield if block_given?
|
10
|
+
end
|
11
|
+
|
12
|
+
def force_update
|
13
|
+
yield if block_given?
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
class Bluesky::ViewController
|
19
|
+
|
20
|
+
def index
|
21
|
+
@index ||= 0
|
22
|
+
@index += 1
|
23
|
+
end
|
24
|
+
|
25
|
+
def view_will_appear
|
26
|
+
@will_appear = index
|
27
|
+
end
|
28
|
+
|
29
|
+
def view_did_appear
|
30
|
+
@did_appear = index
|
31
|
+
end
|
32
|
+
|
33
|
+
def view_will_disappear
|
34
|
+
@will_disappear = index
|
35
|
+
end
|
36
|
+
|
37
|
+
def view_did_disappear
|
38
|
+
@did_disappear = index
|
39
|
+
end
|
40
|
+
|
41
|
+
def appeared?
|
42
|
+
!!(@will_appear && @did_appear && @will_appear < @did_appear &&
|
43
|
+
(@will_disappear ? @will_appear > @will_disappear : true) &&
|
44
|
+
(@did_disappear ? @did_appear > @did_disappear : true))
|
45
|
+
end
|
46
|
+
|
47
|
+
def disappeared?
|
48
|
+
!!(@will_disappear && @did_disappear && @will_disappear < @did_disappear &&
|
49
|
+
(@will_appear ? @will_disappear > @will_appear : true) &&
|
50
|
+
(@did_appear ? @did_disappear > @did_appear : true))
|
51
|
+
end
|
52
|
+
|
53
|
+
def reset!
|
54
|
+
@will_appear = @did_appear = @will_disappear = @did_disappear = false
|
55
|
+
@index = 0
|
56
|
+
end
|
57
|
+
|
58
|
+
def dump
|
59
|
+
puts "will_appear == #{@will_appear}"
|
60
|
+
puts "did_appear == #{@did_appear}"
|
61
|
+
puts "will_disappear == #{@will_disappear}"
|
62
|
+
puts "did_disappear == #{@did_disappear}"
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
class Bluesky::NavigationController
|
68
|
+
def self.new *args
|
69
|
+
instance = super
|
70
|
+
instance.parent = MockApplication.new
|
71
|
+
instance
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
# See test_helper for #appeared?, #disappeared? and #reset!
|
4
|
+
module TestViewController
|
5
|
+
|
6
|
+
class Constructed < Minitest::Test
|
7
|
+
|
8
|
+
include Bluesky
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@subject = ViewController.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_appearing_it_appears
|
15
|
+
@subject.begin_appearance_transition(true)
|
16
|
+
@subject.end_appearance_transition()
|
17
|
+
assert @subject.appeared?
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_add_child_view_controller_reparents_child
|
21
|
+
child = ViewController.new
|
22
|
+
assert_nil child.parent
|
23
|
+
@subject.add_child_view_controller(child)
|
24
|
+
assert_equal @subject, child.parent
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bluesky
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Susi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: opal
|
@@ -39,48 +39,20 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 1.0.0.rc4
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: minitest
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: rspec
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: rake
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
45
|
+
- - "~>"
|
74
46
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
47
|
+
version: '5.10'
|
76
48
|
type: :development
|
77
49
|
prerelease: false
|
78
50
|
version_requirements: !ruby/object:Gem::Requirement
|
79
51
|
requirements:
|
80
|
-
- - "
|
52
|
+
- - "~>"
|
81
53
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
83
|
-
description: An app framework
|
54
|
+
version: '5.10'
|
55
|
+
description: An app framework built on top of opal and clearwater
|
84
56
|
email: john@susi.se
|
85
57
|
executables:
|
86
58
|
- bluesky
|
@@ -90,14 +62,14 @@ files:
|
|
90
62
|
- bin/bluesky
|
91
63
|
- lib/bluesky.rb
|
92
64
|
- lib/bluesky/application.rb
|
93
|
-
- lib/bluesky/
|
65
|
+
- lib/bluesky/helpers.rb
|
94
66
|
- lib/bluesky/navigation_controller.rb
|
95
67
|
- lib/bluesky/pure_component.rb
|
96
|
-
- lib/bluesky/try.rb
|
97
68
|
- lib/bluesky/version.rb
|
98
69
|
- lib/bluesky/view_controller.rb
|
99
|
-
-
|
100
|
-
-
|
70
|
+
- test/navigation_controller_test.rb
|
71
|
+
- test/test_helper.rb
|
72
|
+
- test/view_controller_test.rb
|
101
73
|
homepage: http://rubygems.org/gems/bluesky
|
102
74
|
licenses:
|
103
75
|
- MIT
|
@@ -123,5 +95,6 @@ signing_key:
|
|
123
95
|
specification_version: 4
|
124
96
|
summary: An app framework for Clearwater
|
125
97
|
test_files:
|
126
|
-
-
|
127
|
-
-
|
98
|
+
- test/test_helper.rb
|
99
|
+
- test/view_controller_test.rb
|
100
|
+
- test/navigation_controller_test.rb
|
data/lib/bluesky/try.rb
DELETED
@@ -1,100 +0,0 @@
|
|
1
|
-
class Object
|
2
|
-
# Invokes the public method whose name goes as first argument just like
|
3
|
-
# +public_send+ does, except that if the receiver does not respond to it the
|
4
|
-
# call returns +nil+ rather than raising an exception.
|
5
|
-
#
|
6
|
-
# This method is defined to be able to write
|
7
|
-
#
|
8
|
-
# @person.try(:name)
|
9
|
-
#
|
10
|
-
# instead of
|
11
|
-
#
|
12
|
-
# @person.name if @person
|
13
|
-
#
|
14
|
-
# +try+ calls can be chained:
|
15
|
-
#
|
16
|
-
# @person.try(:spouse).try(:name)
|
17
|
-
#
|
18
|
-
# instead of
|
19
|
-
#
|
20
|
-
# @person.spouse.name if @person && @person.spouse
|
21
|
-
#
|
22
|
-
# +try+ will also return +nil+ if the receiver does not respond to the method:
|
23
|
-
#
|
24
|
-
# @person.try(:non_existing_method) #=> nil
|
25
|
-
#
|
26
|
-
# instead of
|
27
|
-
#
|
28
|
-
# @person.non_existing_method if @person.respond_to?(:non_existing_method) #=> nil
|
29
|
-
#
|
30
|
-
# +try+ returns +nil+ when called on +nil+ regardless of whether it responds
|
31
|
-
# to the method:
|
32
|
-
#
|
33
|
-
# nil.try(:to_i) # => nil, rather than 0
|
34
|
-
#
|
35
|
-
# Arguments and blocks are forwarded to the method if invoked:
|
36
|
-
#
|
37
|
-
# @posts.try(:each_slice, 2) do |a, b|
|
38
|
-
# ...
|
39
|
-
# end
|
40
|
-
#
|
41
|
-
# The number of arguments in the signature must match. If the object responds
|
42
|
-
# to the method the call is attempted and +ArgumentError+ is still raised
|
43
|
-
# in case of argument mismatch.
|
44
|
-
#
|
45
|
-
# If +try+ is called without arguments it yields the receiver to a given
|
46
|
-
# block unless it is +nil+:
|
47
|
-
#
|
48
|
-
# @person.try do |p|
|
49
|
-
# ...
|
50
|
-
# end
|
51
|
-
#
|
52
|
-
# You can also call try with a block without accepting an argument, and the block
|
53
|
-
# will be instance_eval'ed instead:
|
54
|
-
#
|
55
|
-
# @person.try { upcase.truncate(50) }
|
56
|
-
#
|
57
|
-
# Please also note that +try+ is defined on +Object+. Therefore, it won't work
|
58
|
-
# with instances of classes that do not have +Object+ among their ancestors,
|
59
|
-
# like direct subclasses of +BasicObject+. For example, using +try+ with
|
60
|
-
# +SimpleDelegator+ will delegate +try+ to the target instead of calling it on
|
61
|
-
# the delegator itself.
|
62
|
-
def try(*a, &b)
|
63
|
-
try!(*a, &b) if a.empty? || respond_to?(a.first)
|
64
|
-
end
|
65
|
-
|
66
|
-
# Same as #try, but will raise a NoMethodError exception if the receiver is not +nil+ and
|
67
|
-
# does not implement the tried method.
|
68
|
-
|
69
|
-
def try!(*a, &b)
|
70
|
-
if a.empty? && block_given?
|
71
|
-
if b.arity.zero?
|
72
|
-
instance_eval(&b)
|
73
|
-
else
|
74
|
-
yield self
|
75
|
-
end
|
76
|
-
else
|
77
|
-
public_send(*a, &b)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
class NilClass
|
83
|
-
# Calling +try+ on +nil+ always returns +nil+.
|
84
|
-
# It becomes especially helpful when navigating through associations that may return +nil+.
|
85
|
-
#
|
86
|
-
# nil.try(:name) # => nil
|
87
|
-
#
|
88
|
-
# Without +try+
|
89
|
-
# @person && @person.children.any? && @person.children.first.name
|
90
|
-
#
|
91
|
-
# With +try+
|
92
|
-
# @person.try(:children).try(:first).try(:name)
|
93
|
-
def try(*args)
|
94
|
-
nil
|
95
|
-
end
|
96
|
-
|
97
|
-
def try!(*args)
|
98
|
-
nil
|
99
|
-
end
|
100
|
-
end
|
data/spec/bluesky_spec.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
require 'bluesky'
|
2
|
-
|
3
|
-
class RootView < Bluesky::PureComponent
|
4
|
-
|
5
|
-
def render
|
6
|
-
nil
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
class RootViewController < Bluesky::ViewController
|
11
|
-
|
12
|
-
def view
|
13
|
-
RootView.new
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|
17
|
-
|
18
|
-
class Application < Bluesky::Application
|
19
|
-
|
20
|
-
root_view_controller RootViewController
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
describe Application do
|
25
|
-
context 'when run' do
|
26
|
-
it 'calls render on the root_view_controller' do
|
27
|
-
logger = double()
|
28
|
-
application = Application.new
|
29
|
-
application.logger = logger
|
30
|
-
expect(logger).to receive(:render).with(application)
|
31
|
-
application.run
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,109 +0,0 @@
|
|
1
|
-
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
-
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
-
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
4
|
-
# this file to always be loaded, without a need to explicitly require it in any
|
5
|
-
# files.
|
6
|
-
#
|
7
|
-
# Given that it is always loaded, you are encouraged to keep this file as
|
8
|
-
# light-weight as possible. Requiring heavyweight dependencies from this file
|
9
|
-
# will add to the boot time of your test suite on EVERY test run, even for an
|
10
|
-
# individual file that may not need all of that loaded. Instead, consider making
|
11
|
-
# a separate helper file that requires the additional dependencies and performs
|
12
|
-
# the additional setup, and require it from the spec files that actually need
|
13
|
-
# it.
|
14
|
-
#
|
15
|
-
# The `.rspec` file also contains a few flags that are not defaults but that
|
16
|
-
# users commonly want.
|
17
|
-
#
|
18
|
-
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
19
|
-
require 'bundler/setup'
|
20
|
-
Bundler.setup
|
21
|
-
|
22
|
-
require 'clearwater'
|
23
|
-
require 'bluesky'
|
24
|
-
|
25
|
-
RSpec.configure do |config|
|
26
|
-
# rspec-expectations config goes here. You can use an alternate
|
27
|
-
# assertion/expectation library such as wrong or the stdlib/minitest
|
28
|
-
# assertions if you prefer.
|
29
|
-
config.expect_with :rspec do |expectations|
|
30
|
-
# This option will default to `true` in RSpec 4. It makes the `description`
|
31
|
-
# and `failure_message` of custom matchers include text for helper methods
|
32
|
-
# defined using `chain`, e.g.:
|
33
|
-
# be_bigger_than(2).and_smaller_than(4).description
|
34
|
-
# # => "be bigger than 2 and smaller than 4"
|
35
|
-
# ...rather than:
|
36
|
-
# # => "be bigger than 2"
|
37
|
-
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
38
|
-
end
|
39
|
-
|
40
|
-
# rspec-mocks config goes here. You can use an alternate test double
|
41
|
-
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
42
|
-
config.mock_with :rspec do |mocks|
|
43
|
-
# Prevents you from mocking or stubbing a method that does not exist on
|
44
|
-
# a real object. This is generally recommended, and will default to
|
45
|
-
# `true` in RSpec 4.
|
46
|
-
mocks.verify_partial_doubles = true
|
47
|
-
end
|
48
|
-
|
49
|
-
# This option will default to `:apply_to_host_groups` in RSpec 4 (and will
|
50
|
-
# have no way to turn it off -- the option exists only for backwards
|
51
|
-
# compatibility in RSpec 3). It causes shared context metadata to be
|
52
|
-
# inherited by the metadata hash of host groups and examples, rather than
|
53
|
-
# triggering implicit auto-inclusion in groups with matching metadata.
|
54
|
-
config.shared_context_metadata_behavior = :apply_to_host_groups
|
55
|
-
|
56
|
-
# The settings below are suggested to provide a good initial experience
|
57
|
-
# with RSpec, but feel free to customize to your heart's content.
|
58
|
-
=begin
|
59
|
-
# This allows you to limit a spec run to individual examples or groups
|
60
|
-
# you care about by tagging them with `:focus` metadata. When nothing
|
61
|
-
# is tagged with `:focus`, all examples get run. RSpec also provides
|
62
|
-
# aliases for `it`, `describe`, and `context` that include `:focus`
|
63
|
-
# metadata: `fit`, `fdescribe` and `fcontext`, respectively.
|
64
|
-
config.filter_run_when_matching :focus
|
65
|
-
|
66
|
-
# Allows RSpec to persist some state between runs in order to support
|
67
|
-
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
68
|
-
# you configure your source control system to ignore this file.
|
69
|
-
config.example_status_persistence_file_path = "spec/examples.txt"
|
70
|
-
|
71
|
-
# Limits the available syntax to the non-monkey patched syntax that is
|
72
|
-
# recommended. For more details, see:
|
73
|
-
# - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
|
74
|
-
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
75
|
-
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
|
76
|
-
config.disable_monkey_patching!
|
77
|
-
|
78
|
-
# This setting enables warnings. It's recommended, but in some cases may
|
79
|
-
# be too noisy due to issues in dependencies.
|
80
|
-
config.warnings = true
|
81
|
-
|
82
|
-
# Many RSpec users commonly either run the entire suite or an individual
|
83
|
-
# file, and it's useful to allow more verbose output when running an
|
84
|
-
# individual spec file.
|
85
|
-
if config.files_to_run.one?
|
86
|
-
# Use the documentation formatter for detailed output,
|
87
|
-
# unless a formatter has already been configured
|
88
|
-
# (e.g. via a command-line flag).
|
89
|
-
config.default_formatter = 'doc'
|
90
|
-
end
|
91
|
-
|
92
|
-
# Print the 10 slowest examples and example groups at the
|
93
|
-
# end of the spec run, to help surface which specs are running
|
94
|
-
# particularly slow.
|
95
|
-
config.profile_examples = 10
|
96
|
-
|
97
|
-
# Run specs in random order to surface order dependencies. If you find an
|
98
|
-
# order dependency and want to debug it, you can fix the order by providing
|
99
|
-
# the seed, which is printed after each run.
|
100
|
-
# --seed 1234
|
101
|
-
config.order = :random
|
102
|
-
|
103
|
-
# Seed global randomization in this process using the `--seed` CLI option.
|
104
|
-
# Setting this allows you to use `--seed` to deterministically reproduce
|
105
|
-
# test failures related to randomization by passing the same `--seed` value
|
106
|
-
# as the one that triggered the failure.
|
107
|
-
Kernel.srand config.seed
|
108
|
-
=end
|
109
|
-
end
|