lissio 0.1.0.beta3 → 0.1.0

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 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