pakyow-ui 0.11.3 → 1.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/{pakyow-ui/CHANGELOG.md → CHANGELOG.md} +0 -0
  3. data/LICENSE +4 -0
  4. data/{pakyow-ui/README.md → README.md} +1 -2
  5. data/lib/pakyow/ui/behavior/recording.rb +51 -0
  6. data/lib/pakyow/ui/behavior/rendering/install_transforms.rb +47 -0
  7. data/lib/pakyow/ui/behavior/rendering.rb +105 -0
  8. data/lib/pakyow/ui/behavior/timeouts.rb +31 -0
  9. data/lib/pakyow/ui/framework.rb +75 -0
  10. data/lib/pakyow/ui/handler.rb +42 -0
  11. data/lib/pakyow/ui/helpers.rb +19 -0
  12. data/lib/pakyow/ui/recordable/attribute.rb +39 -0
  13. data/lib/pakyow/ui/recordable/attributes.rb +50 -0
  14. data/lib/pakyow/ui/recordable/helpers/client_remapping.rb +30 -0
  15. data/lib/pakyow/ui/recordable.rb +303 -0
  16. data/lib/pakyow/ui.rb +9 -0
  17. metadata +46 -60
  18. data/pakyow-ui/LICENSE +0 -20
  19. data/pakyow-ui/lib/pakyow/ui/base.rb +0 -26
  20. data/pakyow-ui/lib/pakyow/ui/channel_builder.rb +0 -55
  21. data/pakyow-ui/lib/pakyow/ui/config.rb +0 -11
  22. data/pakyow-ui/lib/pakyow/ui/ext/app.rb +0 -52
  23. data/pakyow-ui/lib/pakyow/ui/ext/view_context.rb +0 -30
  24. data/pakyow-ui/lib/pakyow/ui/fetch_view_handler.rb +0 -68
  25. data/pakyow-ui/lib/pakyow/ui/helpers.rb +0 -15
  26. data/pakyow-ui/lib/pakyow/ui/mock_mutation_eval.rb +0 -25
  27. data/pakyow-ui/lib/pakyow/ui/mutable.rb +0 -99
  28. data/pakyow-ui/lib/pakyow/ui/mutable_data.rb +0 -21
  29. data/pakyow-ui/lib/pakyow/ui/mutate_context.rb +0 -64
  30. data/pakyow-ui/lib/pakyow/ui/mutation_set.rb +0 -38
  31. data/pakyow-ui/lib/pakyow/ui/mutation_store.rb +0 -41
  32. data/pakyow-ui/lib/pakyow/ui/mutator.rb +0 -63
  33. data/pakyow-ui/lib/pakyow/ui/no_op_view.rb +0 -87
  34. data/pakyow-ui/lib/pakyow/ui/registries/redis_mutation_registry.rb +0 -70
  35. data/pakyow-ui/lib/pakyow/ui/registries/simple_mutation_registry.rb +0 -37
  36. data/pakyow-ui/lib/pakyow/ui/ui.rb +0 -81
  37. data/pakyow-ui/lib/pakyow/ui/ui_attrs.rb +0 -40
  38. data/pakyow-ui/lib/pakyow/ui/ui_component.rb +0 -68
  39. data/pakyow-ui/lib/pakyow/ui/ui_context.rb +0 -16
  40. data/pakyow-ui/lib/pakyow/ui/ui_instructable.rb +0 -117
  41. data/pakyow-ui/lib/pakyow/ui/ui_request.rb +0 -14
  42. data/pakyow-ui/lib/pakyow/ui/ui_view.rb +0 -200
  43. data/pakyow-ui/lib/pakyow/ui.rb +0 -1
  44. data/pakyow-ui/lib/pakyow-ui.rb +0 -1
