pakyow-presenter 0.8rc1 → 0.8.rc4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bf40bbef34bd10129e0986f8009446fe4c9c427a
4
+ data.tar.gz: afa46cf16a50b2277a1e901d8e021ed8ef7865ce
5
+ SHA512:
6
+ metadata.gz: 044a33ab081707cd3ac31e43185a7f1f7cfb742f25fd74862fe8b8ce31a4f5fed1749eed120ffac43fd962c0350ff62efe80410e20325c853ec5873a2a5121fb
7
+ data.tar.gz: 457ca686a0b5ed1cb98aaaf6cd10406143a42eb6fb12996e32d9fe2301297e2817c267bd8de7d9015978880474560dbdcc49c23899a2830be20038fe94a26575
@@ -9,5 +9,6 @@ require 'presenter/base'
9
9
  include Presenter
10
10
 
11
11
  require 'presenter/presenter'
12
- require 'presenter/configuration/base'
12
+ require 'presenter/config/presenter'
13
13
  require 'presenter/helpers'
14
+ require 'presenter/ext/app'
@@ -1,13 +1,111 @@
1
1
  module Pakyow
2
2
  module Presenter
3
- class Attributes
3
+ class Attribute
4
4
  @@types = {
5
- :boolean => [:selected, :checked, :disabled, :readonly, :multiple],
6
- :multiple => [:class]
5
+ :hash => [:style],
6
+ :bool => [:selected, :checked, :disabled, :readonly, :multiple],
7
+ :mult => [:class]
7
8
  }
8
9
 
9
- def initialize(view)
10
- @view = view
10
+ def initialize(name, raw_value, control, doc)
11
+ @type = type_of_attribute(name)
12
+ @name = name
13
+ @value = deconstruct_attribute_value_of_type(raw_value, @type)
14
+ @control = control
15
+ @doc = doc
16
+ end
17
+
18
+ def set(value)
19
+ value = Array(value) if @type == :mult && !value.is_a?(Proc)
20
+ @value = value
21
+ update_value
22
+ end
23
+
24
+ # ensures that the value passed is contained in `value`
25
+ def ensure(value)
26
+ case @type
27
+ when :mult
28
+ @value << value unless @value.include?(value)
29
+ when :bool
30
+ @value = value
31
+ else
32
+ @value << value if @value.nil? || !@value.match(value)
33
+ end
34
+
35
+ update_value
36
+ end
37
+
38
+ # passes method call to `value`
39
+ def method_missing(method, *args)
40
+ ret = @value.send(method, *args)
41
+ update_value
42
+
43
+ return ret
44
+ end
45
+
46
+ # returns the full string value
47
+ def to_s
48
+ value = @value
49
+ value = value.call(deconstruct_attribute_value_of_type(@doc[@name], @type)) if value.is_a? Proc
50
+ value = construct_attribute_value_of_type(value, @type, @name)
51
+
52
+ return value
53
+ end
54
+
55
+ def value
56
+ @value
57
+ end
58
+
59
+ private
60
+
61
+ def update_value
62
+ updated_value = to_s
63
+
64
+ if updated_value.nil?
65
+ @control.remove_attribute(@name)
66
+ else
67
+ @control.update_value_for_attribute(@name, updated_value)
68
+ end
69
+ end
70
+
71
+ def type_of_attribute(attribute)
72
+ attribute = attribute.to_sym
73
+
74
+ return :bool if @@types[:bool].include?(attribute)
75
+ return :mult if @@types[:mult].include?(attribute)
76
+ return :hash if @@types[:hash].include?(attribute)
77
+ return :single
78
+ end
79
+
80
+ def deconstruct_attribute_value_of_type(value, type)
81
+ return value ? value : '' if type == :single
82
+ return value ? value.split(' ') : [] if type == :mult
83
+ return !value.nil? if type == :bool
84
+ return value_to_hash(value) if type == :hash
85
+ end
86
+
87
+ def construct_attribute_value_of_type(value, type, attribute)
88
+ return value if type == :single
89
+ return value.join(' ') if type == :mult
90
+ return value ? attribute : nil if type == :bool
91
+ return value.to_a.map {|a| a.join(':')}.join(';') if type == :hash
92
+ end
93
+
94
+ def value_to_hash(value)
95
+ return {} if value.nil?
96
+
97
+ value.split(';').inject({}) {|h, style|
98
+ k,v = style.split(':')
99
+ h[k.to_sym] = v
100
+ h
101
+ }
102
+ end
103
+ end
104
+
105
+ class Attributes
106
+ def initialize(doc)
107
+ @doc = doc
108
+ @attributes = {}
11
109
  end
