pakyow-presenter 0.8.0 → 0.9.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 833e0e238ad912016ef2aa6b0a0745754a6507b3
4
- data.tar.gz: d73f4a20f79596cb69f34d11a43ef9c1ebd2eac9
3
+ metadata.gz: f3b7aee6ee2692ce29bd4e2bdaa323fae1aedef3
4
+ data.tar.gz: 1e743f0597314c2a52fcc32a2c4f490b62eb1db8
5
5
  SHA512:
6
- metadata.gz: bab66d8a3f87d2c57b67e2cc4670d401b229c1c1fff8b2e1e0c87a286170d5d722893ab2d90d77bd2a3e96eb405d076fcfeaf0b8d9b6d4db3115ce780bafc04b
7
- data.tar.gz: 5fa4f5ee5cebed37990c3299e91652c7f23a763b726106309449e4104d14bc75dbd351bc4cdc63148f343418b7445b4c7a847bbd4ecb1333e987224d15e083bd
6
+ metadata.gz: dd7aa6575f476fe52a680d9b7fe0a4465337556323b1bdc03ff0997aa6f6bbc8fdd71b3137b678c1239d626b3bd5b002f28fc81cbe9ce0a56ba6dc38137e48df
7
+ data.tar.gz: 1d0e79dcdd3c8910cd6af4f76906bd1b26575f33e83759dae61414766b55c0ba26d7e1380939f5a0bfb885de9c160572984623b62b112d6083e2210e5b0e7a13
@@ -1,3 +1,13 @@
1
+ = 0.9.0
2
+
3
+ * Introduces StringDoc, replacing Nokogiri for view rendering with a performant alternative
4
+ * Adds the ability to set an empty first option in bindings
5
+ * Remove the annoying "no content" for container log message
6
+ * Binding context must now be explicitly passed when binding
7
+ * Yield bindable value before bindable in view binding fns
8
+ * Fixes bug causing non-html view contents to be processed twice
9
+ * Removes support for Ruby versions < 2.0.0
10
+
1
11
  = 0.8.0 / 2014-03-02
2
12
 
3
13
  * Major rewrite, including new view syntax, composer, and transformation api
@@ -38,4 +48,4 @@
38
48
 
39
49
  = 0.6.0 / 2011-08-20
40
50
 
41
- * Initial gem release of 0.6.0 codebase
51
+ * Initial gem release of 0.6.0 codebase
@@ -53,14 +53,13 @@ module Pakyow
53
53
  def method_missing(method, *args)
54
54
  ret = @value.send(method, *args)
55
55
  update_value
56
-
57
56
  return ret
58
57
  end
59
58
 
60
59
  # returns the full string value
61
60
  def to_s
62
61
  value = @value
63
- value = value.call(deconstruct_attribute_value_of_type(@doc[@name], @type)) if value.is_a? Proc
62
+ value = value.call(deconstruct_attribute_value_of_type(@doc.get_attribute(@name), @type)) if value.is_a? Proc
64
63
  value = construct_attribute_value_of_type(value, @type, @name)
65
64
 
66
65
  return value
@@ -70,6 +69,10 @@ module Pakyow
70
69
  @value
71
70
  end
72
71
 
72
+ def ancestors
73
+ self.class.ancestors
74
+ end
75
+
73
76
  private
74
77
 
75
78
  def update_value
@@ -117,10 +120,9 @@ module Pakyow
117
120
  end
118
121
 
119
122
  class Attributes
120
- def initialize(doc, composer = nil)
123
+ def initialize(doc)
121
124
  @doc = doc
122
125
  @attributes = {}
123
- @composer = composer
124
126
  end
125
127
 
126
128
  def method_missing(method, *args)
@@ -144,34 +146,28 @@ module Pakyow
144
146
  def class(*args)
145
147
  method_missing(:class, *args)
146
148
  end
147
-
149
+
148
150
  def id(*args)
149
151
  method_missing(:id, *args)
150
152
  end
151
153
 
152
154
  def update_value_for_attribute(attribute, value)
153
- if !@doc.is_a?(Nokogiri::XML::Element) && @doc.children.count == 1
154
- @doc.children.first[attribute] = value
155
- else
156
- @doc[attribute] = value
157
- end
155
+ @doc.update_attribute(attribute, value)
158
156
  end
159
157
 
160
158
  def remove_attribute(attribute)
161
159
  @doc.remove_attribute(attribute)
162
- @composer.dirty! unless @composer.nil?
163
160
  end
164
161
 
165
162
  protected
166
163
 
167
164
  def set_attribute(attribute, value)
168
165
  get_attribute(attribute).set(value)
