pakyow-presenter 0.8.rc4 → 0.8.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: bf40bbef34bd10129e0986f8009446fe4c9c427a
4
- data.tar.gz: afa46cf16a50b2277a1e901d8e021ed8ef7865ce
3
+ metadata.gz: 833e0e238ad912016ef2aa6b0a0745754a6507b3
4
+ data.tar.gz: d73f4a20f79596cb69f34d11a43ef9c1ebd2eac9
5
5
  SHA512:
6
- metadata.gz: 044a33ab081707cd3ac31e43185a7f1f7cfb742f25fd74862fe8b8ce31a4f5fed1749eed120ffac43fd962c0350ff62efe80410e20325c853ec5873a2a5121fb
7
- data.tar.gz: 457ca686a0b5ed1cb98aaaf6cd10406143a42eb6fb12996e32d9fe2301297e2817c267bd8de7d9015978880474560dbdcc49c23899a2830be20038fe94a26575
6
+ metadata.gz: bab66d8a3f87d2c57b67e2cc4670d401b229c1c1fff8b2e1e0c87a286170d5d722893ab2d90d77bd2a3e96eb405d076fcfeaf0b8d9b6d4db3115ce780bafc04b
7
+ data.tar.gz: 5fa4f5ee5cebed37990c3299e91652c7f23a763b726106309449e4104d14bc75dbd351bc4cdc63148f343418b7445b4c7a847bbd4ecb1333e987224d15e083bd
@@ -1,3 +1,7 @@
1
+ = 0.8.0 / 2014-03-02
2
+
3
+ * Major rewrite, including new view syntax, composer, and transformation api
4
+
1
5
  = 0.7.2 / 2012-02-29
2
6
 
3
7
  * No changes -- bumped version to be consistent
@@ -35,6 +35,20 @@ module Pakyow
35
35
  update_value
36
36
  end
37
37
 
38
+ # opposite of `ensure`
39
+ def deny(value)
40
+ case @type
41
+ when :mult
42
+ @value.delete(value.to_s)
43
+ when :bool
44
+ @value = !value
45
+ else
46
+ @value.gsub!(value.to_s, '')
47
+ end
48
+
49
+ update_value
50
+ end
51
+
38
52
  # passes method call to `value`
39
53
  def method_missing(method, *args)
40
54
  ret = @value.send(method, *args)
@@ -103,18 +117,24 @@ module Pakyow
103
117
  end
104
118
 
105
119
  class Attributes
106
- def initialize(doc)
120
+ def initialize(doc, composer = nil)
107
121
  @doc = doc
108
122
  @attributes = {}
123
+ @composer = composer
109
124
  end
110
125
 
111
126
  def method_missing(method, *args)
112
- attribute = method.to_s
127
+ method_str = method.to_s
113
128
 
114
- if method.to_s.include?('=')
115
- attribute = attribute.gsub('=', '')
129
+ if method_str[0..1] == '[]'
130
+ attribute = args[0]
131
+ value = args[1]
132
+ else
133
+ attribute = method_str.gsub('=', '')
116
134
  value = args[0]
135
+ end
117
136
 
137
+ if method_str.include?('=')
118
138
  self.set_attribute(attribute, value)
119
139
  else
120
140
  self.get_attribute(attribute)
@@ -139,12 +159,14 @@ module Pakyow
139
159
 
140
160
  def remove_attribute(attribute)
141
161
  @doc.remove_attribute(attribute)
162
+ @composer.dirty! unless @composer.nil?
142
163
  end
143
164
 
144
165
  protected
145
166
 
146
167
  def set_attribute(attribute, value)
147
168
  get_attribute(attribute).set(value)
169
+ @composer.dirty! unless @composer.nil?
148
170
  end
149
171
 
150
172
  def get_attribute(attribute)
@@ -168,9 +190,11 @@ module Pakyow
168
190
  end
169
191
 
170
192
  def method_missing(method, *args)
171
- ret = []
172
- @attributes.each{|a| ret << a.send(method, *args)}
173
- ret
193
+ @attributes.inject(AttributesCollection.new) { |coll, a| coll<< a.send(method, *args) }
194
+ end
195
+
196
+ def to_s
197
+ @attributes.inject([]) { |arr, a| arr << a.to_s }
174
198
  end
175
199
 
176
200
  def class(*args)
