lissio 0.1.0.beta1
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 +7 -0
- data/.gitignore +5 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +53 -0
- data/README.md +184 -0
- data/Rakefile +5 -0
- data/lib/lissio.rb +5 -0
- data/lib/lissio/server.rb +148 -0
- data/lissio.gemspec +27 -0
- data/opal/lissio.rb +26 -0
- data/opal/lissio/adapter.rb +45 -0
- data/opal/lissio/adapter/rest.rb +268 -0
- data/opal/lissio/adapter/storage.rb +167 -0
- data/opal/lissio/application.rb +66 -0
- data/opal/lissio/collection.rb +70 -0
- data/opal/lissio/component.rb +177 -0
- data/opal/lissio/component/alert.rb +110 -0
- data/opal/lissio/component/container.rb +56 -0
- data/opal/lissio/component/markdown.rb +332 -0
- data/opal/lissio/component/tooltip.rb +373 -0
- data/opal/lissio/model.rb +204 -0
- data/opal/lissio/router.rb +164 -0
- data/opal/lissio/version.rb +3 -0
- data/spec/route_spec.rb +65 -0
- data/spec/router_spec.rb +109 -0
- data/spec/spec_helper.rb +33 -0
- metadata +154 -0
data/opal/lissio.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'browser'
|
12
|
+
|
13
|
+
module Lissio
|
14
|
+
DOM = Browser::DOM
|
15
|
+
CSS = Browser::CSS
|
16
|
+
end
|
17
|
+
|
18
|
+
require 'lissio/version'
|
19
|
+
require 'lissio/router'
|
20
|
+
|
21
|
+
require 'lissio/model'
|
22
|
+
require 'lissio/collection'
|
23
|
+
require 'lissio/adapter'
|
24
|
+
|
25
|
+
require 'lissio/component'
|
26
|
+
require 'lissio/application'
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
module Lissio
|
12
|
+
|
13
|
+
class Adapter
|
14
|
+
attr_reader :for
|
15
|
+
|
16
|
+
def initialize(value)
|
17
|
+
if value.ancestors.include?(Model)
|
18
|
+
@type = :model
|
19
|
+
elsif value.ancestors.include?(Collection)
|
20
|
+
@type = :collection
|
21
|
+
else
|
22
|
+
raise ArgumentError, "the passed value isn't a Model or a Collection"
|
23
|
+
end
|
24
|
+
|
25
|
+
@for = value
|
26
|
+
end
|
27
|
+
|
28
|
+
def model?
|
29
|
+
@type == :model
|
30
|
+
end
|
31
|
+
|
32
|
+
def collection?
|
33
|
+
@type == :collection
|
34
|
+
end
|
35
|
+
|
36
|
+
def install
|
37
|
+
raise NotImplementedError, "install has not been implemented"
|
38
|
+
end
|
39
|
+
|
40
|
+
def uninstall
|
41
|
+
raise NotImplementedError, "uninstall has not been implemented"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,268 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'browser/location'
|
12
|
+
require 'browser/http'
|
13
|
+
require 'promise'
|
14
|
+
|
15
|
+
module Lissio; class Adapter
|
16
|
+
|
17
|
+
class REST < Adapter
|
18
|
+
def initialize(value, options = {}, &block)
|
19
|
+
super(value)
|
20
|
+
|
21
|
+
domain options[:domain] || $document.location.host
|
22
|
+
endpoint options[:endpoint] || endpoint_for(value)
|
23
|
+
|
24
|
+
if block.arity == 0
|
25
|
+
instance_exec(&block)
|
26
|
+
else
|
27
|
+
block.call(self)
|
28
|
+
end if block
|
29
|
+
end
|
30
|
+
|
31
|
+
def domain(value = nil)
|
32
|
+
value ? @domain = value : @domain
|
33
|
+
end
|
34
|
+
|
35
|
+
def endpoint(value = nil, &block)
|
36
|
+
if value
|
37
|
+
if Proc === value
|
38
|
+
@endpoint = value
|
39
|
+
elsif model?
|
40
|
+
@endpoint = proc {|method, instance, id|
|
41
|
+
case method
|
42
|
+
when :fetch
|
43
|
+
"#{value}/#{id}"
|
44
|
+
|
45
|
+
when :save, :create, :destroy
|
46
|
+
"#{value}/#{instance.id!}"
|
47
|
+
end
|
48
|
+
}
|
49
|
+
else
|
50
|
+
@endpoint = proc {|method, instance, desc|
|
51
|
+
if method == :fetch
|
52
|
+
"#{value}?#{desc.encode_uri}"
|
53
|
+
end
|
54
|
+
}
|
55
|
+
end
|
56
|
+
elsif block
|
57
|
+
@endpoint = block
|
58
|
+
else
|
59
|
+
@endpoint
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def http(&block)
|
64
|
+
block ? @http = block : @http
|
65
|
+
end
|
66
|
+
|
67
|
+
def with(method, model, *args)
|
68
|
+
point = @endpoint.call(method, model, *args)
|
69
|
+
|
70
|
+
if Hash === point
|
71
|
+
point.first[0].to_s.downcase
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def url(method, model, *args)
|
76
|
+
point = @endpoint.call(method, model, *args)
|
77
|
+
|
78
|
+
if Hash === point
|
79
|
+
point = point.first[1]
|
80
|
+
end
|
81
|
+
|
82
|
+
"//#{domain}#{point}"
|
83
|
+
end
|
84
|
+
|
85
|
+
def install
|
86
|
+
if model?
|
87
|
+
@for.instance_eval {
|
88
|
+
def self.fetch(*args)
|
89
|
+
promise = Promise.new
|
90
|
+
with = adapter.with(:fetch, nil, *args) || :get
|
91
|
+
url = adapter.url(:fetch, nil, *args)
|
92
|
+
|
93
|
+
Browser::HTTP.send(with, url) do |req|
|
94
|
+
req.on :success do |res|
|
95
|
+
promise.resolve(new(res.json, *args))
|
96
|
+
end
|
97
|
+
|
98
|
+
req.on :failure do |res|
|
99
|
+
promise.reject(res.status)
|
100
|
+
end
|
101
|
+
|
102
|
+
adapter.http.call(req) if adapter.http
|
103
|
+
end
|
104
|
+
|
105
|
+
promise
|
106
|
+
end
|
107
|
+
|
108
|
+
def save
|
109
|
+
promise = Promise.new
|
110
|
+
with = adapter.with(:save, self) || :put
|
111
|
+
url = adapter.url(:save, self)
|
112
|
+
|
113
|
+
Browser::HTTP.send(with, url, to_json) do |req|
|
114
|
+
req.on :success do |res|
|
115
|
+
promise.resolve(res.status)
|
116
|
+
end
|
117
|
+
|
118
|
+
req.on :failure do |res|
|
119
|
+
promise.reject(res.status)
|
120
|
+
end
|
121
|
+
|
122
|
+
adapter.http.call(req) if adapter.http
|
123
|
+
end
|
124
|
+
|
125
|
+
promise
|
126
|
+
end
|
127
|
+
|
128
|
+
def create
|
129
|
+
promise = Promise.new
|
130
|
+
with = adapter.with(:create, self) || :post
|
131
|
+
url = adapter.url(:create, self)
|
132
|
+
|
133
|
+
Browser::HTTP.send(with, url, to_json) do |req|
|
134
|
+
req.on :success do |res|
|
135
|
+
promise.resolve(res.status)
|
136
|
+
end
|
137
|
+
|
138
|
+
req.on :failure do |res|
|
139
|
+
promise.reject(res.status)
|
140
|
+
end
|
141
|
+
|
142
|
+
adapter.http.call(req) if adapter.http
|
143
|
+
end
|
144
|
+
|
145
|
+
promise
|
146
|
+
end
|
147
|
+
|
148
|
+
def destroy
|
149
|
+
promise = Promise.new
|
150
|
+
with = adapter.with(:destroy, self) || :delete
|
151
|
+
url = adapter.url(:destroy, self)
|
152
|
+
|
153
|
+
Browser::HTTP.send(with, url) do |req|
|
154
|
+
req.on :success do |res|
|
155
|
+
promise.resolve(res.status)
|
156
|
+
end
|
157
|
+
|
158
|
+
req.on :failure do |res|
|
159
|
+
promise.reject(res.status)
|
160
|
+
end
|
161
|
+
|
162
|
+
adapter.http.call(req) if adapter.http
|
163
|
+
end
|
164
|
+
|
165
|
+
promise
|
166
|
+
end
|
167
|
+
|
168
|
+
def reload
|
169
|
+
promise = Promise.new
|
170
|
+
fetched = fetched_with.empty? ? [id!] : fetched_with
|
171
|
+
with = adapter.with(:fetch, self, *fetched) || :get
|
172
|
+
url = adapter.url(:fetch, self, *fetched)
|
173
|
+
|
174
|
+
Browser::HTTP.send(with, url) do |req|
|
175
|
+
req.on :success do |res|
|
176
|
+
initialize(res.json, *fetched_with)
|
177
|
+
|
178
|
+
promise.resolve(self)
|
179
|
+
end
|
180
|
+
|
181
|
+
req.on :failure do |res|
|
182
|
+
promise.reject(res.status)
|
183
|
+
end
|
184
|
+
|
185
|
+
adapter.http.call(req) if adapter.http
|
186
|
+
end
|
187
|
+
|
188
|
+
promise
|
189
|
+
end
|
190
|
+
}
|
191
|
+
else
|
192
|
+
@for.instance_eval {
|
193
|
+
def self.fetch(*args, &block)
|
194
|
+
promise = Promise.new
|
195
|
+
with = adapter.with(:fetch, nil, *args)
|
196
|
+
url = adapter.url(:fetch, nil, *args)
|
197
|
+
|
198
|
+
Browser::HTTP.send(with, url) do |req|
|
199
|
+
req.on :success do |res|
|
200
|
+
promise.resolve(new(res.json, *args))
|
201
|
+
end
|
202
|
+
|
203
|
+
req.on :failure do |res|
|
204
|
+
promise.reject(res.status)
|
205
|
+
end
|
206
|
+
|
207
|
+
adapter.http.call(req) if adapter.http
|
208
|
+
end
|
209
|
+
|
210
|
+
promise
|
211
|
+
end
|
212
|
+
|
213
|
+
def reload(&block)
|
214
|
+
promise = Promise.new
|
215
|
+
with = adapter.with(:fetch, self, *fetched_with) || :get
|
216
|
+
url = adapter.url(:fetch, self, *fetched_with)
|
217
|
+
|
218
|
+
Browser::HTTP.send(with, url) do |req|
|
219
|
+
req.on :success do |res|
|
220
|
+
initialize(res.json, *fetched_with)
|
221
|
+
promise.resolve(self)
|
222
|
+
end
|
223
|
+
|
224
|
+
req.on :failure do |res|
|
225
|
+
promise.reject(res.status)
|
226
|
+
end
|
227
|
+
|
228
|
+
adapter.http.call(req) if adapter.http
|
229
|
+
end
|
230
|
+
|
231
|
+
promise
|
232
|
+
end
|
233
|
+
}
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def uninstall
|
238
|
+
if model?
|
239
|
+
@for.instance_eval {
|
240
|
+
class << self
|
241
|
+
remove_method :fetch
|
242
|
+
end
|
243
|
+
|
244
|
+
remove_method :save
|
245
|
+
remove_method :create
|
246
|
+
remove_method :destroy
|
247
|
+
remove_method :reload
|
248
|
+
}
|
249
|
+
else
|
250
|
+
@for.instance_eval {
|
251
|
+
class << self
|
252
|
+
remove_method :fetch
|
253
|
+
end
|
254
|
+
|
255
|
+
remove_method :reload
|
256
|
+
}
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
private
|
261
|
+
def endpoint_for(klass)
|
262
|
+
klass.name.match(/([^:]+)$/) {|m|
|
263
|
+
"/#{m[1].downcase}"
|
264
|
+
}
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
end; end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'browser/storage'
|
12
|
+
require 'browser/immediate'
|
13
|
+
|
14
|
+
module Lissio; class Adapter
|
15
|
+
|
16
|
+
class Storage < Adapter
|
17
|
+
def initialize(value, options = {}, &block)
|
18
|
+
super(value)
|
19
|
+
|
20
|
+
@autoincrement = []
|
21
|
+
|
22
|
+
if collection?
|
23
|
+
@model = options[:model] || value.model
|
24
|
+
@filter = options[:filter] if options[:filter]
|
25
|
+
end
|
26
|
+
|
27
|
+
if block.arity == 0
|
28
|
+
instance_exec(&block)
|
29
|
+
else
|
30
|
+
block.call(self)
|
31
|
+
end if block
|
32
|
+
end
|
33
|
+
|
34
|
+
def model(name = nil)
|
35
|
+
name ? @model = name : @model
|
36
|
+
end
|
37
|
+
|
38
|
+
def filter(&block)
|
39
|
+
block ? @filter = block : @filter
|
40
|
+
end
|
41
|
+
|
42
|
+
def autoincrement(field = nil)
|
43
|
+
if field
|
44
|
+
@autoincrement << field
|
45
|
+
else
|
46
|
+
@autoincrement
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def autoincrement!(field, storage)
|
51
|
+
storage[[:__autoincrement__, field]] ||= 0
|
52
|
+
storage[[:__autoincrement__, field]] += 1
|
53
|
+
end
|
54
|
+
|
55
|
+
def install
|
56
|
+
if model?
|
57
|
+
@for.instance_eval {
|
58
|
+
def self.storage
|
59
|
+
$window.storage(name)
|
60
|
+
end
|
61
|
+
|
62
|
+
def storage
|
63
|
+
self.class.storage
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.fetch(id, &block)
|
67
|
+
proc {
|
68
|
+
block.call(storage[id] || :error)
|
69
|
+
}.defer
|
70
|
+
end
|
71
|
+
|
72
|
+
def create(&block)
|
73
|
+
proc {
|
74
|
+
key = id!
|
75
|
+
|
76
|
+
if key && storage[key]
|
77
|
+
block.call(:error) if block
|
78
|
+
else
|
79
|
+
adapter.autoincrement.each {|name|
|
80
|
+
unless __send__ name
|
81
|
+
__send__ "#{name}=", adapter.autoincrement!(name, storage)
|
82
|
+
end
|
83
|
+
}
|
84
|
+
|
85
|
+
storage[id!] = self
|
86
|
+
|
87
|
+
block.call(:ok) if block
|
88
|
+
end
|
89
|
+
}.defer
|
90
|
+
end
|
91
|
+
|
92
|
+
def save(&block)
|
93
|
+
proc {
|
94
|
+
if storage[id!]
|
95
|
+
storage[id!] = self
|
96
|
+
|
97
|
+
block.call(:ok) if block
|
98
|
+
else
|
99
|
+
block.call(:error) if block
|
100
|
+
end
|
101
|
+
}.defer
|
102
|
+
end
|
103
|
+
|
104
|
+
def destroy(&block)
|
105
|
+
proc {
|
106
|
+
if storage[id!]
|
107
|
+
storage.delete(id!)
|
108
|
+
|
109
|
+
block.call(:ok) if block
|
110
|
+
else
|
111
|
+
block.call(:error) if block
|
112
|
+
end
|
113
|
+
}.defer
|
114
|
+
end
|
115
|
+
}
|
116
|
+
else
|
117
|
+
@for.instance_eval {
|
118
|
+
def self.storage
|
119
|
+
$window.storage(adapter.model.name)
|
120
|
+
end
|
121
|
+
|
122
|
+
def storage
|
123
|
+
self.class.storage
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.fetch(*args, &block)
|
127
|
+
proc {
|
128
|
+
block.call new(storage.map {|name, value|
|
129
|
+
next if Array === name && name.length == 2 && name.first == :__autoincrement__
|
130
|
+
|
131
|
+
if !adapter.filter || adapter.filter.call(value, *args)
|
132
|
+
value
|
133
|
+
end
|
134
|
+
}.compact)
|
135
|
+
}.defer
|
136
|
+
end
|
137
|
+
}
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def uninstall
|
142
|
+
if model?
|
143
|
+
@for.instance_eval {
|
144
|
+
class << self
|
145
|
+
remove_method :storage
|
146
|
+
remove_method :fetch
|
147
|
+
end
|
148
|
+
|
149
|
+
remove_method :storage
|
150
|
+
remove_method :create
|
151
|
+
remove_method :save
|
152
|
+
remove_method :destroy
|
153
|
+
}
|
154
|
+
else
|
155
|
+
@for.instance_eval {
|
156
|
+
class << self
|
157
|
+
remove_method :storage
|
158
|
+
remove_method :fetch
|
159
|
+
end
|
160
|
+
|
161
|
+
remove_method :storage
|
162
|
+
}
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
end; end
|