pakyow-presenter 0.6.3.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,22 +2,21 @@ module Pakyow
2
2
  module Presenter
3
3
  class Presenter < PresenterBase
4
4
  attr_accessor :current_context
5
-
5
+
6
+ def initialize
7
+ reset_state()
8
+ end
9
+
6
10
  #
7
11
  # Methods that are called by core. This is the interface that core expects a Presenter to have
8
12
  #
9
13
 
10
- def reload!
14
+ def load
11
15
  load_views
12
16
  end
13
-
14
- def present_for_request(request)
15
- @presented = false
16
- @root_path = nil
17
- @root_view_is_built = false
18
- @root_view = nil
19
- @view_path = nil
20
- @container_name = nil
17
+
18
+ def prepare_for_request(request)
19
+ reset_state()
21
20
  @request = request
22
21
  end
23
22
 
@@ -99,6 +98,25 @@ module Pakyow
99
98
  v
100
99
  end
101
100
 
101
+ # This is also for creating views from within a controller using the route based lookup mechanism.
102
+ # This method takes either a dir or file path and builds either a root_view or view, respectively.
103
+ def view_for_full_view_path(f_v_p, deep = false)
104
+ v = nil
105
+ real_path_info = @view_lookup_store.real_path_info(f_v_p)
106
+ if real_path_info
107
+ if real_path_info[:file_or_dir] == :file
108
+ v = View.new(real_path_info[:real_path])
109
+ elsif real_path_info[:file_or_dir] == :dir
110
+ root_view = @view_lookup_store.view_info(f_v_p)[:root_view]
111
+ v = View.new(root_view)
112
+ if v && deep
113
+ populate_view(v, @view_lookup_store.view_info(f_v_p)[:views])
114
+ end
115
+ end
116
+ end
117
+ v
118
+ end
119
+
102
120
  def populate_view_for_view_path(view, v_p)
103
121
  return view unless view_info = @view_lookup_store.view_info(v_p)
104
122
  views = view_info[:views]
@@ -124,27 +142,44 @@ module Pakyow
124
142
  #
125
143
  protected
126
144
  #
127
-
145
+
146
+ def reset_state
147
+ @presented = false
148
+ @root_path = nil
149
+ @root_view_is_built = false
150
+ @root_view = nil
151
+ @view_path = nil
152
+ @container_name = nil
153
+ end
154
+
128
155
  def build_root_view
129
156
  @root_view_is_built = true
130
157
 
131
158
  if @view_path
132
159
  v_p = @view_path
133
- elsif @request.restful
160
+ elsif @request && @request.restful
134
161
  v_p = restful_view_path(@request.restful)
135
- elsif @request.route_spec && @request.route_spec.index(':')
162
+ elsif @request && @request.route_spec && !@request.route_spec.is_a?(Regexp) && @request.route_spec.index(':')
136
163
  v_p = StringUtils.remove_route_vars(@request.route_spec)
137
164
  else
138
- v_p = @request.env['PATH_INFO']
165
+ v_p = @request && @request.working_path
139
166
  end
140
167
  return unless v_p
141
- return unless view_info = @view_lookup_store.view_info(v_p)
142
168
 
143
- @presented = true
144
- @root_path ||= view_info[:root_view]
145
- @root_view = LazyView.new(@root_path, true)
146
- views = view_info[:views]
147
- populate_view(self.view, views)
169
+ if Configuration::Base.presenter.view_caching
170
+ r_v = @populated_root_view_cache[v_p]
171
+ if r_v then
172
+ @root_view = r_v.dup
173
+ @presented = true
174
+ end
175
+ else
176
+ return unless view_info = @view_lookup_store.view_info(v_p)
177
+ @root_path ||= view_info[:root_view]
178
+ @root_view = LazyView.new(@root_path, true)
179
+ views = view_info[:views]
180
+ populate_view(self.view, views)
181
+ @presented = true
182
+ end
148
183
  end
149
184
 