12
110
 
13
111
  def method_missing(method, *args)
@@ -31,45 +129,30 @@ module Pakyow
31
129
  method_missing(:id, *args)
32
130
  end
33
131
 
34
- protected
35
-
36
- def set_attribute(attribute, value)
37
- type = self.type_of_attribute(attribute)
38
-
39
- value = value.call(self.deconstruct_attribute_value_of_type(@view.doc[attribute], type)) if value.is_a? Proc
40
- value = construct_attribute_value_of_type(value, type, attribute)
41
-
42
- if value.nil?
43
- @view.doc.remove_attribute(attribute)
132
+ def update_value_for_attribute(attribute, value)
133
+ if !@doc.is_a?(Nokogiri::XML::Element) && @doc.children.count == 1
134
+ @doc.children.first[attribute] = value
44
135
  else
45
- @view.doc[attribute] = value
136
+ @doc[attribute] = value
46
137
  end
47
138
  end
48
139
 
49
- def get_attribute(attribute)
50
- self.deconstruct_attribute_value_of_type(@view.doc[attribute], self.type_of_attribute(attribute))
140
+ def remove_attribute(attribute)
141
+ @doc.remove_attribute(attribute)
51
142
  end
52
143
 
53
- def type_of_attribute(attribute)
54
- attribute = attribute.to_sym
55
-
56
- return :boolean if @@types[:boolean].include?(attribute)
57
- return :multiple if @@types[:multiple].include?(attribute)
58
- return :single
59
- end
144
+ protected
60
145
 
61
- def deconstruct_attribute_value_of_type(value, type)
62
- return value if type == :single
63
- return value ? value.split(' ') : [] if type == :multiple
64
- return !value.nil? if type == :boolean
146
+ def set_attribute(attribute, value)
147
+ get_attribute(attribute).set(value)
65
148
  end
66
149
 
67
- def construct_attribute_value_of_type(value, type, attribute)
68
- return value if type == :single
69
- return value.join(' ') if type == :multiple
150
+ def get_attribute(attribute)
151
+ unless a = @attributes[attribute]
152
+ a = Attribute.new(attribute, @doc[attribute], self, @doc)
153
+ end
70
154
 
71
- # pp value
72
- return value ? attribute : nil if type == :boolean
155
+ return a
73
156
  end
74
157
  end
75
158
 
@@ -85,7 +168,17 @@ module Pakyow
85
168
  end
86
169
 
87
170
  def method_missing(method, *args)
88
- @attributes.each{|a| a.send(method, *args)}
171
+ ret = []
172
+ @attributes.each{|a| ret << a.send(method, *args)}
173
+ ret
174
+ end
175
+
176
+ def class(*args)
177
+ method_missing(:class, *args)
178
+ end
179
+
180
+ def id(*args)
181
+ method_missing(:id, *args)
89
182
  end
90
183
  end
91
184
  end
@@ -1,11 +1,15 @@
1
- require 'presenter/view_lookup_store'
1
+ require 'yaml'
2
+ require 'presenter/view_store'
3
+ require 'presenter/doc_helpers'
2
4
  require 'presenter/view'
3
- require 'presenter/lazy_view'
5
+ require 'presenter/template'
6
+ require 'presenter/page'
7
+ require 'presenter/partial'
4
8
  require 'presenter/view_collection'
5
9
  require 'presenter/binder'
6
- require 'presenter/view_context'
10
+ require 'presenter/binder_set'
7
11
  require 'presenter/attributes'
8
- require 'presenter/bindings'
12
+ require 'presenter/exceptions'
9
13
 
10
14
  module Pakyow
11
15
  module Presenter
@@ -1,43 +1,75 @@
1
1
  module Pakyow
2
2
  module Presenter
3
+ # A singleton that manages route sets.
4
+ #
3
5
  class Binder