@@ -1,15 +1,18 @@
1
1
  require 'yaml'
2
2
  require 'presenter/view_store'
3
3
  require 'presenter/doc_helpers'
4
+ require 'presenter/title_helpers'
4
5
  require 'presenter/view'
5
6
  require 'presenter/template'
6
7
  require 'presenter/page'
8
+ require 'presenter/container'
7
9
  require 'presenter/partial'
8
10
  require 'presenter/view_collection'
9
11
  require 'presenter/binder'
10
12
  require 'presenter/binder_set'
11
13
  require 'presenter/attributes'
12
14
  require 'presenter/exceptions'
15
+ require 'presenter/view_composer'
13
16
 
14
17
  module Pakyow
15
18
  module Presenter
@@ -25,7 +25,8 @@ module Pakyow
25
25
  @sets[name].instance_exec(&block)
26
26
  end
27
27
 
28
- def value_for_prop(prop, scope, bindable, bindings = {})
28
+ def value_for_prop(prop, scope, bindable, bindings = {}, context)
29
+ @context = context
29
30
  binding = nil
30
31
  @sets.each {|set|
31
32
  binding = set[1].match_for_prop(prop, scope, bindable, bindings)
@@ -33,12 +34,15 @@ module Pakyow
33
34
  }
34
35
 
35
36
  if binding
37
+ binding_eval = BindingEval.new(prop, bindable, context)
38
+
36
39
  case binding.arity
37
- when 0
38
- binding_eval = BindingEval.new(bindable)
39
- binding_eval.instance_exec(&binding)
40
- when 1
41
- self.instance_exec(bindable, &binding)
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)
42
46
  end
43
47
  else
44
48
  # default
@@ -20,9 +20,9 @@ module Pakyow
20
20
  return bindings_for_scope(scope, bindings)[prop]
21
21
  end
22
22
 
23
- def options_for_prop(prop, scope, bindable)
23
+ def options_for_prop(prop, scope, bindable, context)
24
24
  if block = (@options[scope] || {})[prop]
25
- binding_eval = BindingEval.new(bindable)
25
+ binding_eval = BindingEval.new(bindable, prop, context)
26
26
  binding_eval.instance_exec(&block)
27
27
  end
28
28
  end
@@ -65,10 +65,10 @@ module Pakyow
65
65
 
66
66
  if id = bindable[:id]
67
67
  return_data[:view] = lambda { |view|
68
- view.prepend(View.from_doc(Nokogiri::HTML.fragment('<input type="hidden" name="_method" value="put">')))
68
+ view.prepend(View.from_doc(Nokogiri::HTML.fragment('<input type="hidden" name="_method" value="patch">')))
69
69
  }
70
70
 
71
- action = routes.path(:update, :id => id)
71
+ action = routes.path(:update, :"#{route_group}_id" => id)
72
72
  else
73
73
  action = routes.path(:create)
74
74
  end
@@ -85,10 +85,17 @@ module Pakyow
85
85
  class BindingEval
86
86
  include Helpers
87
87
 
88
+ attr_accessor :context
88
89
  attr_reader :bindable
89
90
 
90
- def initialize(bindable)
91
+ def initialize(prop, bindable, context)
92
+ @prop = prop
91
93
  @bindable = bindable
94
+ @context = context
95
+ end
96
+
97
+ def value
98
+ bindable[@prop]
92
99
  end
93
100
  end
94
101
  end
@@ -0,0 +1,6 @@
1
+ module Pakyow
2
+ module Presenter
3
+ class Container < View
4
+ end
5
+ end
6
+ end
@@ -57,6 +57,9 @@ module Pakyow
57
57
  view = View.from_doc(doc_from_path(path))
58
58
  view.related_views << self
59
59
 
60
+ # workaround for nokogiri in jruby (see https://github.com/sparklemotion/nokogiri/issues/1060)
61
+ view.doc.document.errors = []
62
+
60
63
  return view
61
64
  end
62
65
 
@@ -65,6 +68,10 @@ module Pakyow
65
68
  end
66
69
 
67
70
  alias :to_s :to_html
71
+
72
+ def ==(o)
73
+ self.class == o.class && self.to_html == o.to_html
74
+ end
68
75
  end
69
76
  end
70
77
  end
@@ -3,5 +3,9 @@ module Pakyow
3
3
  class MissingTemplatesDir < Error; end
4
4
  class MissingView < Error; end
5
5
  class MissingTemplate < Error; end
6
+ class MissingPage < Error; end
7
+ class MissingPartial < Error; end
8
+ class MissingComposer < Error; end
9
+ class MissingContainer < Error; end
6
10
  end
7
11
  end
@@ -1,5 +1,11 @@
1
1
  module Pakyow
2
2
  module AppHelpers
3
+ extend Forwardable
4
+
5
+ def_delegators :@presenter, :store, :store=, :content, :view, :view=,
6
+ :partial, :template, :template=, :page, :page=, :path, :path=, :compose,
7
+ :composer, :container
8
+
3
9
  def presenter
4
10
  @presenter
5
11
  end
@@ -7,9 +13,5 @@ module Pakyow
7
13
  def bindings(name)
8
14
  presenter.bindings(name).bindings
9
15
  end
10
-
11
- def view
12
- presenter.view
13
- end
14
16
  end
15
17
  end
@@ -5,7 +5,7 @@ module Pakyow
5
5
 
6
6
  class << self
7
7
  def load(path)
8
- format = StringUtils.split_at_last_dot(path)[-1]
8
+ format = Utils::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
 
@@ -13,65 +13,66 @@ module Pakyow
13
13
  end
14
14
  end
15
15
 
16
- attr_reader :contents, :path
16
+ attr_reader :path, :contents
17
+ attr_accessor :composer
17
18
 
18
19
  def initialize(name, contents, path, format = :html)
19
20
  @name, @contents, @path, @format = name, contents, path, format
20
21
 
21
- @info = {}
22
- @content = {}
22
+ @info = { template: :pakyow }
23
+ @containers = {}
23
24
 
24
25
  unless @contents.nil?
25
26
  parse_info
26
27
  parse_content
27
28
  end
28
-
29
- partials
30
29
  end
31
30
 
32
31
  def initialize_copy(original_page)
33
32
  super
34
33
 
35
34
  # copy content
36
- @content = {}
37
- original_page.content.each_pair do |k, v|
38
- @content[k] = v.dup
35
+ @containers = {}
36
+ original_page.instance_variable_get(:'@containers').each_pair do |k, v|
37
+ @containers[k] = v.dup
39
38
  end
40
39
  end
41
40
 
42
- def build(partial_map)
43
- partials.each do |container, partial_list|
44
- partial_list.each do |partial_name|
45
- regex = /<!--\s*@include\s*#{partial_name}\s*-->/
46
- @content[container].gsub!(regex, partial_map[partial_name].to_s)
41
+ def content(container = nil)
42
+ return @contents if container.nil?
47
43
 
48
- partial_map.delete(partial_name)
49
- end
50
- end
44
+ container = @containers.fetch(container.to_sym) {
45
+ raise MissingContainer, "No container named #{container} in #{@path}"
46
+ }
51
47
 
52
- # we have more partials
53
- if partial_map.count > 0
54
- # initiate another build if content contains partials
55
- build(partial_map) if partials(true).count > 0
56
- end
48
+ return container.to_html
49
+ end
57
50
 
58
- return self
51
+ def info(key = nil)
52
+ return @info if key.nil?
53
+ return @info[key]
59
54
  end
60
55
 
61
- def content(container = nil)
62
- return container.nil? ? @content : @content[container.to_sym]
56
+ def ==(page)
57
+ @contents == page.contents
63
58
  end
64
59
 
65
- def template
66
- @info[:template] || :pakyow
60
+ def container(name)
61
+ @containers.fetch(name.to_sym) {
62
+ raise MissingContainer, "No container named #{name} in #{@path}"
63
+ }
67
64
  end
68
65
 
69
- def ==(page)
70
- @contents == page.contents
66
+ def each_container
67
+ @containers.each_pair { |name, container| yield(name, container) }
71
68
  end
72
69
 
73
- def partials(refind = false)
74
- @partials = (!@partials || refind) ? find_partials : @partials
70
+ def composer=(composer)
71
+ @composer = composer
72
+
73
+ @containers.each do |name, container|
74
+ container.composer = composer
75
+ end
75
76
  end
76
77
 
77
78
  private
@@ -80,7 +81,7 @@ module Pakyow
80
81
  info = parse_front_matter(@contents)
81
82
  info = {} if !info || !info.is_a?(Hash)
82
83
 