150
185
  def restful_view_path(restful_info)
@@ -157,6 +192,19 @@ module Pakyow
157
192
 
158
193
  def load_views
159
194
  @view_lookup_store = ViewLookupStore.new("#{Configuration::Presenter.view_dir}")
195
+ if Configuration::Base.presenter.view_caching then
196
+ @populated_root_view_cache = build_root_view_cache(@view_lookup_store.view_info)
197
+ end
198
+ end
199
+
200
+ def build_root_view_cache(view_info)
201
+ r_v_c = {}
202
+ view_info.each{|dir,info|
203
+ r_v = LazyView.new(info[:root_view], true)
204
+ populate_view(r_v, info[:views])
205
+ r_v_c[dir] = r_v
206
+ }
207
+ r_v_c
160
208
  end
161
209
 
162
210
  # populates the top_view using view_store data by recursively building
@@ -168,7 +216,7 @@ module Pakyow
168
216
  path = views[name]
169
217
  if path
170
218
  v = populate_view(View.new(path), views)
171
- top_view.reset_container(name)
219
+ top_view.reset_container(name) # TODO revisit how this is implemented; assumes all LazyViews are root views
172
220
  top_view.add_content_to_container(v, name)
173
221
  end
174
222
  }
@@ -2,7 +2,7 @@ module Pakyow
2
2
  module Presenter
3
3
  class View
4
4
  class << self
5
- attr_accessor :binders, :cache, :default_view_path, :default_is_root_view
5
+ attr_accessor :binders, :default_view_path, :default_is_root_view
6
6
 
7
7
  def view_path(dvp, dirv=false)
8
8
  self.default_view_path = dvp
@@ -11,12 +11,16 @@ module Pakyow
11
11
  end
12
12
 
13
13
  attr_accessor :doc
14
-
14
+
15
+ def dup
16
+ self.class.new(@doc.dup)
17
+ end
18
+
15
19
  def initialize(arg=nil, is_root_view=false)
16
20
  arg = self.class.default_view_path if arg.nil? && self.class.default_view_path
17
21
  is_root_view = self.class.default_is_root_view if arg.nil? && self.class.default_is_root_view
18
22
 
19
- if arg.is_a?(Nokogiri::XML::Element)
23
+ if arg.is_a?(Nokogiri::XML::Element) || arg.is_a?(Nokogiri::XML::Document)
20
24
  @doc = arg
21
25
  elsif arg.is_a?(Pakyow::Presenter::Views)
22
26
  @doc = arg.first.doc.dup
@@ -28,18 +32,11 @@ module Pakyow
28
32
  else
29
33
  view_path = "#{Configuration::Presenter.view_dir}/#{arg}"
30
34
  end
31
- # Only load one time if view caching is enabled
32
- self.class.cache ||= {}
33
-
34
- if !self.class.cache.has_key?(view_path) || !Configuration::Base.presenter.view_caching
35
- if is_root_view then
36
- self.class.cache[view_path] = Nokogiri::HTML::Document.parse(File.read(view_path))
37
- else
38
- self.class.cache[view_path] = Nokogiri::HTML.fragment(File.read(view_path))
39
- end
35
+ if is_root_view then
36
+ @doc = Nokogiri::HTML::Document.parse(File.read(view_path))
37
+ else
38
+ @doc = Nokogiri::HTML.fragment(File.read(view_path))
40
39
  end
41
-
42
- @doc = self.class.cache[view_path].dup
43
40
  else
44
41
  raise ArgumentError, "No View for you! Come back, one year."
45
42
  end
@@ -91,8 +88,8 @@ module Pakyow
91
88
  ViewContext.new(self).instance_eval(&block)
92
89
  end
93
90
 
94
- def bind(object, type = nil)
95
- type = type || StringUtils.underscore(object.class.name)
91
+ def bind(object, opts = {})
92
+ bind_as = opts[:to] ? opts[:to].to_s : StringUtils.underscore(object.class.name.split('::').last)
96
93
 
