pakyow-presenter 0.9.1 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/pakyow-presenter/CHANGELOG.md +94 -0
  3. data/pakyow-presenter/{MIT-LICENSE → LICENSE} +2 -2
  4. data/pakyow-presenter/README.md +36 -0
  5. data/pakyow-presenter/lib/pakyow-presenter.rb +1 -4
  6. data/pakyow-presenter/lib/presenter/attributes.rb +10 -1
  7. data/pakyow-presenter/lib/presenter/base.rb +2 -1
  8. data/pakyow-presenter/lib/presenter/binder.rb +20 -6
  9. data/pakyow-presenter/lib/presenter/binder_set.rb +21 -17
  10. data/pakyow-presenter/lib/presenter/config/presenter.rb +2 -2
  11. data/pakyow-presenter/lib/presenter/ext/app.rb +32 -1
  12. data/pakyow-presenter/lib/presenter/helpers.rb +6 -6
  13. data/pakyow-presenter/lib/presenter/page.rb +4 -4
  14. data/pakyow-presenter/lib/presenter/presenter.rb +32 -10
  15. data/pakyow-presenter/lib/presenter/string_doc.rb +78 -11
  16. data/pakyow-presenter/lib/presenter/string_doc_parser.rb +16 -7
  17. data/pakyow-presenter/lib/presenter/view.rb +55 -17
  18. data/pakyow-presenter/lib/presenter/view_collection.rb +74 -7
  19. data/pakyow-presenter/lib/presenter/view_composer.rb +52 -8
  20. data/pakyow-presenter/lib/presenter/view_context.rb +19 -1
  21. data/pakyow-presenter/lib/presenter/view_store.rb +202 -163
  22. data/pakyow-presenter/lib/presenter/view_store_loader.rb +43 -0
  23. data/pakyow-presenter/lib/presenter/view_version.rb +97 -0
  24. data/pakyow-presenter/lib/views/errors/404.html +5 -0
  25. data/pakyow-presenter/{README → lib/views/errors/500.html} +0 -0
  26. metadata +36 -21
  27. data/pakyow-presenter/CHANGES +0 -57
  28. data/pakyow-presenter/lib/presenter/nokogiri_doc.rb +0 -321
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b03aed00a4341ef92f5c05f9e2154a46259c8bc4
4
- data.tar.gz: d22155fe2adad963042ba51b15d10ab98572fe20
3
+ metadata.gz: bf054d209c01b7b382b09b1e1a01730781d8f1df
4
+ data.tar.gz: ab58a2cfde9f832773b94b15315a84f488768ece
5
5
  SHA512:
