opalla 0.1.0 → 0.1.1
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 +4 -4
- data/README.md +394 -14
- data/lib/opalla.rb +11 -4
- data/lib/opalla/component_helper.rb +28 -0
- data/lib/opalla/controller_add_on.rb +11 -0
- data/lib/opalla/engine.rb +27 -0
- data/lib/opalla/middleware.rb +20 -0
- data/lib/opalla/util.rb +63 -0
- data/lib/opalla/version.rb +1 -1
- data/lib/rails/generators/opalla/assets_generator.rb +23 -0
- data/lib/rails/generators/opalla/collection_generator.rb +19 -0
- data/lib/rails/generators/opalla/component_generator.rb +27 -0
- data/lib/rails/generators/opalla/install_generator.rb +62 -0
- data/lib/rails/generators/opalla/model_generator.rb +19 -0
- data/opal/collection.rb +71 -0
- data/opal/component.rb +136 -0
- data/opal/controller.rb +60 -0
- data/opal/diffDOM.js +1371 -0
- data/opal/diff_dom.rb +26 -0
- data/opal/element.rb +17 -0
- data/opal/hex_random.rb +12 -0
- data/opal/model.rb +50 -0
- data/opal/opalla.rb +21 -0
- data/opal/router.rb +66 -0
- data/opal/sha1.js +366 -0
- data/opal/view_helper.rb +168 -0
- data/opalla.gemspec +8 -0
- data/opalla.gif +0 -0
- metadata +109 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b420c0145b5c3f9a19e09a13662039babd80df2f
|
4
|
+
data.tar.gz: 9aad2ce70fb5d1b98fc5039c4dbe3c727e2e63e3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a1ff2aeb30b377ffcbc3718b73642be5338251088750a588ebebc6d9db4aca5cad6ea9a51c1c8b8108d93df15a7c148010bf06b5120a3a1844ba751a52093fbe
|
7
|
+
data.tar.gz: 5a83dc76cdfaee6cfc9b0359dbcd981c5273c2748835ef1c405e13b50f14adee079a2049229c358ca8b489b34a9840bc7ca7e36de19bafd381d9ccf36ef7a9a8
|
data/README.md
CHANGED
@@ -1,41 +1,421 @@
|
|
1
1
|
# Opalla
|
2
|
+