97
94
  @doc.traverse do |o|
98
95
  if attribute = o.get_attribute('itemprop')
@@ -105,16 +102,10 @@ module Pakyow
105
102
 
106
103
  next unless attribute
107
104
 
108
- if selector.include?('[')
109
- type_len = type.length
110
- object_type = selector[0,type_len]
111
- attribute = selector[type_len + 1, attribute.length - type_len - 2]
112
- else
113
- object_type = nil
114
- attribute = selector
115
- end
105
+ type_len = bind_as.length
106
+ next if selector[0, type_len + 1] != "#{bind_as}["
116
107
 
117
- next if !object_type.nil? && object_type != type
108
+ attribute = selector[type_len + 1, attribute.length - type_len - 2]
118
109
 
119
110
  binding = {
120
111
  :element => o,
@@ -122,15 +113,15 @@ module Pakyow
122
113
  :selector => selector
123
114
  }
124
115
 
125
- bind_object_to_binding(object, binding, object_type.nil?)
116
+ bind_object_to_binding(object, binding, bind_as)
126
117
  end
127
118
  end
128
119
 
129
- def repeat_for(objects, &block)
120
+ def repeat_for(objects, opts = {}, &block)
130
121
  if o = @doc
131
122
  objects.each do |object|
132
123
  view = View.new(self)
133
- view.bind(object)
124
+ view.bind(object, opts)
134
125
  ViewContext.new(view).instance_exec(object, &block) if block_given?
135
126
 
136
127
  o.add_previous_sibling(view.doc)
@@ -239,23 +230,7 @@ module Pakyow
239
230
  end
240
231
 
241
232
  alias :render :append
242
-
243
- def +(value)
244
- if @previous_method
245
- append_value(val)
246
- else
247
- super
248
- end
249
- end
250
-
251
- def <<(value)
252
- if @previous_method
253
- append_value(val)
254
- else
255
- super
256
- end
257
- end
258
-
233
+
259
234
  def method_missing(method, *args)
260
235
  return unless @previous_method == :attributes
261
236
  @previous_method = nil
@@ -264,7 +239,15 @@ module Pakyow
264
239
  attribute = method.to_s.gsub('=', '')
265
240
  value = args[0]
266
241
 
267
- self.doc[attribute] = value
242
+ if value.is_a? Proc
243
+ value = value.call(self.doc[attribute])
244
+ end
245
+
246
+ if value.nil?
247
+ self.doc.remove_attribute(attribute)
248
+ else
249
+ self.doc[attribute] = value
250
+ end
268
251
  else
269
252
  return self.doc[method.to_s]
270
253
  end
@@ -298,32 +281,20 @@ module Pakyow
298
281
 
299
282
  protected
300
283
 
301
- def append_value(value_to_append)
302
- case @previous_method
303
- when :content
304
- append(value_to_append)
305
- end
306
-
307
- @previous_method = nil
308
- end
309
-
310
- def bind_object_to_binding(object, binding, wild = false)
284
+ def bind_object_to_binding(object, binding, bind_as)
311
285
  binder = nil
312
286
 
313
- # fetch value
314
- if object.is_a? Hash
315
- value = object[binding[:attribute]]
287
+ if View.binders
288
+ b = View.binders[bind_as.to_sym] and binder = b.new(object, binding[:element])
289
+ end
290
+
291
+ if binder && binder.class.method_defined?(binding[:attribute])
292
+ value = binder.send(binding[:attribute])
316
293
  else
317
- if View.binders
318
- b = View.binders[object.class.to_s.to_sym] and binder = b.new(object, binding[:element])
319
- end
320
-
321
- if binder && binder.class.method_defined?(binding[:attribute])
322
- value = binder.send(binding[:attribute])
294
+ if object.is_a? Hash
295
+ value = object[binding[:attribute]]
323
296
  else