169
- @composer.dirty! unless @composer.nil?
170
166
  end
171
167
 
172
168
  def get_attribute(attribute)
173
169
  unless a = @attributes[attribute]
174
- a = Attribute.new(attribute, @doc[attribute], self, @doc)
170
+ a = Attribute.new(attribute, @doc.get_attribute(attribute), self, @doc)
175
171
  end
176
172
 
177
173
  return a
@@ -187,10 +183,17 @@ module Pakyow
187
183
 
188
184
  def <<(attributes)
189
185
  @attributes << attributes
186
+ self
187
+ end
188
+
189
+ def each
190
+ @attributes.each { |a| yield(a) }
190
191
  end
191
192
 
192
193
  def method_missing(method, *args)
193
- @attributes.inject(AttributesCollection.new) { |coll, a| coll<< a.send(method, *args) }
194
+ @attributes.inject(AttributesCollection.new) { |coll, a|
195
+ coll << a.send(method, *args)
196
+ }
194
197
  end
195
198
 
196
199
  def to_s
@@ -200,7 +203,7 @@ module Pakyow
200
203
  def class(*args)
201
204
  method_missing(:class, *args)
202
205
  end
203
-
206
+
204
207
  def id(*args)
205
208
  method_missing(:id, *args)
206
209
  end
@@ -1,7 +1,6 @@
1
1
  require 'yaml'
2
+
2
3
  require 'presenter/view_store'
3
- require 'presenter/doc_helpers'
4
- require 'presenter/title_helpers'
5
4
  require 'presenter/view'
6
5
  require 'presenter/template'
7
6
  require 'presenter/page'
@@ -13,6 +12,13 @@ require 'presenter/binder_set'
13
12
  require 'presenter/attributes'
14
13
  require 'presenter/exceptions'
15
14
  require 'presenter/view_composer'
15
+ require 'presenter/nokogiri_doc'
16
+ require 'presenter/string_doc'
17
+ require 'presenter/string_doc_parser'
18
+ require 'presenter/string_doc_renderer'
19
+ require 'presenter/binding_eval'
20
+ require 'presenter/doc_helpers'
21
+ require 'presenter/view_context'
16
22
 
17
23
  module Pakyow
18
24
  module Presenter
@@ -1,78 +1,88 @@
1
+ require 'singleton'
2
+
1
3
  module Pakyow
2
4
  module Presenter
3
- # A singleton that manages route sets.
5
+ # A singleton that manages BinderSet instances for an app. It handles
6
+ # the creation / registration of sets and provides a mechanism
7
+ # to augment data to be bound with values from the sets.
4
8
  #
5
9
  class Binder
6
10
  include Singleton
7
- include Helpers
8
11
 
12
+ # Access to the registered binder sets for an app.
13
+ #
9
14
  attr_reader :sets
10
15
 
11
16
  def initialize
12
17
  @sets = {}
13
18
  end
14
19
 
15
- #TODO want to do this for all sets?
20
+ # Resets the registered binder sets.
21
+ #
22
+ # @return [Binder] the reset instance
23
+ #
16
24
  def reset
17
25
  @sets = {}
18
26
  self
19
27
  end
20
28
 
21
- # Creates a new set.
29
+ # Creates and registers a new binder set by name. A block should be passed
30
+ # that defines the bindings. This block will be evaluated in context
31
+ # of the created binder set.
32
+ #
33
+ # @see BinderSet
34
+ #
35
+ # @param name [Symbol] the name of the binder set to be created
22
36
  #
23
37
  def set(name, &block)
24
- @sets[name] = BinderSet.new
25
- @sets[name].instance_exec(&block)
38
+ @sets[name] = BinderSet.new(&block)
26
39
  end
27
40
 
28
- def value_for_prop(prop, scope, bindable, bindings = {}, context)
29
- @context = context
30
- binding = nil
31
- @sets.each {|set|
32
- binding = set[1].match_for_prop(prop, scope, bindable, bindings)
33
- break if binding
41
+ # Returns the value for the scope->prop by applying any defined bindings to the data.
42
+ #
43
+ # @param scope [Symbol] the scope name
44
+ # @param prop [Symbol] the prop name
45
+ # @param bindable [Symbol] the data being bound
46
+ # @param bindings [Symbol] additional bindings to take into consideration when determining the value
47
+ # @param context [Symbol] context passed through to the defined bindings
48
+ #
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?
34
54
  }
35
55
 
36
- if binding
56
+ if binding_fn
37
57
  binding_eval = BindingEval.new(prop, bindable, context)
38
-
39
- case binding.arity
40
- when 0
41
- binding_eval.instance_exec(&binding)
42
- when 1
43
- self.instance_exec(bindable, &binding)
44
- when 2
45
- self.instance_exec(bindable, binding_eval.value, &binding)
58
+ binding_eval.instance_exec(binding_eval.value, bindable, context, &binding_fn)
59
+ else # default value
60
+ if bindable.is_a?(Hash)
61
+ bindable[prop]
62
+ elsif bindable.class.method_defined?(prop)
63
+ bindable.send(prop)
46
64
  end
47
- else
48
- # default
49
- prop_value_for_bindable(bindable, prop)
50
65
  end
51
66
  end
52
67
 
53
- def prop_value_for_bindable(bindable, prop)
54
- return bindable[prop] if bindable.is_a?(Hash)
55
- return bindable.send(prop) if bindable.class.method_defined?(prop)
56
- end
57
-
58
- def options_for_prop(*args)
59
- match = nil
60
- @sets.each {|set|
61
- match = set[1].options_for_prop(*args)
62
- break if match
68
+ # Returns true if a binding is defined for the scope->prop.
69
+ #
70
+ def has_scoped_prop?(scope, prop, bindings)
71
+ @sets.lazy.map { |set|
72
+ set[1].has_prop?(scope, prop, bindings)
73
+ }.find { |has_prop|
74
+ has_prop
63
75
  }
64
-
65
- return match
66
76
  end
67
77
 
68
- def has_prop?(*args)
69
- has = nil
70
- @sets.each {|set|
71
- has = set[1].has_prop?(*args)
72
- break if has
78
+ # Returns options for the scope->prop.
79
+ #
80
+ def options_for_scoped_prop(scope, prop, bindable, context)
81
+ @sets.lazy.map { |set|
82
+ set[1].options_for_prop(scope, prop, bindable, context)
83
+ }.find { |options|
84
+ !options.nil?
73
85
  }
74
-
75
- return has
76
86
  end
77
87
  end
78
88
  end
@@ -3,31 +3,37 @@ module Pakyow
3
3
  class BinderSet
4
4
  attr_reader :scopes
5
5
 
6
- def initialize
6
+ def initialize(&block)
7
7
  @scopes = {}
8
8
  @options = {}
9
+ @config = {}
10
+
11
+ instance_exec(&block)
9
12
  end
10
13
 
11
14
  def scope(name, &block)
12
15
  scope_eval = ScopeEval.new
13
- bindings, options = scope_eval.eval(&block)
16
+ bindings, options, config = scope_eval.eval(&block)
14
17
 
15
18
  @scopes[name.to_sym] = bindings
16
19
  @options[name.to_sym] = options
20
+ @config[name.to_sym] = config
17
21
  end
18
22
 
19
23
  def match_for_prop(prop, scope, bindable, bindings = {})
20
24
  return bindings_for_scope(scope, bindings)[prop]
21
25
  end
22
26
 
23
- def options_for_prop(prop, scope, bindable, context)
27
+ def options_for_prop(scope, prop, bindable, context)
24
28
  if block = (@options[scope] || {})[prop]
25
- binding_eval = BindingEval.new(bindable, prop, context)
26
- binding_eval.instance_exec(&block)
29
+ binding_eval = BindingEval.new(prop, bindable, context)
30
+ values = binding_eval.instance_exec(binding_eval.value, bindable, context, &block)
31
+ values.unshift(['', '']) if @config[scope][prop][:empty]
32
+ values
27
33
  end
28
34
  end
29
35
 
30
- def has_prop?(prop, scope, bindings)
36
+ def has_prop?(scope, prop, bindings)
31
37
  bindings_for_scope(scope, bindings).key? prop
32
38
  end
33
39
 
@@ -43,29 +49,31 @@ module Pakyow
43
49
  def initialize
44
50
  @bindings = {}
45
51
  @options = {}
52
+ @config = {}
46
53
  end
47
54
 
48
55
  def eval(&block)
49
56
  self.instance_eval(&block)
50
- return @bindings, @options
57
+ return @bindings, @options, @config
51
58
  end
52
59
 
53
60
  def binding(name, &block)
54
61
  @bindings[name.to_sym] = block
55
62
  end
56
63
 
57
- def options(name, &block)
64
+ def options(name, empty: false, &block)
58
65
  @options[name] = block
66
+ @config[name] = { empty: empty }
59
67
  end
60
68
 
61
69
  def restful(route_group)
62
70
  binding(:action) {
63
- routes = router.group(route_group)
71
+ routes = Router.instance.group(route_group)
64
72
  return_data = {}
65
73
 
66
74
  if id = bindable[:id]
67
75
  return_data[:view] = lambda { |view|
68
- view.prepend(View.from_doc(Nokogiri::HTML.fragment('<input type="hidden" name="_method" value="patch">')))
76
+ view.prepend(View.from_doc(NokogiriDoc.from_doc(Nokogiri::HTML.fragment('<input type="hidden" name="_method" value="patch">'))))
69
77
  }
70
78
 
71
79
  action = routes.path(:update, :"#{route_group}_id" => id)
@@ -81,22 +89,5 @@ module Pakyow
81
89
 
82
90
  #TODO options
83
91
  end
84
-
85
- class BindingEval
86
- include Helpers
87
-
88
- attr_accessor :context
89
- attr_reader :bindable
90
-
91
- def initialize(prop, bindable, context)
92
- @prop = prop
93
- @bindable = bindable
94
- @context = context
95
- end
96
-
97
- def value
98
- bindable[@prop]
99
- end
100
- end
101
92
  end
102
93
  end
@@ -0,0 +1,23 @@
1
+ module Pakyow
2
+ module Presenter
3
+ class BindingEval
4
+ include Pakyow::Helpers
5
+
6
+ attr_reader :context, :bindable
7
+
8
+ def initialize(prop, bindable, context)
9
+ @prop = prop
10
+ @bindable = bindable
11
+ @context = context
12
+ end
13
+
14
+ def value
15
+ if bindable.is_a?(Hash)
16
+ bindable[@prop]
17
+ elsif bindable.respond_to?(@prop)
18
+ bindable.send(@prop)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,61 +1,36 @@
1
- module Pakyow
2
- module Config
3
- class Presenter
4
- Config::Base.register_config(:presenter, self)
5
-
6
- class << self
7
- attr_accessor :javascripts, :stylesheets, :view_stores, :default_views,
8
- :scope_attribute, :prop_attribute, :container_attribute, :template_dirs
9
-
10
- # Location of javascripts
11
- def javascripts
12
- @javascripts || '/javascripts'
13
- end
14
-
15
- # Location of stylesheets
16
- def stylesheets
17
- @stylesheets || '/stylesheets'
18
- end
19
-
20
- def view_stores
21
- @view_stores ||= {:default => "#{Config::Base.app.root}/app/views"}
22
- end
23
-
24
- def default_views(store_name = nil)
25
- @default_views ||= {:default => "pakyow.html"}
26
- end
27
-
28
- # Returns the default view for store, or default.
29
- #
30
- def default_view(store_name)
31
- views = default_views
32
- views.key?(store_name) ? views[store_name] : views[:default]
33
- end
34
-
35
- def template_dirs(store_name = nil)
36
- @template_dirs ||= {:default => '_templates'}
37
- end
38
-
39
- # Returns the default template dir for store, or default.
40
- #
41
- def template_dir(store_name)
42
- dirs = template_dirs
43
- dirs.key?(store_name) ? dirs[store_name] : dirs[:default]
44
- end
45
-
46
- def scope_attribute
47
- @scope_attribute || "data-scope"
48
- end
49
-
50
- def prop_attribute
51
- @prop_attribute || "data-prop"
52
- end
53
-
54
- def container_attribute
55
- @container_attribute || "data-container"
56
- end
57
-
58
- end
59
- end
60
- end
61
- end
1
+ Pakyow::Config.register(:presenter) { |config|
2
+
3
+ # registered view stores
4
+ config.opt :view_stores, lambda {
5
+ @stores ||= {
6
+ default: File.join(Pakyow::Config.app.root, 'app', 'views')
7
+ }
8
+ }
9
+
10
+ # the default view for each view store
11
+ config.opt :default_views, { default: 'pakyow.html' }
12
+
13
+ # a convenience option to lookup the default_view for a view store by name
14
+ config.opt :default_view, lambda { |store_name|
15
+ views = Pakyow::Config.presenter.default_views
16
+ views.fetch(store_name) { views[:default] }
17
+ }
18
+
19
+ # the default template dir for each view store
20
+ config.opt :template_dirs, { default: '_templates' }
21
+
22
+ # a convenience option to lookup the template_dir for a view store by name
23
+ config.opt :template_dir, lambda { |store_name|
24
+ dirs = Pakyow::Config.presenter.template_dirs
25
+ dirs.fetch(store_name) { dirs[:default] }
26
+ }
27
+
28
+ # the attribute expected for scope definitions
29
+ config.opt :scope_attribute, 'data-scope'
30
+
31
+ # the attribute expected for prop definitions
32
+ config.opt :prop_attribute, 'data-prop'
33
+
34
+ # the document class used to parse and render views
35
+ config.opt :view_doc_class, StringDoc
36
+ }