pakyow-presenter 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }