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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dfaafd8786e4bd056829a65cd8f5a14f33949285
4
- data.tar.gz: 7720815c7598b37bc3319cfbfb510c026cf9d7ba
3
+ metadata.gz: 4d4328cde93f67bc47a4aee2d593220645316d93
4
+ data.tar.gz: 62a22329ca4140f17bbb78e8f753b13d91a12232
5
5
  SHA512:
6
- metadata.gz: 56951c1ca1358d86972136bc92c9795ae6c078c2fa1d84bf316ea7d7d0e5f874bdd080bda2c36a3ad4d0cc8bbf154e449c0585232b9bbdef254c6231eb3aced1
7
- data.tar.gz: 491f8196d41e43b1c3ef8a9f11a9ddb67b7f128f893f37650747d403d54fcd1e278936b989d77b68c67578f13be30295a7c5cc62f68a21acd301112315af033a
6
+ metadata.gz: 257d8ce4b732fcd54222164a2b62982a3e5c1e1ab0e840ad62a4e08e21c8e13ec0660343649dbc36edd4579b5c4d789fe7bd6e54696794874b655ecc78e7692d
7
+ data.tar.gz: f99393744bbc3d089fea238aa8e57226ea8ab2b75a01dbfd804552476a73f2416017200628d3dd4dde468b875f7195d46006011baeee68df63ca313b87072db6
data/Gemfile CHANGED
@@ -3,5 +3,4 @@ gemspec
3
3
 
4
4
  gem 'opal', github: 'opal/opal'
5
5
  gem 'opal-browser', github: 'opal/opal-browser'
6
- gem 'parslet', github: 'kschiess/parslet'
7
6
  gem 'rake'
data/README.md CHANGED
@@ -6,6 +6,14 @@ implement frontends completely on the client side.
6
6
  [Here](http://www.youtube.com/watch?v=7JU5ssyKczw) you can find the best
7
7
  musical background while developing **lissio** applications.
8
8
 
9
+ Getting Started
10
+ ---------------
11
+ To create a new lissio application just run `lissio new <path>`, change to the
12
+ created directory and run `lissio start`.
13
+
14
+ By default it will run the server on port `9001`, you can change it with
15
+ `--port`, run `lissio help start` to see a list of other options.
16
+
9
17
  Application
10
18
  -----------
11
19
  Every **lissio** frontend begins with an `Application` singleton.
@@ -43,8 +51,6 @@ in pure Ruby using various DSLs.
43
51
 
44
52
  ```ruby
45
53
  class MyComponent < Lissio::Component
46
- tag class: 'my-component'
47
-
48
54
  on :click, '.title' do
49
55
  alert 'You clicked on the title'
50
56
  end
@@ -59,15 +65,13 @@ class MyComponent < Lissio::Component
59
65
  end
60
66
 
61
67
  css do
62
- rule '.my-component' do
63
- rule '.title' do
64
- font size: 32.px
65
- end
66
-
67
- rule '.subtitle' do
68
- font size: 18.px,
69
- style: :italic
70
- end
68
+ rule '.title' do
69
+ font size: 32.px
70
+ end
71
+
72
+ rule '.subtitle' do
73
+ font size: 18.px,
74
+ style: :italic
71
75
  end
72
76
  end
73
77
  end
@@ -131,20 +135,14 @@ end
131
135
  Now you'll be able to fetch a model like this.
132
136
 
133
137
  ```ruby
134
- Message.fetch(1) {|msg|
135
- if Message === msg
136
- alert msg.content
137
- else
138
- alert msg.inspect
139
- end
138
+ Message.fetch(1).then {|msg|
139
+ alert msg.content
140
+ }.rescue {|error|
141
+ alert error.inspect
140
142
  }
141
143
  ```
142
144
 
143
- When you do operations using adapters you'll always have to provide a block
144
- that will be called, since all operations are asynchronous.
145
-
146
- The class check is done because the block will be either passed the model or an
147
- error.
145
+ All operations using adapters use promises, since they're all asynchronous.
148
146
 
149
147
  Server
150
148
  ------
data/bin/lissio CHANGED
@@ -8,49 +8,16 @@ end
8
8
  require 'thor'
9
9
  require 'rack'
10
10
  require 'fileutils'
11
+ require 'yaml'
11
12
 
12
13
  require 'lissio'
13
- require 'opal/browser'
14
14
 
15
15
  class CLI < Thor
16
- desc "start [OPTIONS]", "start the lissio server"
17
-
18
- option :server, type: :string
19
- option :port, type: :numeric, default: 9001
20
- option :host, type: :string, default: '0.0.0.0'
21
- option :use, type: :array, default: []
22
- option :path, type: :array, default: ['app', 'js']
23
- option :static, type: :array, default: ['/css', '/fonts', '/img']
24
- option :index, type: :string
25
- option :debug, type: :boolean
26
-
27
- def start
28
- options[:use].each {|u|
29
- require u
30
- }
31
-
32
- Rack::Server.start(
33
- Host: options[:host],
34
- Port: options[:port],
35
-
36
- app: Lissio::Server.new {|s|
37
- options[:path].each {|path|
38
- s.append_path path
39
- }
40
-
41
- s.static = options[:static]
16
+ desc "new [PATH]", "create a new lissio application"
42
17
 
43
- if index = options[:index]
44
- s.index = index
45
- end
18
+ option :adapter, type: :string
19
+ option :components, type: :string
46
20
 
47
- if options[:debug]
48
- s.debug = true
49
- end
50
- })
51
- end
52
-
53
- desc "new [PATH]", "create a new lissio application"
54
21
  def new(path = '.')
55
22
  FileUtils.mkpath path
56
23
  FileUtils.cd path do
@@ -106,6 +73,114 @@ class CLI < Thor
106
73
  end
107
74
  end
108
75
  end
76
+
77
+ desc "start", "start the lissio server"
78
+
79
+ option :server, aliases: '-S', type: :string
80
+ option :port, aliases: '-p', type: :numeric
81
+ option :host, type: :string
82
+ option :main, aliases: '-m', type: :string
83
+ option :require, aliases: '-r', type: :array, default: []
84
+ option :use, aliases: '-u', type: :array, default: []
85
+ option :path, type: :array, default: ['app', 'js', 'opal']
86
+ option :static, aliases: '-s', type: :array, default: ['/css', '/fonts', '/img', '/js']
87
+ option :index, aliases: '-i', type: :string
88
+ option :debug, aliases: '-d', type: :boolean
89
+
90
+ def start
91
+ options = prepare
92
+
93
+ Rack::Server.start(
94
+ Host: options[:host] || '0.0.0.0',
95
+ Port: options[:port] || 9292,
96
+ server: options[:server],
97
+
98
+ app: Lissio::Server.new {|s|
99
+ options[:path].each {|path|
100
+ s.append_path path
101
+ }
102
+
103
+ s.static = options[:static]
104
+
105
+ if main = options[:main]
106
+ s.main = main
107
+ end
108
+
109
+ if index = options[:index]
110
+ s.index = index
111
+ end
112
+
113
+ if options[:debug]
114
+ s.debug = true
115
+ end
116
+ })
117
+ end
118
+
119
+ desc "build [PATH]", "create a server-less distribution of the application"
120
+
121
+ option :main, aliases: '-m', type: :string
122
+ option :require, aliases: '-r', type: :array, default: []
123
+ option :use, aliases: '-u', type: :array, default: []
124
+ option :path, type: :array, default: ['app', 'js', 'opal']
125
+ option :index, aliases: '-i', type: :string
126
+ option :force, aliases: '-f', type: :boolean, default: false
127
+
128
+ def build(output = 'index.html')
129
+ if !options[:force] && File.exists?(output)
130
+ raise ArgumentError, "'#{output}' already exists"
131
+ end
132
+
133
+ options = prepare
134
+
135
+ File.open output, 'w' do |f|
136
+ builder = Lissio::Builder.new {|b|
137
+ options[:path].each {|path|
138
+ b.append_path path
139
+ }
140
+
141
+ if main = options[:main]
142
+ b.main = main
143
+ end
144
+
145
+ if index = options[:index]
146
+ b.index = index
147
+ end
148
+ }
149
+
150
+ f.write builder.build
151
+ end
152
+ end
153
+
154
+ private
155
+ def prepare
156
+ options = self.options.dup
157
+
158
+ if File.readable? 'lissio.yml'
159
+ YAML.load_file('lissio.yml').tap {|opts|
160
+ %w[server port host index debug main].each {|name|
161
+ if opts.has_key?(name) && !options.has_key?(name)
162
+ options[name] = opts[name]
163
+ end
164
+ }
165
+
166
+ %w[use require path static].each {|name|
167
+ if opts.has_key? name
168
+ options[name] = Array(opts[name])
169
+ end
170
+ }
171
+ }
172
+ end
173
+
174
+ options[:require].each {|r|
175
+ require r
176
+ }
177
+
178
+ options[:use].each {|u|
179
+ Opal.use_gem u
180
+ }
181
+
182
+ options
183
+ end
109
184
  end
110
185
 
111
186
  CLI.start(ARGV)
@@ -1,5 +1,7 @@
1
1
  require 'opal'
2
+ require 'opal/browser'
2
3
 
3
4
  require 'lissio/server'
5
+ require 'lissio/builder'
4
6
 
5
7
  Opal.append_path File.expand_path('../../opal', __FILE__)
@@ -0,0 +1,54 @@
1
+ require 'opal/sprockets/environment'
2
+ require 'uglifier'
3
+ require 'forwardable'
4
+
5
+ module Lissio
6
+
7
+ class Builder
8
+ extend Forwardable
9
+
10
+ attr_accessor :debug, :index, :main, :static, :source_maps, :sprockets
11
+ def_delegators :@sprockets, :append_path, :use_gem
12
+
13
+ def initialize(&block)
14
+ @sprockets = Sprockets::Environment.new
15
+ @main = 'app'
16
+
17
+ Opal.paths.each {|path|
18
+ @sprockets.append_path path
19
+ }
20
+
21
+ block.call(self) if block
22
+ end
23
+
24
+ def build
25
+ source = if @path
26
+ unless File.exist?(@path)
27
+ raise "index does not exist: #{@path}"
28
+ end
29
+
30
+ File.read @path
31
+ elsif File.exist? 'index.html.erb'
32
+ File.read 'index.html.erb'
33
+ else
34
+ <<-HTML
35
+ <!DOCTYPE html>
36
+ <html>
37
+ <head>
38
+ <%= lissio %>
39
+ </head>
40
+ <body>
41
+ </body>
42
+ </html>
43
+ HTML
44
+ end
45
+
46
+ ::ERB.new(source).result binding
47
+ end
48
+
49
+ def lissio(source = main)
50
+ "<script>#{Uglifier.compile(@sprockets[source].to_s)}</script>"
51
+ end
52
+ end
53
+
54
+ end
@@ -61,8 +61,6 @@ class Server
61
61
  end
62
62
 
63
63
  File.read @path
64
- elsif File.exist? 'index.html'
65
- File.read 'index.html'
66
64
  elsif File.exist? 'index.html.erb'
67
65
  File.read 'index.html.erb'
68
66
  else
@@ -88,9 +86,10 @@ class Server
88
86
  end
89
87
 
90
88
  assets.map {|a|
91
- %Q{<script src="/assets/#{a.logical_path}?body=1"></script>}
89
+ %{<script src="/assets/#{a.logical_path}?body=1"></script>}
92
90
  }.join ?\n
93
91
  else
92
+ @server.sprockets.js_compressor = :uglify
94
93
  "<script src=\"/assets/#{source}.js\"></script>"
95
94
  end
96
95
  end
@@ -101,8 +100,13 @@ class Server
101
100
  attr_accessor :debug, :index, :main, :static, :source_maps, :sprockets
102
101
  def_delegators :@sprockets, :append_path, :use_gem
103
102
 
104
- def initialize(options = {}, &block)
105
- @sprockets = Opal::Environment.new
103
+ def initialize(&block)
104
+ @sprockets = Sprockets::Environment.new
105
+ @main = 'app'
106
+
107
+ Opal.paths.each {|path|
108
+ @sprockets.append_path path
109
+ }
106
110
 
107
111
  block.call(self) if block
108
112
  end
@@ -131,6 +135,7 @@ class Server
131
135
 
132
136
  @app ||= Rack::Builder.app do
133
137
  use Rack::ShowExceptions
138
+ use Rack::Deflater
134
139
 
135
140
  map '/assets' do
136
141
  run this.sprockets
@@ -16,12 +16,9 @@ Gem::Specification.new do |s|
16
16
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
17
  s.require_paths = ['lib']
18
18
 
19
- s.add_dependency 'opal', '>= 0.5.5'
20
- s.add_dependency 'opal-browser'
19
+ s.add_dependency 'opal', '>= 0.7.0'
20
+ s.add_dependency 'opal-browser', '>= 0.2.0.beta1'
21
21
  s.add_dependency 'rack'
22
-
23
- s.add_development_dependency 'opal-spec'
24
- s.add_development_dependency 'rake'
25
-
26
22
  s.add_dependency 'thor'
23
+ s.add_dependency 'uglifier'
27
24
  end
@@ -18,8 +18,11 @@ class REST < Adapter
18
18
  def initialize(value, options = {}, &block)
19
19
  super(value)
20
20
 
21
- domain options[:domain] || $document.location.host
21
+ domain options[:domain]
22
+ base options[:base]
22
23
  endpoint options[:endpoint] || endpoint_for(value)
24
+ parse &options[:parse]
25
+ error &options[:error]
23
26
 
24
27
  if block.arity == 0
25
28
  instance_exec(&block)
@@ -28,28 +31,50 @@ class REST < Adapter
28
31
  end if block
29
32
  end
30
33
 
34
+ def parse(&block)
35
+ block ? @parse = block : @parse
36
+ end
37
+
38
+ def error(&block)
39
+ block ? @error = block : @error
40
+ end
41
+
42
+ def http(&block)
43
+ block ? @http = block : @http
44
+ end
45
+
31
46
  def domain(value = nil)
32
47
  value ? @domain = value : @domain
33
48
  end
34
49
 
50
+ def base(value = nil)
51
+ value ? @base = value : @base
52
+ end
53
+
35
54
  def endpoint(value = nil, &block)
36
55
  if value
37
- if Proc === value
56
+ if Proc === value || Hash === value
38
57
  @endpoint = value
39
58
  elsif model?
40
- @endpoint = proc {|method, instance, id|
59
+ @endpoint = proc {|method, *args|
60
+ next value if args.empty?
61
+
41
62
  case method
42
63
  when :fetch
43
- "#{value}/#{id}"
64
+ "#{value}/#{args.first}"
44
65
 
45
66
  when :save, :create, :destroy
46
- "#{value}/#{instance.id!}"
67
+ "#{value}/#{args.first.id!}"
47
68
  end
48
69
  }
49
70
  else
50
- @endpoint = proc {|method, instance, desc|
71
+ @endpoint = proc {|method, *args|
51
72
  if method == :fetch
52
- "#{value}?#{desc.encode_uri}"
73
+ if desc = args.first
74
+ "#{value}?#{desc.encode_uri}"
75
+ else
76
+ value
77
+ end
53
78
  end
54
79
  }
55
80
  end
@@ -60,43 +85,62 @@ class REST < Adapter
60
85
  end
61
86
  end
62
87
 
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
88
+ def to(method, *args)
89
+ result = if Proc === @endpoint
90
+ @endpoint.call(method, *args)
91
+ else
92
+ @endpoint[method].call(*args)
72
93
  end
73
- end
74
94
 
75
- def url(method, model, *args)
76
- point = @endpoint.call(method, model, *args)
95
+ if Hash === result
96
+ with, point = result.first
97
+ else
98
+ point = result
99
+ end
77
100
 
78
- if Hash === point
79
- point = point.first[1]
101
+ url = if @base
102
+ "#@base#{point}"
103
+ elsif @domain
104
+ "//#@domain#{point}"
105
+ else
106
+ "//#{$document.location.host}#{point}"
80
107
  end
81
108
 
82
- "//#{domain}#{point}"
109
+ if with
110
+ [url, with]
111
+ else
112
+ url
113
+ end
83
114
  end
84
115
 
85
116
  def install
86
117
  if model?
87
118
  @for.instance_eval {
88
119
  def self.fetch(*args)
89
- promise = Promise.new
90
- with = adapter.with(:fetch, nil, *args) || :get
91
- url = adapter.url(:fetch, nil, *args)
120
+ promise = Promise.new
121
+ url, with = adapter.to(:fetch, *args)
92
122
 
93
- Browser::HTTP.send(with, url) do |req|
123
+ Browser::HTTP.send(with || :get, url) do |req|
94
124
  req.on :success do |res|
95
- promise.resolve(new(res.json, *args))
125
+ if block = adapter.parse
126
+ value = block.call(res.json, *args)
127
+
128
+ if self === value
129
+ promise.resolve(value)
130
+ else
131
+ promise.resolve(new(value, *args))
132
+ end
133
+ else
134
+ promise.resolve(new(res.json, *args))
135
+ end
96
136
  end
97
137
 
98
138
  req.on :failure do |res|
99
- promise.reject(res.status)
139
+ if block = adapter.error
140
+ promise.reject(block.call(res))
141
+ else
142
+ promise.reject(res.status)
143
+ end
100
144
  end
101
145
 
102
146
  adapter.http.call(req) if adapter.http
@@ -106,17 +150,20 @@ class REST < Adapter
106
150
  end
107
151
 
108
152
  def save
109
- promise = Promise.new
110
- with = adapter.with(:save, self) || :put
111
- url = adapter.url(:save, self)
153
+ promise = Promise.new
154
+ url, with = adapter.to(:save, self)
112
155
 
113
- Browser::HTTP.send(with, url, to_json) do |req|
156
+ Browser::HTTP.send(with || :put, url, to_json) do |req|
114
157
  req.on :success do |res|
115
158
  promise.resolve(res.status)
116
159
  end
117
160
 
118
161
  req.on :failure do |res|
119
- promise.reject(res.status)
162
+ if block = adapter.error
163
+ promise.reject(block.call(res))
164
+ else
165
+ promise.reject(res.status)
166
+ end
120
167
  end
121
168
 
122
169
  adapter.http.call(req) if adapter.http
@@ -126,17 +173,20 @@ class REST < Adapter
126
173
  end
127
174
 
128
175
  def create
129
- promise = Promise.new
130
- with = adapter.with(:create, self) || :post
131
- url = adapter.url(:create, self)
176
+ promise = Promise.new
177
+ url, with = adapter.to(:create, self)
132
178
 
133
- Browser::HTTP.send(with, url, to_json) do |req|
179
+ Browser::HTTP.send(with || :post, url, to_json) do |req|
134
180
  req.on :success do |res|
135
181
  promise.resolve(res.status)
136
182
  end
137
183
 
138
184
  req.on :failure do |res|
139
- promise.reject(res.status)
185
+ if block = adapter.error
186
+ promise.reject(block.call(res))
187
+ else
188
+ promise.reject(res.status)
189
+ end
140
190
  end
141
191
 
142
192
  adapter.http.call(req) if adapter.http
@@ -146,17 +196,20 @@ class REST < Adapter
146
196
  end
147
197
 
148
198
  def destroy
149
- promise = Promise.new
150
- with = adapter.with(:destroy, self) || :delete
151
- url = adapter.url(:destroy, self)
199
+ promise = Promise.new
200
+ url, with = adapter.to(:destroy, self)
152
201
 
153
- Browser::HTTP.send(with, url) do |req|
202
+ Browser::HTTP.send(with || :delete, url) do |req|
154
203
  req.on :success do |res|
155
204
  promise.resolve(res.status)
156
205
  end
157
206
 
158
207
  req.on :failure do |res|
159
- promise.reject(res.status)
208
+ if block = adapter.error
209
+ promise.reject(block.call(res))
210
+ else
211
+ promise.reject(res.status)
212
+ end
160
213
  end
161
214
 
162
215
  adapter.http.call(req) if adapter.http
@@ -166,12 +219,11 @@ class REST < Adapter
166
219
  end
167
220
 
168
221
  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)
222
+ promise = Promise.new
223
+ fetched = fetched_with.empty? ? [id!] : fetched_with
224
+ url, with = adapter.to(:fetch, self, *fetched)
173
225
 
174
- Browser::HTTP.send(with, url) do |req|
226
+ Browser::HTTP.send(with || :get, url) do |req|
175
227
  req.on :success do |res|
176
228
  initialize(res.json, *fetched_with)
177
229
 
@@ -179,7 +231,11 @@ class REST < Adapter
179
231
  end
180
232
 
181
233
  req.on :failure do |res|
182
- promise.reject(res.status)
234
+ if block = adapter.error
235
+ promise.reject(block.call(res))
236
+ else
237
+ promise.reject(res.status)
238
+ end
183
239
  end
184
240
 
185
241
  adapter.http.call(req) if adapter.http
@@ -191,17 +247,30 @@ class REST < Adapter
191
247
  else
192
248
  @for.instance_eval {
193
249
  def self.fetch(*args, &block)
194
- promise = Promise.new
195
- with = adapter.with(:fetch, nil, *args)
196
- url = adapter.url(:fetch, nil, *args)
250
+ promise = Promise.new
251
+ url, with = adapter.to(:fetch, *args)
197
252
 
198
- Browser::HTTP.send(with, url) do |req|
253
+ Browser::HTTP.send(with || :get, url) do |req|
199
254
  req.on :success do |res|
200
- promise.resolve(new(res.json, *args))
255
+ if block = adapter.parse
256
+ value = block.call(res.json, *args)
257
+
258
+ if self === value
259
+ promise.resolve(value)
260
+ else
261
+ promise.resolve(new(value, *args))
262
+ end
263
+ else
264
+ promise.resolve(new(res.json, *args))
265
+ end
201
266
  end
202
267
 
203
268
  req.on :failure do |res|
204
- promise.reject(res.status)
269
+ if block = adapter.error
270
+ promise.reject(block.call(res))
271
+ else
272
+ promise.reject(res.status)
273
+ end
205
274
  end
206
275
 
207
276
  adapter.http.call(req) if adapter.http
@@ -211,18 +280,21 @@ class REST < Adapter
211
280
  end
212
281
 
213
282
  def reload(&block)
214
- promise = Promise.new
215
- with = adapter.with(:fetch, self, *fetched_with) || :get
216
- url = adapter.url(:fetch, self, *fetched_with)
283
+ promise = Promise.new
284
+ url, with = adapter.to(:fetch, self, *fetched_with)
217
285
 
218
- Browser::HTTP.send(with, url) do |req|
286
+ Browser::HTTP.send(with || :get, url) do |req|
219
287
  req.on :success do |res|
220
288
  initialize(res.json, *fetched_with)
221
289
  promise.resolve(self)
222
290
  end
223
291
 
224
292
  req.on :failure do |res|
225
- promise.reject(res.status)
293
+ if block = adapter.error
294
+ promise.reject(block.call(res))
295
+ else
296
+ promise.reject(res.status)
297
+ end
226
298
  end
227
299
 
228
300
  adapter.http.call(req) if adapter.http