6
- metadata.gz: 0e5e1f65609f24045e2ef75bd45f7bb50e877d1f606a2b54ba17b7ad3096b0c90d3624b7beecde1fd41abff86824992e8c021c089b48c6cb333481c91310143e
7
- data.tar.gz: 28c195177d878332fe18f91e46f3786f70bd32459a89d112fcd8d326c76579c27d4356987bbdeec3a17645de389341065df972a1942cddd33377f8ec978eb365
6
+ metadata.gz: 0780b1e866ae3de214d7166cba28564eb0ddf64a737d6fae768bf0bd9c372597802b174fe75c6885f24994d8bfcbdb2d5d3282608c5c1389056958143d12dc62
7
+ data.tar.gz: e2ae8354a5e0f68c0052c62a3d194cb1ac5ee557661b9238f8ff9e7319c543bf7484951ca31fca7e3e2d3a71e95fe0834556f5b2f2220d5ccb853a1a927a57e4
@@ -0,0 +1,94 @@
1
+ # 0.10.0 (to be released)
2
+
3
+ * Adds precomposition to composer
4
+ * Fixes binding action for nested resources
5
+ * Fixes variable name in remap_partials
6
+ * Setup form on `_root` rather than `action` prop
7
+ * Handles view paths that don't define an `index` view
8
+ * Adds `ViewCollection#scoped_as` convenience method
9
+ * Renames default template to `default` (instead of `pakyow`)
10
+ * Adds convenience method to check for view existence
11
+ * Support binding hashes with `String` keys
12
+ * Fixes a bug when a node is inserted after another node that also has a sibling
13
+ * Fixes binding to props nested in partial
14
+ * Now allows views for a view store to be fragmented across multiple directories
15
+ * Adds versioned scopes with auto empty handling
16
+ * Now sets scope name explicitly for `ViewCollection` built from a `View`
17
+ * Adds a helper method for checking if a attribute exists
18
+ * Adds support for components and channels (realtime ftw)
19
+ * Adds id of object to scoped node during binding
20
+ * Fixes a bug with view contexts in presenter helpers
21
+ * Ported all tests to rspec
22
+ * Fixes a bug finding scopes in a nested partial
23
+ * Fixes a bug where unused partials still being included as parts in the composer
24
+ * Fixes a `StringDoc` bug when structure is empty
25
+ * Fixes bug preventing passed binding functions from being used when no binding sets are registered for scope
26
+ * Fixes namespace collisions
27
+ * Fixes error when matching data empty collection
28
+ * Renames `inner_html` to `html`
29
+ * Now only reloads a view store if the contents have changed
30
+ * Yields `prop` node rather than full `scope` in bindings
31
+ * Handles `AttributesCollection#<<` for all use cases
32
+ * Adds `to_html` method on composer
33
+ * No longer complains about a view store missing a template directory
34
+ * Now finds view info for a path that doesn't contain a page
35
+ * Allows root nodes to be appendable in string doc
36
+ * Removes nokogiri doc
37
+
38
+ # 0.9.1 / 2014-12-06
39
+
40
+ * Fixes bug where name attribute wasn't set on form fields when binding without a value
41
+ * Fixes bug causing all partials (not just available ones) to be defined as view parts
42
+ * Fixes bug causing form action binding to break
43
+
44
+ # 0.9.0 / 2014-11-09
45
+
46
+ * Introduces StringDoc, replacing Nokogiri for view rendering with a performant alternative
47
+ * Adds the ability to set an empty first option in bindings
48
+ * Remove the annoying "no content" for container log message
49
+ * Binding context must now be explicitly passed when binding
50
+ * Yield bindable value before bindable in view binding fns
51
+ * Fixes bug causing non-html view contents to be processed twice
52
+ * Removes support for Ruby versions < 2.0.0
53
+
54
+ # 0.8.0 / 2014-03-02
55
+
56
+ * Major rewrite, including new view syntax, composer, and transformation api
57
+
58
+ # 0.7.2 / 2012-02-29
59
+
60
+ * No changes -- bumped version to be consistent
61
+
62
+ # 0.7.1 / 2012-01-08
63
+
64
+ * View caching fixes
65
+ * Changed binder to allow definition for multiple endpoints
66
+ * Changed in_context to accept context as block argument
67
+ * Changed action binder to add leading slash if needed
68
+ * Change to allow a view's content to be set to nil
69
+ * Fixed Views#find method
70
+
71
+ # 0.7.0 / 2011-11-19
72
+
73
+ * Cleaned up core/presenter interface
74
+ * Optimized view caching
75
+ * Binding occurs on the label an object is being bound to, not the object type
76
+ * Root view override in index directories no longer specify a root view for siblings
77
+ * Fixed problem binding to a checkbox who’s value attribute is not set
78
+
79
+ # 0.6.3 / 2011-09-13
80
+
81
+ * Fixes binding to bindings defined by HTML 'name' attribute
82
+
83
+ # 0.6.2 / 2011-08-20
84
+
85
+ * Fixes issue binding object to root nodes
86
+ * JRuby Support
87
+
88
+ # 0.6.1 / 2011-08-20
89
+
90
+ * Fixes gemspec problem
91
+
92
+ # 0.6.0 / 2011-08-20
93
+
94
+ * Initial gem release of 0.6.0 codebase
@@ -1,4 +1,4 @@
1
- Copyright (c) 2011 Bryan Powell
1
+ Copyright (c) 2011-2015 Bryan Powell
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
17
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
18
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
19
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,36 @@
1
+ # pakyow-presenter
2
+
3
+ A view-first presentation layer for Pakyow.
4
+
5
+ # Download
6
+
7
+ The latest version of Pakyow Presenter can be installed with RubyGems:
8
+
9
+ ```
10
+ gem install pakyow-presenter
11
+ ```
12
+
13
+ Source code can be downloaded as part of the Pakyow project on Github:
14
+
15
+ - https://github.com/pakyow/pakyow/tree/master/pakyow-presenter
16
+
17
+ # License
18
+
19
+ Pakyow Presenter is released free and open-source under the [MIT
20
+ License](http://opensource.org/licenses/MIT).
21
+
22
+ # Support
23
+
24
+ Documentation is available here:
25
+
26
+ - http://pakyow.org/docs/view-composition
27
+ - http://pakyow.org/docs/data-binding
28
+ - http://pakyow.org/docs/view-management
29
+
30
+ Found a bug? Tell us about it here:
31
+
32
+ - https://github.com/pakyow/pakyow/issues
33
+
34
+ We'd love to have you in the community:
35
+
36
+ - http://pakyow.org/get-involved
@@ -1,13 +1,10 @@
1
1
  libdir = File.dirname(__FILE__)
2
2
  $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
3
3
 
4
- # Gems
4
+ # Gems
5
5
  require 'nokogiri'
6
6
 
7
- # Base
8
7
  require 'presenter/base'
9
- include Presenter
10
-
11
8
  require 'presenter/presenter'
12
9
  require 'presenter/config/presenter'
13
10
  require 'presenter/helpers'
@@ -49,6 +49,10 @@ module Pakyow
49
49
  update_value
50
50
  end
51
51
 
52
+ def include?(attribute)
53
+ @doc.has_attribute?(attribute)
54
+ end
55
+
52
56
  # passes method call to `value`
53
57
  def method_missing(method, *args)
54
58
  ret = @value.send(method, *args)
@@ -182,7 +186,12 @@ module Pakyow
182
186
  end
183
187
 
184
188
  def <<(attributes)
185
- @attributes << attributes
189
+ if attributes.is_a?(Attribute) || attributes.is_a?(Attributes)
190
+ @attributes << attributes
191
+ else
192
+ method_missing(:<<, *attributes)
193
+ end
194
+
186
195
  self
187
196
  end
188
197
 
@@ -12,13 +12,14 @@ require 'presenter/binder_set'
12
12
  require 'presenter/attributes'
13
13
  require 'presenter/exceptions'
14
14
  require 'presenter/view_composer'
15
- require 'presenter/nokogiri_doc'
16
15
  require 'presenter/string_doc'
17
16
  require 'presenter/string_doc_parser'
18
17
  require 'presenter/string_doc_renderer'
19
18
  require 'presenter/binding_eval'
20
19
  require 'presenter/doc_helpers'
20
+ require 'presenter/view_version'
21
21
  require 'presenter/view_context'
22
+ require 'presenter/view_store_loader'
22
23
 
23
24
  module Pakyow
24
25
  module Presenter
@@ -47,18 +47,22 @@ module Pakyow
47
47
  # @param context [Symbol] context passed through to the defined bindings
48
48
  #
49
49
  def value_for_scoped_prop(scope, prop, bindable, bindings, context)
50
- binding_fn = @sets.lazy.map { |set|
51
- set[1].match_for_prop(prop, scope, bindable, bindings)
52
- }.find { |match|
53
- !match.nil?
54
- }
50
+ if @sets.empty?
51
+ binding_fn = bindings[prop]
52
+ else
53
+ binding_fn = @sets.lazy.map { |set|
54
+ set[1].match_for_prop(prop, scope, bindable, bindings)
55
+ }.find { |match|
56
+ !match.nil?
57
+ }
58
+ end
55
59
 
56
60
  if binding_fn
57
61
  binding_eval = BindingEval.new(prop, bindable, context)
58
62
  binding_eval.instance_exec(binding_eval.value, bindable, context, &binding_fn)
59
63
  else # default value
60
64
  if bindable.is_a?(Hash)
61
- bindable[prop]
65
+ bindable.fetch(prop) { bindable[prop.to_s] }
62
66
  elsif bindable.class.method_defined?(prop)
63
67
  bindable.send(prop)
64
68
  end
@@ -84,6 +88,16 @@ module Pakyow
84
88
  !options.nil?
85
89
  }