4
- include Pakyow::GeneralHelpers
5
-
6
- class << self
7
- attr_accessor :options
8
-
9
- def binder_for(*args)
10
- View.binders ||= {}
11
- args.each { |klass| View.binders[klass.to_s.to_sym] = self }
12
- end
13
-
14
- def options_for(*args)
15
- self.options = {} unless self.options
16
- self.options[args[0]] = args[1]
17
- end
6
+ include Singleton
7
+ include Helpers
8
+
9
+ attr_reader :sets
10
+
11
+ def initialize
12
+ @sets = {}
18
13
  end
19
-
20
- attr_accessor :bindable
21
-
22
- def initialize(bindable)
23
- self.bindable = bindable
14
+
15
+ #TODO want to do this for all sets?
16
+ def reset
17
+ @sets = {}
18
+ self
24
19
  end
25
20
 
26
- def value_for_prop(prop)
27
- self.class.method_defined?(prop) ? self.send(prop) : bindable[prop]
21
+ # Creates a new set.
22
+ #
23
+ def set(name, &block)
24
+ @sets[name] = BinderSet.new
25
+ @sets[name].instance_exec(&block)
28
26
  end
29
-
30
- def fetch_options_for(attribute)
31
- if self.class.options
32
- if options = self.class.options[attribute]
33
- unless options.is_a?(Array) || options.is_a?(Hash)
34
- options = self.send(options)
35
- end
36
-
37
- return options
27
+
28
+ def value_for_prop(prop, scope, bindable, bindings = {})
29
+ binding = nil
30
+ @sets.each {|set|
31
+ binding = set[1].match_for_prop(prop, scope, bindable, bindings)
32
+ break if binding
33
+ }
34
+
35
+ if binding
36
+ 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)
38
42
  end
43
+ else
44
+ # default
45
+ prop_value_for_bindable(bindable, prop)
39
46
  end
40
47
  end
48
+
49
+ def prop_value_for_bindable(bindable, prop)
50
+ return bindable[prop] if bindable.is_a?(Hash)
51
+ return bindable.send(prop) if bindable.class.method_defined?(prop)
52
+ end
53
+
54
+ def options_for_prop(*args)
55
+ match = nil
56
+ @sets.each {|set|
57
+ match = set[1].options_for_prop(*args)
58
+ break if match
59
+ }
60
+
61
+ return match
62
+ end
63
+
64
+ def has_prop?(*args)
65
+ has = nil
66
+ @sets.each {|set|
67
+ has = set[1].has_prop?(*args)
68
+ break if has
69
+ }
70
+
71
+ return has
72
+ end
41
73
  end
42
74
  end
43
75
  end
@@ -0,0 +1,95 @@
1
+ module Pakyow
2
+ module Presenter
3
+ class BinderSet
4
+ attr_reader :scopes
5
+
6
+ def initialize
7
+ @scopes = {}
8
+ @options = {}
9
+ end
10
+
11
+ def scope(name, &block)
12
+ scope_eval = ScopeEval.new
13
+ bindings, options = scope_eval.eval(&block)
14
+
15
+ @scopes[name.to_sym] = bindings
16
+ @options[name.to_sym] = options
17
+ end
18
+
19
+ def match_for_prop(prop, scope, bindable, bindings = {})
20
+ return bindings_for_scope(scope, bindings)[prop]
21
+ end
22
+
23
+ def options_for_prop(prop, scope, bindable)
24
+ if block = (@options[scope] || {})[prop]
25
+ binding_eval = BindingEval.new(bindable)
26
+ binding_eval.instance_exec(&block)
27
+ end
28
+ end
29
+
30
+ def has_prop?(prop, scope, bindings)
31
+ bindings_for_scope(scope, bindings).key? prop
32
+ end
33
+
34
+ def bindings_for_scope(scope, bindings)
35
+ # merge passed bindings with bindings
36
+ (@scopes[scope] || {}).merge(bindings)
37
+ end
38
+ end
39
+
40
+ class ScopeEval
41
+ include Helpers
42
+
43
+ def initialize
44
+ @bindings = {}
45
+ @options = {}
46
+ end
47
+
48
+ def eval(&block)
49
+ self.instance_eval(&block)
50
+ return @bindings, @options
51
+ end
52
+
53
+ def binding(name, &block)
54
+ @bindings[name.to_sym] = block
55
+ end
56
+
57
+ def options(name, &block)
58
+ @options[name] = block
59
+ end
60
+
61
+ def restful(route_group)
62
+ binding(:action) {
63
+ routes = router.group(route_group)
64
+ return_data = {}
65
+
66
+ if id = bindable[:id]
67
+ return_data[:view] = lambda { |view|
68
+ view.prepend(View.from_doc(Nokogiri::HTML.fragment('<input type="hidden" name="_method" value="put">')))
69
+ }
70
+
71
+ action = routes.path(:update, :id => id)
72
+ else
73
+ action = routes.path(:create)
74
+ end
75
+
76
+ return_data[:action] = action
77
+ return_data[:method] = 'post'
78
+ return_data
79
+ }
80
+ end
81
+
82
+ #TODO options
83
+ end
84
+
85
+ class BindingEval
86
+ include Helpers
87
+
88
+ attr_reader :bindable
89
+
90
+ def initialize(bindable)
91
+ @bindable = bindable
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,61 @@
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
@@ -0,0 +1,70 @@
1
+ module Pakyow
2
+ module Presenter
3
+ module DocHelpers
4
+ def breadth_first(doc)
5
+ queue = [doc]
6
+ until queue.empty?
7
+ node = queue.shift
8
+ catch(:reject) {
9
+ yield node
10
+ queue.concat(node.children)
11
+ }
12
+ end
13
+ end
14
+
15
+ def path_to(child)
16
+ path = []
17
+
18
+ return path if child == @doc
19
+
20
+ child.ancestors.each {|a|
21
+ # since ancestors goes all the way to doc root, stop when we get to the level of @doc
22
+ break if a.children.include?(@doc)
23
+
24
+ path.unshift(a.children.index(child))
25
+ child = a
26
+ }
27
+
28
+ return path
29
+ end
30
+
31
+ def path_within_path?(child_path, parent_path)
32
+ parent_path.each_with_index {|pp_step, i|
33
+ return false unless pp_step == child_path[i]
34
+ }
35
+
36
+ true
37
+ end
38
+
39
+ def doc_from_path(path)
40
+ o = @doc
41
+
42
+ # if path is empty we're at self
43
+ return o if path.empty?
44
+
45
+ path.each {|i|
46
+ if child = o.children[i]
47
+ o = child
48
+ else
49
+ break
50
+ end
51
+ }
52
+
53
+ return o
54
+ end
55
+
56
+ def view_from_path(path)
57
+ view = View.from_doc(doc_from_path(path))
58
+ view.related_views << self
59
+
60
+ return view
61
+ end
62
+
63
+ def to_html
64
+ @doc.to_html
65
+ end
66
+
67
+ alias :to_s :to_html
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,7 @@
1
+ module Pakyow
2
+ module Presenter
3
+ class MissingTemplatesDir < Error; end
4
+ class MissingView < Error; end
5
+ class MissingTemplate < Error; end
6
+ end
7
+ end
@@ -0,0 +1,32 @@
1
+ module Pakyow
2
+ class App
3
+ class << self
4
+ @@bindings = {}
5
+ @@processors = {}
6
+
7
+ def bindings(set_name = :main, &block)
8
+ if set_name && block
9
+ @@bindings[set_name] = block
10
+ else
11
+ @@bindings
12
+ end
13
+ end
14
+
15
+ def processor(*args, &block)
16
+ args.each {|format|
17
+ @@processors[format] = block
18
+ }
19
+ end
20
+
21
+ def processors
22
+ @@processors
23
+ end
24
+ end
25
+
26
+ # Convenience method for defining bindings on an app instance.
27
+ #
28
+ def bindings(set_name = :main, &block)
29
+ self.class.bindings(set_name, &block)
30
+ end
31
+ end
32
+ end
@@ -1,16 +1,15 @@
1
1
  module Pakyow
2
- module Helpers
2
+ module AppHelpers
3
3
  def presenter
4
- Pakyow.app.presenter.current_context = self
5
- Pakyow.app.presenter
4
+ @presenter
6
5
  end
7
6
 
8
7
  def bindings(name)
9
8
  presenter.bindings(name).bindings
10
9
  end
11
-
10
+
12
11
  def view
13
- self.presenter.view
12
+ presenter.view
14
13
  end
15
14
  end
16
15
  end