@@ -1,81 +0,0 @@
1
- require_relative 'mutator'
2
- require_relative 'channel_builder'
3
- require_relative 'ui_view'
4
-
5
- module Pakyow
6
- module UI
7
- # The UI context available during routing.
8
- #
9
- # @api public
10
- class UI
11
- attr_accessor :context
12
- attr_reader :mutator
13
-
14
- # Informs Pakyow that a mutation has occurred in application state,
15
- # triggering all the necessary realtime view updates.
16
- #
17
- # @api public
18
- def mutated(scope, data = nil, context = nil)
19
- context ||= @context
20
-
21
- MutationStore.instance.mutations(scope).each do |mutation|
22
- view = UIView.new(mutation[:view_scope], mutation[:session])
23
-
24
- qualified = true
25
-
26
- # qualifiers are defined with the mutation
27
- unless mutation[:qualifiers].empty? || data.nil?
28
- mutation[:qualifiers].each_with_index do |qualifier, i|
29
- qualified = false unless data[qualifier.to_sym].to_s == mutation[:query_args][i].to_s
30
- end
31
- end
32
-
33
- qualified = false if data.nil? && !mutation[:qualifications].empty?
34
-
35
- # qualifications are set on the subscription
36
- unless !qualified || mutation[:qualifications].empty? || data.nil?
37
- mutation[:qualifications].each_pair do |key, value|
38
- qualified = false if data[key.to_sym].to_s != value.to_s
39
- end
40
- end
41
-
42
- next unless qualified
43
-
44
- mutable_data = Mutator.instance.mutable(scope, context).send(mutation[:query_name], *mutation[:query_args]).data
45
- Mutator.instance.mutate(mutation[:mutation].to_sym, view, mutable_data)
46
-
47
- channel = ChannelBuilder.build(
48
- scope: mutation[:view_scope],
49
- mutation: mutation[:mutation].to_sym,
50
- qualifiers: mutation[:qualifiers],
51
- data: mutable_data,
52
- qualifications: mutation[:qualifications]
53
- )
54
-
55
- Pakyow.app.socket.push_message_to_socket_with_key(view.finalize, channel, mutation[:socket_key])
56
- end
57
- end
58
-
59
- # Addresses a component rendered on the client-side.
60
- #
61
- # @api public
62
- def component(name, qualifications = {})
63
- UIComponent.new(name, qualifications)
64
- end
65
-
66
- # @api private
67
- def load(mutators, mutables)
68
- # TODO: this is another pattern I see all over the place
69
- @mutator = Mutator.instance.reset
70
-
71
- mutators.each_pair do |scope, block|
72
- @mutator.set(scope, &block)
73
- end
74
-
75
- mutables.each_pair do |scope, block|
76
- @mutator.mutable(scope, &block)
77
- end
78
- end
79
- end
80
- end
81
- end
@@ -1,40 +0,0 @@
1
- require_relative 'ui_instructable'
2
-
3
- module Pakyow
4
- module UI
5
- # Builds up instructions for changing view attributes.
6
- #
7
- # @api private
8
- class UIAttrs
9
- include Instructable
10
-
11
- def nested_instruct_object(_method, _data, _scope)
12
- UIAttrs.new
13
- end
14
-
15
- def method_missing(method, value)
16
- nested_instruct(method, value)
17
- end
18
-
19
- def class
20
- method_missing(:class, nil)
21
- end
22
-
23
- def id
24
- method_missing(:id, nil)
25
- end
26
-
27
- def <<(value)
28
- method_missing(:insert, value)
29
- end
30
-
31
- def []=(method, value)
32
- method_missing(method, value)
33
- end
34
-
35
- def [](method)
36
- method_missing(method, nil)
37
- end
38
- end
39
- end
40
- end
@@ -1,68 +0,0 @@
1
- require_relative 'ui_instructable'
2
-
3
- module Pakyow
4
- module UI
5
- # An object for interacting with components rendered in a browser. Custom
6
- # messages can be pushed and will be handled by the event listener defined
7
- # on the client-side component.
8
- #
9
- # It's also possible to perform view transformations in realtime. Components
10
- # implement a subset of transformations, including `scope` and `append`.
11
- # This allows for finer control over particular components, completely
12
- # bypassing mutables and mutators.
13
- #
14
- # @api public
15
- class UIComponent
16
- include Instructable
17
-
18
- attr_reader :name, :view, :qualifications
19
-
20
- # Intended to be created through the `ui.component` helper.
21
- #
22
- # @api private
23
- def initialize(name, qualifications = {})
24
- super()
25
- @name = name
26
- @qualifications = qualifications
27
- end
28
-
29
- # Pushes a message to the component.
30
- #
31
- # @api public
32
- def push(payload = nil)
33
- payload ||= { instruct: (root || self).finalize }
34
-
35
- Pakyow.app.socket.push(
36
- payload,
37
-
38
- ChannelBuilder.build(
39
- component: name,
40
- qualifications: qualifications
41
- )
42
- )
43
- end
44
-
45
- # Narrows the scope of component instructions.
46
- #
47
- # @api public
48
- def scope(name)
49
- nested_instruct(:scope, name.to_s, name)
50
- end
51
-
52
- # Other supported transformation methods.
53
- #
54
- # @api public
55
- %i(append prepend).each do |method|
56
- define_method method do |value|
57
- instruct(method, value)
58
- push
59
- end
60
- end
61
-
62
- # @api private
63
- def nested_instruct_object(_method, _data, _scope)
64
- UIComponent.new(name, qualifications)
65
- end
66
- end
67
- end
68
- end
@@ -1,16 +0,0 @@
1
- module Pakyow
2
- module UI
3
- # A simple context object used for accessing the session.
4
- #
5
- # @api private
6
- class UIContext
7
- def initialize(session)
8
- @session = session
9
- end
10
-
11
- def request
12
- UIRequest.new(@session)
13
- end
14
- end
15
- end
16
- end
@@ -1,117 +0,0 @@
1
- module Pakyow
2
- module UI
3
- # Helper methods for instructable objects.
4
- #
5
- # @api private
6
- module Instructable
7
- def self.included(klass)
8
- (@instructables ||= []) << klass
9
- end
10
-
11
- def self.instructable?(object)
12
- @instructables.select { |i|
13
- object.is_a?(i)
14
- }.any?
15
- end
16
-
17
- attr_reader :instructions
18
- attr_accessor :root
19
-
20
- def initialize
21
- @instructions = []
22
- end
23
-
24
- def instruct(method, data)
25
- @instructions << [clean_method(method), hashify(data)]
26
- self
27
- end
28
-
29
- def nested_instruct(method, data, scope = nil)
30
- view = nested_instruct_object(method, data, scope)
31
- view.root = self
32
-
33
- @instructions << [clean_method(method), hashify(data), view]
34
- view
35
- end
36
-
37
- # Returns an instruction set for all view transformations.
38
- #
39
- # e.g. a value-less transformation:
40
- # [[:remove, nil]]
41
- #
42
- # e.g. a transformation with a value:
43
- # [[:text=, 'foo']]
44
- #
45
- # e.g. a nested transformation
46
- # [[:scope, :post, [[:remove, nil]]]]
47
- def finalize
48
- @instructions.map { |instruction|
49
- if Instructable.instructable?(instruction[2])
50
- instruction[2] = instruction[2].finalize
51
- end
52
-
53
- instruction
54
- }
55
- end
56
-
57
- private
58
-
59
- def mixin_bindings(data, bindings = {})
60
- data.map { |bindable|
61
- datum = bindable.to_hash.dup
62
- Pakyow::Presenter::Binder.instance.bindings_for_scope(scoped_as, bindings).keys.each do |key|
63
- result = Pakyow::Presenter::Binder.instance.value_for_scoped_prop(scoped_as, key, bindable, bindings, self)
64
-
65
- if result.is_a?(Hash)
66
- # we don't currently support view manipulations that occur in bindings
67
- # TODO: look into what it would take to support this
68
- result.delete(:view)
69
-
70
- content = result.delete(:content)
71
- if content.respond_to?(:to_proc)
72
- content = content.to_proc.call()
73
- end
74
-
75
- datum[key] = {
76
- __content: content,
77
- __attrs: Hash[*result.flat_map { |k, v|
78
- if v.respond_to?(:to_proc)
79
- attrs = UIAttrs.new
80
- v.call(attrs)
81
- [k, attrs.finalize]
82
- else
83
- [k, v]
84
- end
85
- }]
86
- }
87
- else
88
- datum[key] = result
89
- end
90
- end
91
-
92
- datum
93
- }
94
- end
95
-
96
- def hashify(data)
97
- return hashify_datum(data) unless data.is_a?(Array)
98
-
99
- data.map { |datum|
100
- hashify_datum(datum)
101
- }
102
- end
103
-
104
- def hashify_datum(datum)
105
- if datum.respond_to?(:to_hash)
106
- datum.to_hash
107
- else
108
- datum
109
- end
110
- end
111
-
112
- def clean_method(method)
113
- method.to_s.delete('=').to_sym
114
- end
115
- end
116
- end
117
- end
@@ -1,14 +0,0 @@
1
- module Pakyow
2
- module UI
3
- # A simple request object used for accessing the session.
4
- #
5
- # @api private
6
- class UIRequest
7
- attr_reader :session
8
-
9
- def initialize(session)
10
- @session = Hash.strhash(session)
11
- end
12
- end
13
- end
14
- end
@@ -1,200 +0,0 @@
1
- require_relative 'ui_attrs'
2
- require_relative 'ui_instructable'
3
- require_relative 'ui_context'
4
- require_relative 'ui_request'
5
-
6
- module Pakyow
7
- module UI
8
- # Translates view transformations to instructions.
9
- #
10
- # @api private
11
- class UIView
12
- include Instructable
13
- include Pakyow::Helpers
14
-
15
- def initialize(scope, session = {})
16
- super()
17
- @scope = scope.to_sym
18
- @session = session
19
- @context = UIContext.new(session)
20
- end
21
-
22
- def nested_instruct_object(_method, _data, scope)
23
- UIView.new(scope || @scope, @session)
24
- end
25
-
26
- def scoped_as
27
- @scope
28
- end
29
-
30
- def attrs_instruct
31
- attrs = UIAttrs.new
32
- @instructions << [:attrs, nil, attrs]
33
- attrs
34
- end
35
-
36
- ### view methods w/o args
37
-
38
- %i(
39
- remove
40
- clear
41
- ).each do |method|
42
- define_method method do
43
- instruct(method, nil)
44
- end
45
- end
46
-
47
- ### view methods w/ args
48
-
49
- %i(
50
- title=
51
- text=
52
- html=
53
- append
54
- prepend
55
- after
56
- before
57
- replace
58
- use
59
- ).each do |method|
60
- define_method method do |value|
61
- instruct(method, value.to_s)
62
- end
63
- end
64
-
65
- ### misc view methods
66
-
67
- def with(&block)
68
- if block.arity == 0
69
- instance_exec(&block)
70
- else
71
- yield(self)
72
- end
73
-
74
- self
75
- end
76
-
77
- def match(data)
78
- instruct(:match, Array.ensure(data))
79
- end
80
-
81
- def scope(name)
82
- nested_instruct(:scope, name.to_s, name)
83
- end
84
-
85
- def attrs
86
- attrs_instruct
87
- end
88
-
89
- ### view methods that change context
90
-
91
- %i(
92
- prop
93
- component
94
- ).each do |method|
95
- define_method method do |value|
96
- nested_instruct(method, value.to_s)
97
- end
98
- end
99
-
100
- ### view methods that continue into a new context
101
-
102
- def version(data, &block)
103
- nested = nested_instruct(:version, data)
104
- Array.ensure(data).each do |datum|
105
- sub = UIView.new(@scope)
106
-
107
- if block.arity == 1
108
- sub.instance_exec(datum, &block)
109
- else
110
- block.call(sub, datum)
111
- end
112
-
113
- nested.instructions << sub.finalize
114
- end
115
-
116
- self
117
- end
118
-
119
- def for(data, &block)
120
- nested = nested_instruct(:for, data)
121
- Array.ensure(data).each do |datum|
122
- sub = UIView.new(@scope)
123
-
124
- if block.arity == 1
125
- sub.instance_exec(datum, &block)
126
- else
127
- block.call(sub, datum)
128
- end
129
-
130
- nested.instructions << sub.finalize
131
- end
132
- end
133
-
134
- def for_with_index(*args, &block)
135
- self.for(*args, &block)
136
- end
137
-
138
- def repeat(data, &block)
139
- nested = nested_instruct(:repeat, data)
140
- Array.ensure(data).each do |datum|
141
- sub = UIView.new(@scope)
142
-
143
- if block.arity == 1
144
- sub.instance_exec(datum, &block)
145
- else
146
- block.call(sub, datum)
147
- end
148
-
149
- nested.instructions << sub.finalize
150
- end
151
- end
152
-
153
- def repeat_with_index(*args, &block)
154
- repeat(*args, &block)
155
- end
156
-
157
- def bind(data, bindings: {}, context: nil, &block)
158
- # TODO: handle context?
159
-
160
- nested = nested_instruct(:bind, mixin_bindings(Array.ensure(data), bindings))
161
- return self unless block_given?
162
-
163
- data.each do |datum|
164
- sub = UIView.new(@scope)
165
-
166
- if block.arity == 1
167
- sub.instance_exec(datum, &block)
168
- else
169
- block.call(sub, datum)
170
- end
171
-
172
- nested.instructions << sub.finalize
173
- end
174
- end
175
-
176
- def bind_with_index(*args, &block)
177
- bind(*args, &block)
178
- end
179
-
180
- def apply(data, bindings: {}, context: nil, &block)
181
- # TODO: handle context?
182
-
183
- nested = nested_instruct(:apply, mixin_bindings(Array.ensure(data), bindings))
184
- return self unless block_given?
185
-
186
- data.each do |datum|
187
- sub = UIView.new(@scope, @session)
188
-
189
- if block.arity == 1
190
- sub.instance_exec(datum, &block)
191
- else
192
- block.call(sub, datum)
193
- end
194
-
195
- nested.instructions << sub.finalize
196
- end
197
- end
198
- end
199
- end
200
- end
@@ -1 +0,0 @@
1
- require 'pakyow/ui/base'
@@ -1 +0,0 @@
1
- require 'pakyow/ui'