83
- @info = HashUtils.symbolize(info)
84
+ @info.merge!(Utils::Hash.symbolize(info))
84
85
  end
85
86
 
86
87
  def parse_content
@@ -94,32 +95,24 @@ module Pakyow
94
95
  within_regex = /<!--\s*@within\s*([a-zA-Z0-9\-_]*)\s*-->(.*?)<!--\s*\/within\s*-->/m
95
96
 
96
97
  @contents.scan(within_regex) do |m|
97
- container = m[0].to_sym
98
- @content[container] = m[1]
98
+ container_name = m[0].to_sym
99
+ @containers[container_name] = Container.new(m[1], @format)
99
100
  end
100
101
 
101
102
  # find default content
102
- @content[:default] = @contents.gsub(within_regex, '')
103
+ @containers[:default] = Container.new(@contents.gsub(within_regex, ''), @format)
103
104
  end
104
105
 
105
- # returns an array of hashes, each with the partial name and doc
106
- def find_partials
107
- partials = {}
108
-
109
- partial_regex = /<!--\s*@include\s*([a-zA-Z0-9\-_]*)\s*-->/
110
- @content.each do |name, content|
111
- content.scan(partial_regex) do |m|
112
- partials[name] ||= []
113
- partials[name] << m[0].to_sym
114
- end
115
- end
106
+ def parse_front_matter(contents)
107
+ # match the matter
108
+ matter = contents.match(MATTER_MATCHER).to_s
116
109
 
117
- return partials
118
- end
110
+ # remove the opening/closing '---'
111
+ matter = matter.split("\n")[1..-2]
112
+ # return if no matter
113
+ return {} if matter.nil?
119
114
 
120
- def parse_front_matter(contents)
121
- matter = YAML.load(contents.match(MATTER_MATCHER).to_s)
122
- return matter
115
+ YAML.load(matter.join("\n"))
123
116
  end
124
117
  end
125
118
  end
@@ -1,24 +1,6 @@
1
1
  module Pakyow
2
2
  module Presenter
3
- class Partial
4
- include DocHelpers
5
-
6
- class << self
7
- def load(path)
8
- format = StringUtils.split_at_last_dot(path)[-1]
9
- contents = File.read(path)
10
- name = File.basename(path, '.*')[1..-1].to_sym
11
-
12
- return self.new(name, contents, format)
13
- end
14
- end
15
-
16
- def initialize(name, contents, format = :html)
17
- @name = name
18
-
19
- processed = Presenter.process(contents, format)
20
- @doc = Nokogiri::HTML.fragment(processed)
21
- end
3
+ class Partial < View
22
4
  end
23
5
  end
24
6
  end
@@ -28,13 +28,13 @@ module Pakyow
28
28
 
29
29
  Pakyow::App.after(:match) {
30
30
  @presenter = Pakyow.app.presenter.dup
31
- @presenter.prepare_for_request(@request)
31
+ @presenter.prepare_with_context(context)
32
32
  }
33
33
 