324
- if wild && !object.class.method_defined?(binding[:attribute])
325
- return
326
- elsif Configuration::Base.app.dev_mode == true && !object.class.method_defined?(binding[:attribute])
297
+ if Configuration::Base.app.dev_mode == true && !object.class.method_defined?(binding[:attribute])
327
298
  Log.warn("Attempting to bind object to #{binding[:html_tag]}#{binding[:selector].gsub('*', '').gsub('\'', '')} but #{object.class.name}##{binding[:attribute]} is not defined.")
328
299
  return
329
300
  else
@@ -334,7 +305,13 @@ module Pakyow
334
305
 
335
306
  if value.is_a? Hash
336
307
  value.each do |k, v|
337
- if k == :content
308
+ if v.is_a? Proc
309
+ v = v.call(binding[:element][k.to_s])
310
+ end
311
+
312
+ if v.nil?
313
+ binding[:element].remove_attribute(k.to_s)
314
+ elsif k == :content
338
315
  bind_value_to_binding(v, binding, binder)
339
316
  else
340
317
  binding[:element][k.to_s] = v.to_s
@@ -382,7 +359,7 @@ module Pakyow
382
359
  binding[:element].inner_html = Nokogiri::HTML.fragment(value.to_s)
383
360
  end
384
361
  elsif binding[:element].name == 'input' && binding[:element][:type] == 'checkbox'
385
- if value == true || binding[:element].attributes['value'].value == value.to_s
362
+ if value == true || (binding[:element].attributes['value'] && binding[:element].attributes['value'].value == value.to_s)
386
363
  binding[:element]['checked'] = 'checked'
387
364
  else
388
365
  binding[:element].delete('checked')
@@ -21,8 +21,8 @@ module Pakyow
21
21
  # }
22
22
  # },
23
23
  # :abstract_paths => {
24
- # "/abstract/path/file.html" => "/abstract.root1/path/file.html",
25
- # "/some/other/path" => "/some/other.root1/path.root2"
24
+ # "/abstract/path/file.html" => {:real_path => "/abstract.root1/path/file.html", :file_or_dir => :file},
25
+ # "/some/other/path" => {:real_path => "/some/other.root1/path.root2", :file_or_dir => :dir}
26
26
  # }
27
27
  # }
28
28
  # This takes into account that a view directory may have a .root suffix.
@@ -46,11 +46,6 @@ module Pakyow
46
46
  default_views = {} # view_basename => path_to_view.html
47
47
  if File.exist?(view_dir) then
48
48
  default_root_view_file_path = "#{absolute_path_prefix}/#{Configuration::Presenter.default_view}"
49
- # See if the top level index directory overrides the default root view
50
- index_dirs = Dir.entries(view_dir).partition{|e| File.directory?("#{absolute_path_prefix}/#{e}") && e.start_with?('index.')}[0]
51
- if index_dirs.length == 1
52
- default_root_view_file_path = "#{absolute_path_prefix}/#{StringUtils.split_at_last_dot(index_dirs[0])[1]}.html"
53
- end
54
49
  # The logic depends on this traversing top down