|
2
3
|
|
3
|
-
|
4
|
+
Opalla brings Rails way to the front-end. It follows a Rails conventions mixed with a little of Backbone.
|
4
5
|
|
5
|
-
|
6
|
+
It's built on top of `opal` and `opal-rails`.
|
6
7
|
|
7
8
|
## Installation
|
8
|
-
|
9
9
|
Add this line to your application's Gemfile:
|
10
10
|
|
11
11
|
```ruby
|
12
|
+
gem 'opal-rails'
|
12
13
|
gem 'opalla'
|
13
14
|
```
|
14
15
|
|
16
|
+
Additionally, if you want to use `haml`, add:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
gem 'opal-haml'
|
20
|
+
```
|
21
|
+
(That's actually ultra-highly recommended)
|
22
|
+
|
15
23
|
And then execute:
|
16
24
|
|
17
|
-
|
25
|
+
```
|
26
|
+
$ bundle
|
27
|
+
```
|
28
|
+
|
29
|
+
Then run:
|
30
|
+
|
31
|
+
```sh
|
32
|
+
|
33
|
+
rails g opalla:install
|
34
|
+
|
35
|
+
```
|
36
|
+
|
37
|
+
Opalla is a front-end MVC framework. So you will to have this folder structure:
|
18
38
|
|
19
|
-
|
39
|
+
```
|
40
|
+
your_app
|
41
|
+
\_app
|
42
|
+
\_assets
|
43
|
+
\_javascripts
|
44
|
+
\_components
|
45
|
+
\_controllers
|
46
|
+
\_lib
|
47
|
+
\_collections
|
48
|
+
\_models
|
49
|
+
\_views
|
50
|
+
```
|
20
51
|
|
21
|
-
|
52
|
+
And that’s it! You’re ready to drive Opalla.
|
22
53
|
|
23
54
|
## Usage
|
24
55
|
|
25
|
-
|
56
|
+
### Router & Controllers
|
57
|
+
It all starts with the Opalla router. It will catch the current URL and direct to the controller/action, exactly like Rails does.
|
26
58
|
|
27
|
-
|
59
|
+
So for example, say you have the following route:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
get 'pages#index'
|
63
|
+
```
|
28
64
|
|
29
|
-
|
65
|
+
You can generate controllers by running the default Rails controller generator:
|
30
66
|
|
31
|
-
|
67
|
+
```sh
|
32
68
|
|
33
|
-
|
69
|
+
rails g controller pages
|
34
70
|
|
35
|
-
|
71
|
+
```
|
36
72
|
|
73
|
+
If you just installed Opalla on your existing app and want to generate Opalla controllers for it, just re-run the command above and leave Rails do the rest! (It won't overwrite anything unless you explicitly ask for).
|
37
74
|
|
38
|
-
|
75
|
+
`Opalla::Router` will automatically import the routes and instantiate the `PagesController` inside your `javascripts/controller/pages_controller.rb` and trigger the `index` action:
|
39
76
|
|
40
|
-
|
77
|
+
```ruby
|
78
|
+
|
79
|
+
class PagesController < ApplicationController
|
80
|
+
|
81
|
+
def index
|
82
|
+
el.html 'Hello World!'
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
That would replace the `<body>` tag with 'Hello World', as the default `el` for a controller. If you want to change that, you can add `el 'YOU_SELECTOR_HERE'`, in a jQuery selector fashion. So for example:
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
|
92
|
+
class PagesController < ApplicationController
|
93
|
+
el '.main-content'
|
94
|
+
|
95
|
+
def index
|
96
|
+
el.html 'Hello World!'
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
```
|
101
|
+
|
102
|
+
### Templates
|
103
|
+
Your Opalla templates will live under `app/assets/javascripts/views`. There you have `./controllers` and `./components`. Note that components templates should start their names with `_`, just like `partials`.
|
104
|
+
|
105
|
+
Opalla will automatically have your templates folder added to your server side (Rails). So that means your templates will be rendered server-side and client-side. Yay!
|
106
|
+
|
107
|
+
The template will be able to get any access variables (`@variable`) and `methods`. Please note that you have to have both set on server and client sides.
|
108
|
+
|
109
|
+
Server-side:
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
|
113
|
+
class PagesController < ApplicationController
|
114
|
+
helper_method :something_awesome
|
115
|
+
|
116
|
+
def index
|
117
|
+
@dude = 'Pedro'
|
118
|
+
end
|
119
|
+
|
120
|
+
def something_awesome
|
121
|
+
'Rails'
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
```
|
127
|
+
|
128
|
+
Client-side, note that all methods are available by default, no need to set them as `helper_methods`:
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
|
132
|
+
class PagesController < ApplicationController
|
133
|
+
el '.main-content'
|
134
|
+
|
135
|
+
def index
|
136
|
+
@dude = 'Pedro'
|
137
|
+
render
|
138
|
+
end
|
139
|
+
|
140
|
+
def something_awesome
|
141
|
+
'Rails'
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
```
|
146
|
+
|
147
|
+
The following `haml` layout would work for both sides:
|
148
|
+
|
149
|
+
```haml
|
150
|
+
|
151
|
+
%main
|
152
|
+
The dude is called #{@dude}.
|
153
|
+
He works with #{something_awesome}
|
154
|
+
|
155
|
+
```
|
156
|
+
|
157
|
+
If you want to share variables with your front-end Opalla MVC, that’s ok. Use the `expose` method automatically available on your server-side controllers:
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
|
161
|
+
def index
|
162
|
+
expose message: 'Hello World!',
|
163
|
+
my_array: [1,2,3]
|
164
|
+
end
|
165
|
+
|
166
|
+
```
|
167
|
+
|
168
|
+
And then you can use the data normally in your views:
|
169
|
+
|
170
|
+
```haml
|
171
|
+
%p= message
|
172
|
+
%p= my_array.join('/')
|
173
|
+
```
|
174
|
+
|
175
|
+
That variables will be automatically available on both sides of your application.
|
176
|
+
|
177
|
+
### Components
|
178
|
+
`app/assets/javascripts/components` is where you components live. They will by default render the folder located on `app/assets/javascripts/views/components/_COMPONENT_NAME` (without the '_component' part of the name)
|
179
|
+
|
180
|
+
To create components:
|
181
|
+
|
182
|
+
```sh
|
183
|
+
|
184
|
+
rails g opalla:component my_component
|
185
|
+
|
186
|
+
```
|
187
|
+
|
188
|
+
They are instantiated and rendered from the controller layout, like this:
|
189
|
+
|
190
|
+
```haml
|
191
|
+
|
192
|
+
%main
|
193
|
+
The dude is called #{@dude}.
|
194
|
+
He works with #{something_awesome}
|
195
|
+
component(:contact_box)
|
196
|
+
|
197
|
+
```
|
198
|
+
|
199
|
+
That will look for the component `app/assets/javascripts/components/contact_box_component.rb`:
|
200
|
+
|
201
|
+
```ruby
|
202
|
+
|
203
|
+
class ContactBoxComponent < ApplicationComponent
|
204
|
+
|
205
|
+
end
|
206
|
+
```
|
207
|
+
|
208
|
+
#### Model Data
|
209
|
+
The components can get their data from a `Opalla::Model`. Here's how it goes:
|
210
|
+
|
211
|
+
First, generate your model:
|
212
|
+
|
213
|
+
```sh
|
214
|
+
bin/rails g opalla:model contact_info
|
215
|
+
```
|
216
|
+
|
217
|
+
Let’s add a simple attr_accessor:
|
218
|
+
|
219
|
+
```ruby
|
220
|
+
class Opalla::Model
|
221
|
+
attr_accessor :email
|
222
|
+
end
|
223
|
+
```
|
224
|
+
|
225
|
+
In the server-side controller you can expose the data, so that will be rendered from the server as default (no one wants blank pages to maybe hurt the SEO):
|
226
|
+
|
227
|
+
```ruby
|
228
|
+
class PagesController < ApplicationController
|
229
|
+
@contact_info = ContactInfo.new
|
230
|
+
@contact_info.email = 'pedro@pedromaciel.com'
|
231
|
+
expose contact: @contact_info
|
232
|
+
end
|
233
|
+
```
|
234
|
+
|
235
|
+
Then, in your server side, you don’t need anything for this case, except for render:
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
|
239
|
+
class PagesController < ApplicationController
|
240
|
+
el '.main-content'
|
241
|
+
|
242
|
+
def index
|
243
|
+
render
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
|
248
|
+
```
|
249
|
+
|
250
|
+
In the controller template:
|
251
|
+
|
252
|
+
```haml
|
253
|
+
|
254
|
+
%main
|
255
|
+
component(model: @contact_box)
|
256
|
+
|
257
|
+
```
|
258
|
+
|
259
|
+
In the component template:
|
260
|
+
|
261
|
+
```haml
|
262
|
+
|
263
|
+
.contact-info
|
264
|
+
.email= model.email
|
265
|
+
|
266
|
+
```
|
267
|
+
|
268
|
+
In the model (`app/assets/javascripts/models/contact_info.rb`):
|
41
269
|
|
270
|
+
```ruby
|
271
|
+
class ContactInfo
|
272
|
+
def initialize
|
273
|
+
end
|
274
|
+
end
|
275
|
+
```
|
276
|
+
|
277
|
+
### Collection Data
|
278
|
+
|
279
|
+
Generate collections:
|
280
|
+
```sh
|
281
|
+
bin/rails g opalla:collection products
|
282
|
+
```
|
283
|
+
|
284
|
+
Opalla will automatically assume that the model is the singular name. So `products` for example is a collection of `product` model. So you have to have the `product` model as well.
|
285
|
+
|
286
|
+
When working with collection components, you’ll often want to work with `data-attributes`. Opalla has a nifty way to help you:
|
287
|
+
|
288
|
+
Consider you have the following component:
|
289
|
+
```ruby
|
290
|
+
class ProductComponent < ApplicationComponent
|
291
|
+
|
292
|
+
end
|
293
|
+
```
|
294
|
+
|
295
|
+
With the following collection (notice the binding):
|
296
|
+
```ruby
|
297
|
+
class Products < Opalla::Collection
|
298
|
+
bind :price, :category
|
299
|
+
end
|
300
|
+
```
|
301
|
+
|
302
|
+
On your collection model, you set data:
|
303
|
+
```ruby
|
304
|
+
class Product < Opalla::Model
|
305
|
+
data :price, :category, :model_id
|
306
|
+
end
|
307
|
+
```
|
308
|
+
|
309
|
+
On your component template:
|
310
|
+
```haml
|
311
|
+
.products
|
312
|
+
collection.each |product|
|
313
|
+
.product{data=product.data}
|
314
|
+
end
|
315
|
+
end
|
316
|
+
```
|
317
|
+
|
318
|
+
That will generate data attributes: `data-price`, `data-category`, `data-model-id`, the last enabling you to incorporate events in a very simple fashion
|
319
|
+
|
320
|
+
```ruby
|
321
|
+
class ProductComponent < ApplicationComponent
|
322
|
+
events 'click .product' -> target { buy(collection.find(target)) }
|
323
|
+
end
|
324
|
+
```
|
325
|
+
|
326
|
+
That would trigger the buy element providing the `model` as argument. That’s because the `find method` in collections will look for the element or closest ancestor that has a `data-model-id` and return the model itself for you. Really easy!
|
327
|
+
|
328
|
+
Check the next chapter to see more ways on how collections can be very useful.
|
329
|
+
|
330
|
+
### Bindings
|
331
|
+
|
332
|
+
You can bind data from a model or collection to the component. That will trigger a `#render` action on the component:
|
333
|
+
|
334
|
+
```ruby
|
335
|
+
|
336
|
+
class ContactBoxComponent < ApplicationComponent
|
337
|
+
bind :email, :name
|
338
|
+
|
339
|
+
end
|
340
|
+
|
341
|
+
```
|
342
|
+
|
343
|
+
And that's it! Everytime the resource attributes change, being it a `model` or a `collection`, the component will be re-rendered.
|
344
|
+
|
345
|
+
#### 2-way binding
|
346
|
+
If you assign a `[data-bind='ATTRIBUTE_NAME']` to an input element on your template, it will change look for the closest ancestor that has `[data-model-id]` and change its attribute whenever you change the input.
|
347
|
+
|
348
|
+
Example:
|
349
|
+
```haml
|
350
|
+
.product{data: product.data} # it is expected you set the model data
|
351
|
+
input{data-bind: 'name'}
|
352
|
+
```
|
353
|
+
|
354
|
+
### Events
|
355
|
+
Similarly to Backbone, you can add events in the following way:
|
356
|
+
|
357
|
+
```ruby
|
358
|
+
|
359
|
+
class PagesController < ApplicationController
|
360
|
+
el '.main-content'
|
361
|
+
events 'click a' => :do_something
|
362
|
+
|
363
|
+
def index
|
364
|
+
el.html 'Hello World!'
|
365
|
+
end
|
366
|
+
|
367
|
+
def do_something
|
368
|
+
alert 'I\'m doing something!'
|
369
|
+
end
|
370
|
+
|
371
|
+
end
|
372
|
+
|
373
|
+
```
|
374
|
+
|
375
|
+
You can also provide a `lambda` instead:
|
376
|
+
|
377
|
+
```ruby
|
378
|
+
class PagesController < ApplicationController
|
379
|
+
el '.main-content'
|
380
|
+
events 'click a' => { alert "I'm doing something!" }
|
381
|
+
|
382
|
+
def index
|
383
|
+
el.html 'Hello World!'
|
384
|
+
end
|
385
|
+
|
386
|
+
end
|
387
|
+
|
388
|
+
```
|
389
|
+
You can also access the targetted object like this:
|
390
|
+
|
391
|
+
```ruby
|
392
|
+
class PagesController < ApplicationController
|
393
|
+
el '.main-content'
|
394
|
+
events 'click a' => target { alert 'I\'m doing something!' }
|
395
|
+
|
396
|
+
def index
|
397
|
+
el.html 'Hello World!'
|
398
|
+
end
|
399
|
+
|
400
|
+
def do_something
|
401
|
+
|
402
|
+
end
|
403
|
+
|
404
|
+
end
|
405
|
+
|
406
|
+
```
|
407
|
+
|
408
|
+
Please note that all events have their default behavior prevented by default.
|
409
|
+
|
410
|
+
## Development
|
411
|
+
The project is on beta phase. It's missing:
|
412
|
+
|
413
|
+
* Specs
|
414
|
+
* Tests
|
415
|
+
* Rubocops
|
416
|
+
* ActiveRecord models support
|
417
|
+
|
418
|
+
Fork and make a PR. Or talk to me at `pedro@pedromaciel.com`.
|
419
|
+
|
420
|
+
## License
|
421
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/lib/opalla.rb
CHANGED
@@ -1,5 +1,12 @@
|
|
1
|
-
require
|
1
|
+
require 'ostruct'
|
2
|
+
require 'bundler'; Bundler.require
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
4
|
+
require 'opal-browser'
|
5
|
+
require 'opalla/version'
|
6
|
+
require 'opalla/component_helper'
|
7
|
+
require 'opalla/controller_add_on'
|
8
|
+
require 'opalla/util'
|
9
|
+
require 'opalla/middleware'
|
10
|
+
require 'opalla/engine'
|
11
|
+
require_relative '../opal/model'
|
12
|
+
require_relative '../opal/collection'
|