34
34
  Pakyow::App.after(:route) {
35
35
  if @presenter.presented?
36
36
  @found = true
37
- @response.body = [@presenter.content]
37
+ @context.response.body = [@presenter.content]
38
38
  else
39
39
  @found = false unless found?
40
40
  end
@@ -46,22 +46,16 @@ module Pakyow
46
46
 
47
47
  Pakyow::App.after(:error) {
48
48
  unless config.app.errors_in_browser
49
- @response.body = [@presenter.content] if @presenter.presented?
49
+ @context.response.body = [@presenter.content] if @presenter.presented?
50
50
  end
51
51
  }
52
52
 
53
- attr_accessor :processor_store, :binder, :path, :template, :page
53
+ attr_accessor :processor_store, :binder, :path, :context, :composer
54
54
 
55
55
  def initialize
56
56
  setup
57
57
  end
58
58
 
59
- def setup
60
- @view, @template, @page = nil
61
- @constructed = false
62
- self.store = :default
63
- end
64
-
65
59
  def store(name = nil)
66
60
  if name
67
61
  @view_stores[name]
@@ -73,8 +67,8 @@ module Pakyow
73
67
  def store=(name)
74
68
  @store = name
75
69
 
76
- return unless @path
77
- setup_for_path
70
+ return unless has_path?
71
+ setup_for_path(@path)
78
72
  end
79
73
 
80
74
  def load
@@ -83,85 +77,107 @@ module Pakyow
83
77
  load_bindings
84
78
  end
85
79
 
86
- def prepare_for_request(request)
87
- @request = request
80
+ def prepare_with_context(context)
81
+ @context = context
88
82
 
89
- if @request.has_route_vars?
90
- @path = StringUtils.remove_route_vars(@request.route_path)
83
+ if @context.request.has_route_vars?
84
+ @path = Utils::String.remove_route_vars(@context.request.route_path)
91
85
  else
92
- @path = @request.path
86
+ @path = @context.request.path
93
87
  end
94
88
 
95
89
  setup
96
90
  end
97
91
 
98
92
  def presented?
99
- ensure_construction
100
- @constructed
93
+ !view.nil?
94
+ rescue MissingView
95
+ false
101
96
  end
102
97
 
103
98
  def content
104
- return unless view
105
- view.to_html
99
+ to_present = view
100
+ view.is_a?(ViewComposer) ? view.composed.to_html : view.to_html
106
101
  end
107
102
 
108
103
  def view
109
- ensure_construction
110
- @view
111
- end
104
+ view = @composer || @view
105
+ raise MissingView if view.nil?
112
106
 
113
- def partial(name)
114
- store.partial(@path, name)
107
+ view.context = @context
108
+
109
+ return view
115
110
  end
116
111
 
117
112
  def view=(view)
118
113
  @view = view
119
- @constructed = true
114
+ @view.context = @context
115
+
116
+ # setting a view means we no longer use/need the composer
117
+ @composer = nil
120
118
  end
121
119
 
122
120
  def template=(template)
123
- unless template.is_a?(Template)
124
- # get template by name
125
- template = store.template(template)
126
- end
121
+ raise MissingComposer 'Cannot set template without a composer' if @composer.nil?
122
+ @composer.template = template
123
+ end
127
124
 
128
- @template = template
129
- @constructed = false
125
+ def template
126
+ raise MissingComposer 'Cannot get template without a composer' if @composer.nil?
127
+ @composer.template
130
128
  end
131
129
 
132
130
  def page=(page)
133
- @page = page
134
- @constructed = false
131
+ raise MissingComposer, 'Cannot set page without a composer' if @composer.nil?
132
+ @composer.page = page
133
+ end
134
+
135
+ def page
136
+ raise MissingComposer 'Cannot get page without a composer' if @composer.nil?
137
+ @composer.page
138
+ end
139
+
140
+ def container(name)
141
+ raise MissingComposer 'Cannot get container without a composer' if @composer.nil?
142
+ @composer.container(name)
143
+ end
144
+
145
+ def partial(name)
146
+ raise MissingComposer 'Cannot get partial without a composer' if @composer.nil?
147
+ @composer.partial(name)
135
148
  end
136
149
 
137
150
  def path=(path)
138
- @path = path
139
- setup_for_path(true)
151
+ setup_for_path(path, true)
140
152
  end
141
153
 
142
- def ensure_construction
143
- # only construct once
144
- return if @constructed
154
+ def compose(opts = {}, &block)
155
+ compose_at(@path, opts, &block)
156
+ end
145
157
 
146
- # if no template/page was found, we can't construct
147
- return if @template.nil? || @page.nil?
158
+ def compose_at(path, opts = {}, &block)
159
+ composer = ViewComposer.from_path(store, path, opts, &block)
160
+ return composer unless opts.empty? || block_given?
148
161
 
149
- # construct
150
- @view = @template.dup.build(@page)
151
- @constructed = true
162
+ @composer = composer
163
+ end
164
+
165
+ def has_path?
166
+ !@path.nil?
152
167
  end
153
168
 
154
169
  protected
155
170
 
156
- def setup_for_path(explicit = false)
157
- self.template = store.template(@path)
158
- self.page = store.page(@path)
159
- self.view = store.view(@path)
171
+ def setup
172
+ @view, @composer = nil
173
+ self.store = :default
174
+ end
160
175
 
161
- @constructed = true
176
+ def setup_for_path(path, explicit = false)
177
+ @composer = store.composer(path)
178
+ @path = path
162
179
  rescue MissingView => e # catches no view path error
163
180
  explicit ? raise(e) : Pakyow.logger.debug(e.message)
164
- @constructed = false
165
181
  end
166
182
 
167
183
  def load_views