55
50
  DirUtils.walk_dir(view_dir) { |vpath|
56
51
  if File.directory?(vpath)
@@ -89,9 +84,9 @@ module Pakyow
89
84
  else
90
85
  r_p = vpath.sub(absolute_path_prefix, '')
91
86
  end
92
- @view_store[:abstract_paths][route] = r_p
87
+ @view_store[:abstract_paths][route] = {:real_path => r_p, :file_or_dir => :dir}
93
88
  # duplicate real path under routes permuted with leading/trailing slash
94
- permute_route(route).each { |r| @view_store[:abstract_paths][r] = r_p } unless route == '/'
89
+ permute_route(route).each { |r| @view_store[:abstract_paths][r] = {:real_path => r_p, :file_or_dir => :dir} } unless route == '/'
95
90
  else
96
91
  # files here are direct overrides of the route's views
97
92
  parent,route = pakyow_path_to_route_and_parent(vpath, view_dir, :file)
@@ -108,13 +103,13 @@ module Pakyow
108
103
  # duplicating real path under route without the leading slash
109
104
  r_p = vpath.sub(absolute_path_prefix, '')
110
105
  if route == '/'
111
- @view_store[:abstract_paths]["/#{File.basename(vpath)}"] = r_p
112
- @view_store[:abstract_paths][File.basename(vpath)] = r_p
106
+ @view_store[:abstract_paths]["/#{File.basename(vpath)}"] = {:real_path => r_p, :file_or_dir => :file}
107
+ @view_store[:abstract_paths][File.basename(vpath)] = {:real_path => r_p, :file_or_dir => :file}
113
108
  else
114
109
  route_with_leading_slash = "#{route}/#{File.basename(vpath)}"
115
110
  route_without_leading_slash = route_with_leading_slash.sub('/','')
116
- @view_store[:abstract_paths][route_with_leading_slash] = r_p
117
- @view_store[:abstract_paths][route_without_leading_slash] = r_p
111
+ @view_store[:abstract_paths][route_with_leading_slash] = {:real_path => r_p, :file_or_dir => :file}
112
+ @view_store[:abstract_paths][route_without_leading_slash] = {:real_path => r_p, :file_or_dir => :file}
118
113
  end
119
114
  end
120
115
  }
@@ -162,9 +157,17 @@ module Pakyow
162
157
  return @view_store[:view_dirs]
163
158
  end
164
159
  end
160
+
161
+ def real_path_info(abstract_path = nil)
162
+ if abstract_path then
163
+ @view_store[:abstract_paths][abstract_path]
164
+ else
165
+ @view_store[:abstract_paths]
166
+ end
167
+ end
165
168
 
166
169
  def real_path(abstract_path)
167
- @view_store[:abstract_paths][abstract_path]
170
+ @view_store[:abstract_paths][abstract_path][:real_path] if @view_store[:abstract_paths][abstract_path]
168
171
  end
169
172
 
170
173
  private
@@ -200,7 +203,7 @@ module Pakyow
200
203
  end
201
204
 
202
205
  # Takes a route with a leading slash and no trailing slash (/route) and
203
- # returns the three other permutaions (/route/, route/, and route).
206
+ # returns the three other permutations (/route/, route/, and route).
204
207
  def permute_route(route0)
205
208
  route3 = route0.sub('/','')
206
209
  route2 = "#{route3}/"
@@ -65,16 +65,10 @@ module Pakyow
65
65
  end
66
66
 
67
67
  alias :render :append
68
-
69
- def +(val)
70
- self.each {|e| e + val}
71
- end
72
-
68
+
73
69
  def <<(val)
74
70
  if val.is_a? View
75
71
  @views << val
76
- else
77
- self.each {|e| e << val}
78
72
  end
79
73
  end
80
74
 
@@ -94,18 +88,18 @@ module Pakyow
94
88
  method_missing(:id)
95
89
  end
96
90
 
97
- def repeat_for(objects, &block)
91
+ def repeat_for(objects, opts = {}, &block)
98
92
  first_found = self.first
99
93
 
100
94
  # Remove other matches
101
95
  self.drop(1).each {|found| found.remove}
102
96
 
103
97
  # Repeat for first match
104
- first_found.repeat_for(objects, &block)
98
+ first_found.repeat_for(objects, opts, &block)
105
99
  end
106
100
 
107
- def bind(object)
108
- self.each {|e| e.bind(object)}
101
+ def bind(object, opts = {})
102
+ self.each {|e| e.bind(object, opts)}
109
103
  end
110
104
 
111
105
  def find(element)
metadata CHANGED
@@ -1,66 +1,56 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: pakyow-presenter
3
- version: !ruby/object:Gem::Version
4
- hash: 113
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.0
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 6
9
- - 3
10
- - 1
11
- version: 0.6.3.1
12
6
  platform: ruby