86
90
  end
91
+
92
+ def bindings_for_scope(scope, bindings = {})
93
+ return bindings if @sets.empty?
94
+
95
+ @sets.map { |set|
96
+ set[1].bindings_for_scope(scope, bindings)
97
+ }.inject({}) { |acc, bindings|
98
+ acc.merge(bindings)
99
+ }
100
+ end
87
101
  end
88
102
  end
89
103
  end
@@ -67,27 +67,31 @@ module Pakyow
67
67
  end
68
68
 
69
69
  def restful(route_group)
70
- binding(:action) {
70
+ binding :_root do
71
71
  routes = Router.instance.group(route_group)
72
- return_data = {}
73
72
 
74
- if id = bindable[:id]
75
- return_data[:view] = lambda { |view|
76
- view.prepend(View.new('<input type="hidden" name="_method" value="patch">'))
73
+ {
74
+ view: lambda { |view|
75
+ action = view.attrs.action.value
76
+ return if (action && !action.empty?)
77
+
78
+ route_params = params.dup
79
+ if view.doc.tagname == 'form'
80
+ if id = bindable[:id]
81
+ view.prepend(View.new('<input type="hidden" name="_method" value="patch">'))
82
+ route_params[:"#{route_group}_id"] = id
83
+ action = :update
84
+ else
85
+ action = :create
86
+ end
87
+
88
+ view.attrs.action = routes.path(action, route_params)
89
+ view.attrs.method = 'post'
90
+ end
77
91
  }
78
-
79
- action = routes.path(:update, :"#{route_group}_id" => id)
80
- else
81
- action = routes.path(:create)
82
- end
83
-
84
- return_data[:action] = action
85
- return_data[:method] = 'post'
86
- return_data
87
- }
92
+ }
93
+ end
88
94
  end
89
-
90
- #TODO options
91
95
  end
92
96
  end
93
97
  end
@@ -8,7 +8,7 @@ Pakyow::Config.register(:presenter) { |config|
8
8
  }
9
9
 
10
10
  # the default view for each view store
11
- config.opt :default_views, { default: 'pakyow.html' }
11
+ config.opt :default_views, { default: :default }
12
12
 
13
13
  # a convenience option to lookup the default_view for a view store by name
14
14
  config.opt :default_view, lambda { |store_name|
@@ -32,5 +32,5 @@ Pakyow::Config.register(:presenter) { |config|
32
32
  config.opt :prop_attribute, 'data-prop'
33
33
 
34
34
  # the document class used to parse and render views
35
- config.opt :view_doc_class, StringDoc
35
+ config.opt :view_doc_class, Pakyow::Presenter::StringDoc
36
36
  }
@@ -3,7 +3,7 @@ module Pakyow
3
3
  class << self
4
4
  @@bindings = {}
5
5
  @@processors = {}
6
-
6
+
7
7
  def bindings(set_name = :main, &block)
8
8
  if set_name && block
9
9
  @@bindings[set_name] = block
@@ -28,5 +28,36 @@ module Pakyow
28
28
  def bindings(set_name = :main, &block)
29
29
  self.class.bindings(set_name, &block)
30
30
  end
31
+
32
+ def content_for_code(code)
33
+ content = File.open(
34
+ File.join(
35
+ File.expand_path(
36
+ '../../../../../', __FILE__),
37
+ 'pakyow-core',
38
+ 'lib',
39
+ 'views',
40
+ 'errors',
41
+ code.to_s + '.html'
42
+ )
43
+ ).read + File.open(
44
+ File.join(
45
+ File.expand_path(
46
+ '../../../', __FILE__),
47
+ 'views',
48
+ 'errors',
49
+ code.to_s + '.html'
50
+ )
51
+ ).read
52
+
53
+ path = String.normalize_path(request.path)
54
+ path = '/' if path.empty?
55
+
56
+ content.gsub!('{view_path}', path == '/' ? 'index.html' : "#{path}.html")
57
+
58
+ template = presenter.store.template(:default)
59
+ template.container(:default).replace(content)
60
+ template.to_html
61
+ end
31
62
  end
32
63
  end
@@ -7,26 +7,26 @@ module Pakyow
7
7
  extend Forwardable
8
8
 
9
9
  def_delegators :@presenter, :store, :store=, :content, :view=,
10
- :template=, :page=, :path, :path=, :compose, :composer
10
+ :template=, :page=, :path, :path=, :compose, :composer, :precompose!
11
11
 
12
12
  def view
13
- ViewContext.new(@presenter.view, context)
13
+ Presenter::ViewContext.new(@presenter.view, self)
14
14
  end
15
15
 
16
16
  def partial(*args)
17
- ViewContext.new(@presenter.partial(*args), context)
17
+ Presenter::ViewContext.new(@presenter.partial(*args), self)
18
18
  end
19
19
 
20
20
  def template
21
- ViewContext.new(@presenter.template, context)
21
+ Presenter::ViewContext.new(@presenter.template, self)
22
22
  end
23
23
 
24
24
  def page
25
- ViewContext.new(@presenter.page, context)
25
+ Presenter::ViewContext.new(@presenter.page, self)
26
26
  end
27
27
 
28
28
  def container(*args)
29
- ViewContext.new(@presenter.container(*args), context)
29
+ Presenter::ViewContext.new(@presenter.container(*args), self)
30
30
  end
31
31
 
32
32
  def presenter
@@ -4,21 +4,21 @@ module Pakyow
4
4
  MATTER_MATCHER = /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
5
5
 
6
6
  class << self
7
- def load(path)
7
+ def load(path, view_store_name = :default)
8
8
  format = String.split_at_last_dot(path)[-1]
9
9
  name = File.basename(path, '.*').to_sym
10
10
  contents = FileTest.file?(path) ? File.read(path) : nil
11
11
 
12
- return Page.new(name, contents, path, format)
12
+ return Page.new(name, contents, path, format, view_store_name)
13
13
  end
14
14
  end
15
15
 
16
16
  attr_reader :path, :contents
17
17
 
18
- def initialize(name, contents, path, format = :html)
18
+ def initialize(name, contents, path, format = :html, view_store_name = :default)
19
19
  @name, @contents, @path, @format = name, contents, path, format
20
20
 
21
- @info = { template: :pakyow }
21
+ @info = { template: Pakyow::App.config.presenter.default_view(view_store_name) }
22
22
  @containers = {}
23
23
 
24
24
  unless @contents.nil?
@@ -24,6 +24,7 @@ module Pakyow
24
24
 
25
25
  Pakyow::App.before(:init) {
26
26
  @presenter = Presenter.new
27
+ ViewStoreLoader.instance.reset
27
28
  }
28
29
 
29
30
  Pakyow::App.after(:match) {
@@ -104,6 +105,18 @@ module Pakyow
104
105
  @composer || @view || raise(MissingView)
105
106
  end
106
107
 
108
+ def view?(path)
109
+ if composer_for_path(path)
110
+ true
111
+ else
112
+ false
113
+ end
114
+ end
115
+
116
+ def precompose!
117
+ self.view = @composer.view
118
+ end
119
+
107
120
  def view=(view)
108
121
  @view = view
109
122
 
@@ -168,23 +181,21 @@ module Pakyow
168
181
  end
169
182
 
170
183
  def setup_for_path(path, explicit = false)
171
- @view_stores.each do |name, store|
172
- begin
173
- @composer = store.composer(path)
174
- @path = path
175
- return
176
- rescue MissingView
177
- end
184
+ if composer = composer_for_path(path)
185
+ @composer = composer
186
+ @path = path
187
+ return
178
188
  end
179
189
 
180
190
  e = MissingView.new("No view at path '#{path}'")
181
- explicit ? raise(e) : Pakyow.logger.debug(e.message)
191
+ explicit ? raise(e) : Pakyow.logger.info(e.message)
182
192
  end
183
193
 
184
194
  def load_views
185
- @view_stores = {}
195
+ @view_stores ||= {}
186
196
 
187
- Pakyow::Config.presenter.view_stores.each_pair {|name, path|
197
+ Pakyow::Config.presenter.view_stores.each_pair { |name, path|
198
+ next unless ViewStoreLoader.instance.modified?(name, path)
188
199
  @view_stores[name] = ViewStore.new(path, name)
189
200
  }
190
201
  end
@@ -200,6 +211,17 @@ module Pakyow
200
211
  def load_processors
201
212
  @processor_store = Pakyow::App.processors
202
213
  end
214
+
215
+ def composer_for_path(path)
216
+ @view_stores.each do |name, store|
217
+ begin
218
+ return store.composer(path)
219
+ rescue MissingView
220
+ end
221
+ end
222
+
223
+ return nil
224
+ end
203
225
  end
204
226
  end
205
227
  end
@@ -24,13 +24,12 @@ module Pakyow
24
24
  def initialize_copy(original_doc)
25
25
  super
26
26
 
27
- original_structure = original_doc.instance_variable_get(:@structure)
28
- @structure = Utils::Dup.deep(original_structure) if original_structure
27
+ if original_structure = original_doc.instance_variable_get(:@structure)
28
+ @structure = Utils::Dup.deep(original_structure)
29
+ end
29
30
 
30
- original_node = original_doc.instance_variable_get(:@node)
31
- if original_node
32
- node_index = original_structure.index(original_node)
33
- @node = @structure[node_index]
31
+ if original_doc.node?
32
+ @node = @structure[original_doc.node_index]
34
33
  end
35
34
  end
36
35
 
@@ -68,6 +67,8 @@ module Pakyow
68
67
 
69
68
  def remove
70
69
  @structure.delete_if { |n| n.equal?(node) }
70
+ @node = ['', {}, [['', {}, []]]]
71
+ @removed = true
71
72
  end
72
73
 
73
74
  def clear
@@ -116,7 +117,7 @@ module Pakyow
116
117
  doc = StringDoc.ensure(doc)
117
118
 
118
119
  if doc.node?
119
- @structure.push(doc.node)
120
+ @structure.insert(node_index + 1, doc.node)
120
121
  else
121
122
  @structure.concat(doc.structure)
122
123
  end
@@ -134,7 +135,7 @@ module Pakyow
134
135
 
135
136
  def replace(doc)
136
137
  doc = StringDoc.ensure(doc)
137
- index = @structure.index(node) || 0
138
+ index = node_index || 0
138
139
 
139
140
  if doc.node?
140
141
  @structure.insert(index + 1, node)
@@ -158,6 +159,14 @@ module Pakyow
158
159
  containers.fetch(name, {})[:doc]
159
160
  end
160
161
 
162
+ def component(name)
163
+ components.select { |c| c[:component] == name }
164
+ end
165
+
166
+ def channel(name)
167
+ find_channel(scopes, name)
168
+ end
169
+
161
170
  def containers
162
171
  find_containers(@node ? [@node] : @structure)
163
172
  end
@@ -170,6 +179,10 @@ module Pakyow
170
179
  find_scopes(@node ? [@node] : @structure)
171
180
  end
172
181
 
182
+ def components
183
+ find_components(@node ? [@node] : @structure)
184
+ end
185
+
173
186
  def to_html
174
187
  StringDocRenderer.render(@node ? [@node] : @structure)
175
188
  end
@@ -186,8 +199,15 @@ module Pakyow
186
199
  return @node || @structure[0]
187
200
  end
188
201
 
202
+ def node_index
203
+ return nil unless node?
204
+ @structure.index { |n| n.equal?(@node) }
205
+ end
206
+
189
207
  def node?
190
- !@node.nil?
208
+ return false if @node.nil?
209
+ return false if @removed
210
+ return true
191
211
  end
192
212
 
193
213
  def tagname
@@ -200,6 +220,10 @@ module Pakyow
200
220
  })
201
221
  end
202
222
 
223
+ def exists?
224
+ @structure.include?(node)
225
+ end
226
+
203
227
  private
204
228
 
205
229
  def title_search
@@ -217,8 +241,16 @@ module Pakyow
217
241
  node[1]
218
242
  end
219
243
 
244
+ def has_attribute?(name)
245
+ attributes.key?(name)
246
+ end
247
+
220
248
  def children
221
- node[2][0][2]
249
+ if @structure.empty?
250
+ @structure
251
+ else
252
+ node[2][0][2]
253
+ end
222
254
  end
223
255
 
224
256
  def find_containers(structure, primary_structure = @structure, containers = {})
@@ -245,12 +277,18 @@ module Pakyow
245
277
  def find_scopes(structure, primary_structure = @structure, scopes = [])
246
278
  ret_scopes = structure.inject(scopes) { |s, e|
247
279
  if e[1].has_key?(:'data-scope')
248
- s << {
280
+ scope = {
249
281
  doc: StringDoc.from_structure(primary_structure, node: e),
250
282
  scope: e[1][:'data-scope'].to_sym,
251
283
  props: find_node_props(e).concat(find_props(e[2])),
252
284
  nested: find_scopes(e[2]),
253
285
  }
286
+
287
+ if version = e[1][:'data-version']
288
+ scope[:version] = version.to_sym
289
+ end
290
+
291
+ s << scope
254
292
  end
255
293
  # only find scopes if `e` is the root node or we're not decending into a nested scope
256
294
  find_scopes(e[2], e[2], s) if e == node || !e[1].has_key?(:'data-scope')
@@ -282,6 +320,35 @@ module Pakyow
282
320
 
283
321
  props
284
322
  end
323
+
324
+ def find_channel(scopes, name)
325
+ scopes.each do |scope|
326
+ if scope[:doc].get_attribute(:'data-channel') == name
327
+ return scope[:doc]
328
+ end
329
+
330
+ if doc = find_channel(scope[:nested], name)
331
+ return doc
332
+ end
333
+ end
334
+
335
+ nil
336
+ end
337
+
338
+ def find_components(structure, primary_structure = @structure, components = [])
339
+ ret_components = structure.inject(components) { |s, e|
340
+ if e[1].has_key?(:'data-ui')
341
+ s << {
342
+ doc: StringDoc.from_structure(primary_structure, node: e),
343
+ component: e[1][:'data-ui'].to_sym,
344
+ }
345
+ end
346
+ find_components(e[2], e[2], s)
347
+ s
348
+ } || []
349
+
350
+ ret_components
351
+ end
285
352
  end
286
353
  end
287
354
  end