pakyow-presenter 0.8.rc4 → 0.8.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: 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