13
- authors:
7
+ authors:
14
8
  - Bryan Powell
15
9
  - Bret Young
16
10
  autorequire:
17
11
  bindir: bin
18
12
  cert_chain: []
19
-
20
- date: 2011-09-13 00:00:00 -05:00
21
- default_executable:
22
- dependencies:
23
- - !ruby/object:Gem::Dependency
13
+ date: 2011-11-19 00:00:00.000000000Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
24
16
  name: pakyow-core
25
- prerelease: false
26
- requirement: &id001 !ruby/object:Gem::Requirement
17
+ requirement: &70363184265360 !ruby/object:Gem::Requirement
27
18
  none: false
28
- requirements:
29
- - - "="
30
- - !ruby/object:Gem::Version
31
- hash: 113
32
- segments:
33
- - 0
34
- - 6
35
- - 3
36
- - 1
37
- version: 0.6.3.1
19
+ requirements:
20
+ - - =
21
+ - !ruby/object:Gem::Version
22
+ version: 0.7.0
38
23
  type: :runtime
39
- version_requirements: *id001
40
- - !ruby/object:Gem::Dependency
41
- name: nokogiri
42
24
  prerelease: false
43
- requirement: &id002 !ruby/object:Gem::Requirement
25
+ version_requirements: *70363184265360
26
+ - !ruby/object:Gem::Dependency
27
+ name: nokogiri
28
+ requirement: &70363184264680 !ruby/object:Gem::Requirement
44
29
  none: false
45
- requirements:
30
+ requirements:
46
31
  - - ~>
47
- - !ruby/object:Gem::Version
48
- hash: 5
49
- segments:
50
- - 1
51
- - 5
52
- version: "1.5"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
53
34
  type: :runtime
54
- version_requirements: *id002
35
+ prerelease: false
36
+ version_requirements: *70363184264680
37
+ - !ruby/object:Gem::Dependency
38
+ name: shoulda
39
+ requirement: &70363184263960 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ version: '2.11'
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *70363184263960
55
48
  description: pakyow-presenter
56
49
  email: bryan@metabahn.com
57
50
  executables: []
58
-
59
51
  extensions: []
60
-
61
52
  extra_rdoc_files: []
62
-
63
- files:
53
+ files:
64
54
  - pakyow-presenter/CHANGES
65
55
  - pakyow-presenter/README
66
56
  - pakyow-presenter/MIT-LICENSE
@@ -76,41 +66,29 @@ files:
76
66
  - pakyow-presenter/lib/presenter/view_context.rb
77
67
  - pakyow-presenter/lib/presenter/view_lookup_store.rb
78
68
  - pakyow-presenter/lib/presenter/views.rb
79
- has_rdoc: true
80
69
  homepage: http://pakyow.com
81
70
  licenses: []
82
-
83
71
  post_install_message:
84
72
  rdoc_options: []
85
-
86
- require_paths:
73
+ require_paths:
87
74
  - pakyow-presenter/lib
88
- required_ruby_version: !ruby/object:Gem::Requirement
75
+ required_ruby_version: !ruby/object:Gem::Requirement
89
76
  none: false
90
- requirements:
91
- - - ">="
92
- - !ruby/object:Gem::Version
93
- hash: 57
94
- segments:
95
- - 1
96
- - 8
97
- - 7
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
98
80
  version: 1.8.7
99
- required_rubygems_version: !ruby/object:Gem::Requirement
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
82
  none: false
101
- requirements:
102
- - - ">="
103
- - !ruby/object:Gem::Version
104
- hash: 3
105
- segments:
106
- - 0
107
- version: "0"
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
108
87
  requirements: []
109
-
110
88
  rubyforge_project: pakyow-presenter
111
- rubygems_version: 1.6.2
89
+ rubygems_version: 1.8.10
112
90
  signing_key:
113
91
  specification_version: 3
114
92
  summary: pakyow-presenter
115
93
  test_files: []
116
-
94
+ has_rdoc: