lissio 0.1.0.beta3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -63,19 +63,21 @@ class Storage < Adapter
63
63
  self.class.storage
64
64
  end
65
65
 
66
- def self.fetch(id, &block)
67
- proc {
68
- block.call(storage[id] || :error)
69
- }.defer
66
+ def self.fetch(id)
67
+ if value = storage[id]
68
+ Promise.value(value)
69
+ else
70
+ Promise.error(:missing)
71
+ end
70
72
  end
71
73
 
72
- def create(&block)
73
- proc {
74
- key = id!
74
+ def create
75
+ key = id!
75
76
 
76
- if key && storage[key]
77
- block.call(:error) if block
78
- else
77
+ if key && storage[key]
78
+ Promise.error(:exists)
79
+ else
80
+ Promise.defer {
79
81
  adapter.autoincrement.each {|name|
80
82
  unless __send__ name
81
83
  __send__ "#{name}=", adapter.autoincrement!(name, storage)
@@ -83,34 +85,28 @@ class Storage < Adapter
83
85
  }
84
86
 
85
87
  storage[id!] = self
86
-
87
- block.call(:ok) if block
88
- end
89
- }.defer
88
+ }
89
+ end
90
90
  end
91
91
 
92
- def save(&block)
93
- proc {
94
- if storage[id!]
92
+ def save
93
+ if storage[id!]
94
+ Promise.defer {
95
95
  storage[id!] = self
96
-
97
- block.call(:ok) if block
98
- else
99
- block.call(:error) if block
100
- end
101
- }.defer
96
+ }
97
+ else
98
+ Promise.error(:missing)
99
+ end
102
100
  end
103
101
 
104
102
  def destroy(&block)
105
- proc {
106
- if storage[id!]
103
+ if storage[id!]
104
+ Promise.defer {
107
105
  storage.delete(id!)
108
-
109
- block.call(:ok) if block
110
- else
111
- block.call(:error) if block
112
- end
113
- }.defer
106
+ }
107
+ else
108
+ Promise.error(:missing)
109
+ end
114
110
  end
115
111
  }
116
112
  else
@@ -124,15 +120,15 @@ class Storage < Adapter
124
120
  end
125
121
 
126
122
  def self.fetch(*args, &block)
127
- proc {
128
- block.call new(storage.map {|name, value|
123
+ Promise.defer {
124
+ new(storage.map {|name, value|
129
125
  next if Array === name && name.length == 2 && name.first == :__autoincrement__
130
126
 
131
127
  if !adapter.filter || adapter.filter.call(value, *args)
132
128
  value
133
129
  end
134
130
  }.compact)
135
- }.defer
131
+ }
136
132
  end
137
133
  }
138
134
  end
@@ -9,7 +9,7 @@ class Application < Component
9
9
 
10
10
  klass.include Singleton
11
11
 
12
- $document.on :load do
12
+ $document.ready do
13
13
  klass.start
14
14
  end
15
15
  end
@@ -39,6 +39,8 @@ class Application < Component
39
39
  def_delegators :@router, :navigate, :route
40
40
 
41
41
  def initialize
42
+ super()
43
+
42
44
  @router = Lissio::Router.new(fragment: false)
43
45
  end
44
46
 
@@ -54,10 +56,9 @@ class Application < Component
54
56
 
55
57
  element :body
56
58
 
57
- on :click, 'a[href^="/"]' do |e|
59
+ on :click, 'a[href^="/"], a[href="back"], a[href="forward"]' do |e|
58
60
  unless e.alt? || e.ctrl? || e.meta? || e.shift?
59
- e.stop!
60
-
61
+ e.prevent
61
62
  navigate e.target[:href] || e.target.ancestors('a').first[:href]
62
63
  end
63
64
  end
@@ -28,9 +28,7 @@ class Collection
28
28
  klass ? @model = klass : @model
29
29
  end
30
30
 
31
- def self.parse(&block)
32
- block ? @parse = block : @parse
33
- end
31
+ include Enumerable
34
32
 
35
33
  extend Forwardable
36
34
  def_delegators :class, :adapter, :model
@@ -41,10 +39,8 @@ class Collection
41
39
 
42
40
  if data
43
41
  @items = data.map {|datum|
44
- next datum if Model === datum
45
-
46
- if block = self.class.parse
47
- block.call(datum)
42
+ if Model === datum
43
+ datum
48
44
  else
49
45
  model.new(datum)
50
46
  end
@@ -52,7 +48,15 @@ class Collection
52
48
  end
53
49
  end
54
50
 
55
- include Enumerable
51
+ def [](id)
52
+ @items.find { |e| e.id! == id }
53
+ end
54
+
55
+ def replace(array)
56
+ @items = array
57
+
58
+ self
59
+ end
56
60
 
57
61
  def each(&block)
58
62
  return enum_for :each unless block
@@ -19,9 +19,19 @@ class Component
19
19
  events = @events
20
20
 
21
21
  klass.instance_eval {
22
- @element = element if element
23
- @tag = tag if tag
24
- @events = events.clone if events
22
+ @element = element
23
+
24
+ if tag
25
+ @tag = tag.dup
26
+ @tag[:class] = @tag[:class].dup
27
+ end
28
+
29
+ if events
30
+ @events = events.dup
31
+ @events.each_key {|key|
32
+ @events[key] = @events[key].dup
33
+ }
34
+ end
25
35
  }
26
36
  end
27
37
 
@@ -30,9 +40,49 @@ class Component
30
40
  end
31
41
 
32
42
  def self.tag(options = nil)
43
+ return @tag unless options
44
+
45
+ @tag ||= {}
46
+
47
+ if cls = options.delete(:class)
48
+ @tag[:class] = Array(@tag[:class]).concat(Array(cls))
49
+ end
50
+
51
+ @tag.merge!(options)
52
+ end
53
+
54
+ def self.tag!(options = nil)
33
55
  options ? @tag = options : @tag
34
56
  end
35
57
 
58
+ def self.inheritance
59
+ ancestors.take_while {|klass|
60
+ klass != Component
61
+ }.map {|klass|
62
+ next unless klass.ancestors.include?(Component)
63
+ next unless klass.css
64
+
65
+ tag = klass.tag
66
+ element = klass.element
67
+ parent = klass.superclass
68
+
69
+ if parent != Component
70
+ next if element != parent.element
71
+ next if tag && !parent.tag
72
+ next if tag && (tag[:class] != parent.tag[:class] || tag[:id] != parent.tag[:id])
73
+ else
74
+ next if element
75
+ next if tag && (tag[:class] || tag[:id])
76
+ end
77
+
78
+ if klass.name
79
+ klass.name.gsub(/::/, '-').downcase
80
+ else
81
+ "lissio-#{klass.object_id}"
82
+ end
83
+ }.compact
84
+ end
85
+
36
86
  def self.events
37
87
  @events ||= Hash.new { |h, k| h[k] = [] }
38
88
  end
@@ -40,16 +90,10 @@ class Component
40
90
  def self.on(name, selector = nil, method = nil, &block)
41
91
  if block
42
92
  events[name] << [selector, block]
43
-
44
- [name, selector, block]
45
93
  elsif method
46
94
  events[name] << [selector, method]
47
-
48
- [name, selector, method]
49
95
  else
50
- events[name] << [nil, method]
51
-
52
- [name, nil, method]
96
+ events[name] << [nil, selector]
53
97
  end
54
98
  end
55
99
 
@@ -85,23 +129,63 @@ class Component
85
129
  end
86
130
  end
87
131
 
88
- def self.css(content = nil, &block)
132
+ def self.text(string = nil, &block)
133
+ if block
134
+ render {
135
+ element.inner_text = instance_exec(&block)
136
+ }
137
+ else
138
+ render {
139
+ element.inner_text = string
140
+ }
141
+ end
142
+ end
143
+
144
+ def self.css!(content = nil, &block)
89
145
  if content || block
90
- @style.remove if @style
146
+ @global.remove if @global
91
147
 
92
- @style = CSS(content, &block)
93
- @style.append_to($document.head)
148
+ @global = CSS(content, &block)
149
+ @global.append_to($document.head)
94
150
  else
95
- CSS::StyleSheet.new(@style)
151
+ CSS::StyleSheet.new(@global) if @global
96
152
  end
97
153
  end
98
154
 
99
- attr_accessor :parent
155
+ def self.css(&block)
156
+ if block
157
+ selector = if @tag && id = @tag[:id]
158
+ "##{id}"
159
+ elsif @tag && cls = @tag[:class]
160
+ ".#{Array(cls).join('.')}"
161
+ elsif @element
162
+ @element
163
+ elsif self.name
164
+ ".#{self.name.gsub(/::/, '-').downcase}"
165
+ else
166
+ ".lissio-#{object_id}"
167
+ end
168
+
169
+ @local.remove if @local
100
170
 
101
- def initialize(parent = nil)
102
- @parent = parent
171
+ @local = CSS do
172
+ rule selector do
173
+ if block.arity == 0
174
+ instance_exec(&block)
175
+ else
176
+ block.call(self)
177
+ end
178
+ end
179
+ end
180
+
181
+ @local.append_to($document.head)
182
+ else
183
+ CSS::StyleSheet.new(@local) if @local
184
+ end
103
185
  end
104
186
 
187
+ attr_accessor :parent
188
+
105
189
  def tag
106
190
  { name: :div }.merge(self.class.tag || {})
107
191
  end
@@ -121,17 +205,27 @@ class Component
121
205
  end
122
206
 
123
207
  elem.add_class(*tag[:class]) if tag[:class]
124
- elem[:id] = tag[:id] if tag[:id]
208
+ elem.add_class(*self.class.inheritance)
125
209
 
126
- self.class.events.each {|name, blocks|
127
- blocks.each {|selector, block|
128
- if block.is_a? Symbol
129
- elem.on(name, selector, &method(block))
130
- else
131
- elem.on(name, selector) {|*args|
132
- instance_exec(*args, &block)
133
- }
134
- end
210
+ tag.each {|name, value|
211
+ if name != :class && name != :name
212
+ elem[name] = value
213
+ end
214
+ }
215
+
216
+ [self.class.events, @events].compact.each {|events|
217
+ events.each {|name, blocks|
218
+ blocks.each {|selector, block|
219
+ if Symbol === block
220
+ elem.on name, selector do |*args|
221
+ __send__ block, *args
222
+ end
223
+ else
224
+ elem.on name, selector do |*args|
225
+ instance_exec(*args, &block)
226
+ end
227
+ end
228
+ }
135
229
  }
136
230
  }
137
231
 
@@ -139,29 +233,49 @@ class Component
139
233
  end
140
234
 
141
235
  def on(name, selector = nil, method = nil, &block)
142
- self.class.on(name, selector, method, &block)
143
-
144
236
  if @element
145
237
  if block
146
- @element.on(name, selector) {|*args|
238
+ @element.on name, selector do |*args|
147
239
  instance_exec(*args, &block)
148
- }
240
+ end
149
241
  elsif method
150
- @element.on(name, selector, &method(method))
242
+ @element.on name, selector do |*args|
243
+ __send__ method, *args
244
+ end
151
245
  else
152
- @element.on(name, &method(method))
246
+ @element.on name do |*args|
247
+ __send__ method, *args
248
+ end
153
249
  end
154
- end
250
+ else
251
+ @events ||= Hash.new { |h, k| h[k] = [] }
155
252
 
156
- self
253
+ if block
254
+ @events[name] << [selector, block]
255
+ elsif method
256
+ @events[name] << [selector, method]
257
+ else
258
+ @events[name] << [nil, selector]
259
+ end
260
+ end
157
261
  end
158
262
 
159
263
  # When overriding, remember to call super as last.
160
264
  def render(*)
161
- element.trigger :render, self
265
+ element.trigger! :render, self
162
266
  element
163
267
  end
164
268
 
269
+ def trigger(*args, &block)
270
+ element.trigger(*args, &block)
271
+ self
272
+ end
273
+
274
+ def trigger!(*args, &block)
275
+ element.trigger!(*args, &block)
276
+ self
277
+ end
278
+
165
279
  def remove
166
280
  @element.remove if @element
167
281
  end
@@ -169,7 +283,6 @@ class Component
169
283
  alias destroy remove
170
284
  end
171
285
 
172
-
173
286
  Browser::DOM::Builder.for Component do |_, item|
174
287
  item.render
175
288
  end
@@ -13,10 +13,21 @@ class Alert < Component
13
13
  end
14
14
 
15
15
  def render
16
- if @options[:escape] == false
16
+ if Exception === @message
17
+ element << @message.inspect
18
+ element << DOM { br; br }
19
+
20
+ @message.backtrace.each {|line|
21
+ element << line
22
+ element << DOM { br }
23
+ }
24
+ elsif @options[:escape] == false
17
25
  element.inner_html = @message
18
26
  else
19
- element << @message
27
+ @message.each_line {|line|
28
+ element << line
29
+ element << DOM { br }
30
+ }
20
31
  end
21
32
 
22
33
  super
@@ -25,82 +36,61 @@ class Alert < Component
25
36
  tag class: :alert
26
37
 
27
38
  css do
28
- rule '.alert' do
29
- border 1.px, :solid, :transparent
39
+ border 1.px, :solid, :transparent
30
40
 
31
- padding 15.px
41
+ padding 15.px
32
42
 
33
- rule 'a' do
34
- font weight: :bold
35
- end
43
+ rule 'a' do
44
+ font weight: :bold
36
45
  end
37
46
  end
38
47
 
39
- def self.customize(*args, &block)
40
- if args.length == 1
41
- options = args.first
42
- else
43
- name, options = args
44
- end
45
-
46
- name ||= "alert-custom-#{rand(10000)}"
47
- options ||= {}
48
-
49
- if self == Alert
50
- inherited = []
51
- else
52
- inherited = class_names
53
- end
54
-
48
+ def self.customize(options = {}, &block)
55
49
  Class.new(self) {
56
- define_singleton_method :class_names do
57
- inherited + [name]
58
- end
59
-
60
- tag class: [:alert, name, *inherited]
61
-
62
50
  css do
63
- rule ".alert#{".#{inherited.join('.')}" unless inherited.empty?}.#{name}" do
64
- instance_exec(&block) if block
65
-
66
- if value = options[:background] || options[:bg]
67
- background color: value
68
- end
51
+ if value = options[:background] || options[:bg]
52
+ background color: value
53
+ end
69
54
 
70
- if value = options[:foreground] || options[:fg]
71
- color value
72
- end
55
+ if value = options[:foreground] || options[:fg]
56
+ color value
57
+ end
73
58
 
74
- if value = options[:border]
75
- border color: value
76
- end
59
+ if value = options[:border]
60
+ border color: value
61
+ end
77
62
 
78
- if value = options[:padding]
79
- padding value
80
- end
63
+ if value = options[:padding]
64
+ padding value
81
65
  end
66
+
67
+ if block.arity == 0
68
+ instance_exec(&block)
69
+ else
70
+ block.call(self)
71
+ end if block
82
72
  end
83
73
  }
84
74
  end
85
75
 
86
- Info = customize :info,
76
+ Info = customize \
87
77
  background: '#d9edf7',
88
78
  foreground: '#3a87ad',
89
79
  border: '#bce8f1'
90
80
 
91
- Success = customize :success,
81
+ Success = customize \
92
82
  message: "The operation was successful.",
93
83
  background: '#dff0d8',
94
84
  foreground: '#468847',
95
85
  border: '#d6e9c6'
96
86
 
97
- Warning = customize :warning,
87
+ Warning = customize \
98
88
  message: "Something might have gone wrong.",
99
89
  background: '#fcf8e3',
100
90
  foreground: '#c09853',
101
91
  border: '#fbeed5'
102
92
 
103
- Danger = customize :danger,
93
+ Danger = customize \
104
94
  message: "An unexpected error has occurred.",
105
95
  background: '#f2dede',
106
96
  foreground: '#b94a48',