viu 0.0.2 → 0.0.3

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
  SHA256:
3
- metadata.gz: 2f24b77a5e5a5ada196406705ac4ff02102f7915321f0b22cc946f8a9fc27040
4
- data.tar.gz: 2648be1eec444b862d8c7c40f4ccf557011be42784a9ab29b0944f8251d8fff7
3
+ metadata.gz: a20c56c9d9d9efc2b46ed24d46628f24e671e28f5601cdd475939df6b2162974
4
+ data.tar.gz: 2b940845dd9fc93fc8af36c7341581b2c37d2cf28649d17f156b7f56666191c2
5
5
  SHA512:
6
- metadata.gz: fb6709c1bf1b41bf7cd451fc20683fcb6e98e7aa4bfe107a21bea21e8a0d0ece7277f6de65e75c385eb545498306bd75a148e497ce5e3799d6d8f875238ed6ce
7
- data.tar.gz: 6b75da7d7c59928851d230ed0b1f4dcb527cf3f0d8b3bbef93c6c87d1ecd8f023155d1ab5026b75b64223c45301c6e7b8fbc70c415401ecfb4ab2984401a323f
6
+ metadata.gz: 2864b1ecae62628434db59ea5256914b826c42305b66fa0bf6437319e16fa898e828fdea020b87c5d19e8537261f112732d7fbf90ef092e809e5369aa487f2e9
7
+ data.tar.gz: 1c0cb834d20c499808007b04a3c993a08a6858bb56bd03ac699ee27f047386f585b63075823f4d6cad84c714fefb71b714942865a2335500083f4954bedb53e6
@@ -1,3 +1,9 @@
1
+ ## 0.0.3 2020-09-02
2
+
3
+ - Added Viu::Json
4
+ - Added Viu::Xml
5
+ - Allow Viu::Html template! to receive a proc
6
+
1
7
  ## 0.0.2 2020-08-16
2
8
 
3
9
  ### Added
data/Gemfile CHANGED
@@ -7,3 +7,4 @@ gemspec
7
7
  rails_version = "#{ENV['RAILS_VERSION'] || '~> 5.2'}"
8
8
 
9
9
  gem "rails", rails_version == "master" ? { github: "rails/rails" } : rails_version
10
+ gem "oj"
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- viu (0.0.2)
4
+ viu (0.0.3)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -48,6 +48,7 @@ GEM
48
48
  minitest (~> 5.1)
49
49
  tzinfo (~> 1.1)
50
50
  arel (9.0.0)
51
+ bigdecimal (2.0.0)
51
52
  builder (3.2.4)
52
53
  concurrent-ruby (1.1.7)
53
54
  crass (1.0.6)
@@ -75,6 +76,8 @@ GEM
75
76
  nio4r (2.5.2)
76
77
  nokogiri (1.10.10)
77
78
  mini_portile2 (~> 2.4.0)
79
+ oj (3.10.12)
80
+ bigdecimal (>= 1.0, < 3)
78
81
  rack (2.2.3)
79
82
  rack-test (1.1.0)
80
83
  rack (>= 1.0, < 3)
@@ -134,6 +137,7 @@ DEPENDENCIES
134
137
  bundler (~> 1.17)
135
138
  haml (~> 5)
136
139
  minitest (~> 5.0)
140
+ oj
137
141
  rails (~> 5.2)
138
142
  rake (~> 12.3)
139
143
  simplecov (~> 0.18.0)
data/README.md CHANGED
@@ -1,26 +1,382 @@
1
1
  # Viu
2
2
 
3
- Currently this is a POC (proof of concept), on the usage of `ActionView` as a "real" view layer. Inspired on
4
- `view_component`, `cells` and others, `viu` is just a small wrapper around `AV`. It aims to work with the least amount
5
- of surprise with Rails, but with a few boundaries, like a View won't be able to automatically access `@ivars` defined
6
- in the controller, those have to explicitly be passed to them.
3
+ Rails' missing View layer.
7
4
 
8
- The project is already been tested on a small scale in our production environment.
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem "viu"
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```bash
16
+ $ bundle install
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```bash
22
+ $ gem install viu
23
+ ```
24
+
25
+ ## Rails Setup
26
+
27
+ Add this to your `config/application.rb` file:
28
+
29
+ ```ruby
30
+ config.eager_load_paths << Rails.root.join("app/views")
31
+ ```
32
+
33
+ This will add the `app/views` folder to the eager load paths, as it is not loaded there by default.
34
+
35
+ ## Viu::Html
36
+
37
+ Probably the most common view type of the wild web lands.
38
+
39
+ ### Usage
40
+
41
+ `app/views/awesome_view.rb`:
42
+ ```ruby
43
+ class AwesomeView < Viu::Html
44
+
45
+ layout! Layouts::ApplicationLayout
46
+
47
+ def initialize(posts:)
48
+ @posts = posts
49
+ end
50
+
51
+ private
52
+
53
+ def power_level_class(power)
54
+ return "over-9000" if power > 9000
55
+
56
+ "not-awesome-enough"
57
+ end
58
+ end
59
+ ```
60
+
61
+ `app/views/awesome_view.html.erb`:
62
+ ```erb
63
+ <h1>Awesomesauce</h1>
64
+ <%- @posts.each do |post| %>
65
+ <article class="<%= power_level_class(post.awesomeness) %>">
66
+ <h1><%= link_to post.title, post, class: "awesome-title" %></h1>
67
+ <p><%= post.summary %></p>
68
+ </article>
69
+ <%- end %>
70
+ ```
71
+
72
+ `app/controllers/home_controller.rb`:
73
+ ```ruby
74
+ # GET /awesome
75
+ def awesome
76
+ posts = Post.order(awesomeness: :desc)
77
+ render_view AwesomeView.new(posts: posts)
78
+ end
79
+ ```
9
80
 
10
- ## Your first View
81
+ Unless a template is given directly (using the `template! "file"` option inside the view), a view will try to find a
82
+ template with it's name.
83
+
84
+ As seen in the example above a view's template has access to all instance variables, public and private methods defined
85
+ in the view, as well as all `ActionView` helpers.
86
+
87
+ Also, the view won't have access to any variables or instance variables defined in the controller, those have to be
88
+ passed in directly, as shown in `AwesomeView.new(posts: posts)`.
89
+
90
+ ### ApplicationView
91
+
92
+ Usually it's a common practice in rails projects to define an `ApplicationController` or `ApplicationRecord` base class
93
+ that can be inherited from. The same can be done with views, an `ApplicationView` class is a good place to put some
94
+ basic functionalities that should be available to all views.
95
+
96
+ ### Callable templates
97
+
98
+ A view's template can be overridden with a `proc`. __Attention:__ currently using a callable template doesn't work with
99
+ layouts, they will be ignored.
100
+
101
+ ```ruby
102
+ # defining the view
103
+ class HeaderView < ApplicationView
104
+
105
+ template! proc { tag.h1 @title }
106
+
107
+ def initialize(title:)
108
+ @title = title
109
+ end
110
+ end
111
+
112
+ # rendering the view
113
+ render_view HeaderView.new(title: "Mas Gente!") # => "<h1>Mas Gente!</h1>"
114
+ ```
115
+
116
+ ### Layouts
117
+
118
+ By default a view won't be rendered inside a layout. To use a layout, one has to be declared, either directly on the
119
+ view or passed to the `render_view` method.
120
+
121
+ #### Defining a layout template
11
122
 
12
123
  `app/views/my_view.rb`:
13
124
  ```ruby
14
- class MyView < Viu::Html
15
- def initialize(name)
16
- @name = name
125
+ class MyView < ApplicationView
126
+ # This will look for an application template inside app/views/layouts,
127
+ # it can be a html.erb or any other template language defined in your application.
128
+ layout! 'layouts/application'
129
+ end
130
+ ```
131
+
132
+ `app/views/layouts/application.html.erb`:
133
+ ```erb
134
+ <html>
135
+ <head>
136
+ <title>A view view a Layout</title>
137
+ <%= stylesheet_link_tag "application" %>
138
+ <%= javascript_include_tag "application" %>
139
+ </head>
140
+ <body>
141
+ <header>Header from the layout</header>
142
+ <%= yield %>
143
+ </body>
144
+ </html>
145
+ ```
146
+
147
+ #### Defining a layout view
148
+
149
+ A layout can also be a `Viu::Layout` class, in this case it will work pretty much like a `Viu::Html`.
150
+
151
+ `app/views/my_view.rb`:
152
+ ```ruby
153
+ class MyView < ApplicationView
154
+ layout! Layouts::ApplicationLayout
155
+ end
156
+ ```
157
+
158
+ `app/views/layouts/application_layout.rb`:
159
+ ```ruby
160
+ module Layouts
161
+
162
+ # a layout needs to inherit from Viu::Layout
163
+ class ApplicationLayout < Viu::Layout
164
+
165
+ def header_text
166
+ "This is a Viu::Layout"
167
+ end
17
168
  end
18
169
  end
19
170
  ```
20
171
 
21
- `app/views/my_view.html.erb`:
172
+ `app/views/layouts/application_layout.html.erb`:
22
173
  ```erb
23
- <h1><%= @name %></h1>
174
+ <html>
175
+ <head>
176
+ <title>A Viu::Layout</title>
177
+ <%= stylesheet_link_tag "application" %>
178
+ <%= javascript_include_tag "application" %>
179
+ </head>
180
+ <body>
181
+ <header><%= header_text %></header>
182
+ <%= yield %>
183
+ </body>
184
+ </html>
24
185
  ```
25
186
 
26
- Unless a template is given directly, a view will try to find a template with it's name.
187
+ Similar as the `Viu::Html` a layout will search for a template with it's name, if none is given directly.
188
+
189
+ #### Defining a layout `proc`
190
+
191
+ A layout can also be declared as a `proc`, this is useful when the view wants to override the layout parameters.
192
+ The `proc` will be executed in the context of the view and the result must respond to `render_in`.
193
+
194
+ `app/views/my_view.rb`:
195
+ ```ruby
196
+ class MyView < ApplicationView
197
+
198
+ layout! proc { Layouts::ApplicationLayout.new(header_text: text) }
199
+
200
+ private
201
+
202
+ def text
203
+ "Text from the view"
204
+ end
205
+ end
206
+ ```
207
+
208
+ `app/views/layouts/application_layout.rb`:
209
+ ```ruby
210
+ module Layouts
211
+ class ApplicationLayout < Viu::Layout
212
+
213
+ attr_reader :header_text
214
+
215
+ def initialize(header_text: 'The header text')
216
+ @header_text = header_text
217
+ end
218
+ end
219
+ end
220
+ ```
221
+
222
+ `app/views/layouts/application_layout.html.erb`:
223
+ ```erb
224
+ <html>
225
+ <head>
226
+ <title>A Viu::Layout</title>
227
+ <%= stylesheet_link_tag "application" %>
228
+ <%= javascript_include_tag "application" %>
229
+ </head>
230
+ <body>
231
+ <header>
232
+ <!-- it will render "Text from the view" here -->
233
+ <%= header_text %>
234
+ </header>
235
+ <%= yield %>
236
+ </body>
237
+ </html>
238
+ ```
239
+
240
+ #### Overriding layout on `render_view`
241
+
242
+ Usually a `layout` is defined directly in the view, as most of the times a view will be used in a single "context",
243
+ but if needed it can be overridden on the `render_view` with the `layout:` option, like so:
244
+
245
+ ```ruby
246
+ # it accepts a template
247
+ render_view MyView.new, layout: 'layouts/admin'
248
+
249
+ # a Viu::Layout
250
+ render_view MyView.new, layout: Layouts::OtherLayout
251
+
252
+ # or a proc
253
+ render_view MyView.new, layout: proc { Layouts::OtherLayout.new(title: 'Dashboard') }
254
+ ```
255
+
256
+ ## Viu::Json
257
+
258
+ This is a simple module that can be included in your views, it will add a `to_json` method, this is called by default
259
+ when rendering in a Rails env. The value returned from `json_output` will be the output of the view.
260
+
261
+ `app/views/api/posts/resource_view.rb`:
262
+ ```ruby
263
+ module Api
264
+ module Posts
265
+ class ResourceView
266
+ include Viu::Json
267
+
268
+ def initialize(post:)
269
+ @post = post
270
+ end
271
+
272
+ private
273
+
274
+ def author
275
+ @author ||= @post.author
276
+ end
277
+
278
+ def json_output
279
+ {
280
+ title: @post.title,
281
+ published_on: @post.published_on.to_s(:iso8601),
282
+ author: {
283
+ name: author.name,
284
+ avatar: author.avatar.url
285
+ }
286
+ }
287
+ end
288
+ end
289
+ end
290
+ end
291
+ ```
292
+
293
+ `app/controllers/api/posts_controller.rb`:
294
+ ```ruby
295
+ def show
296
+ post = Post.find(params[:id])
297
+ render json: Api::Posts::ResourceView.new(post: post)
298
+ end
299
+ ```
300
+
301
+ The default `JSON` encoder can be overridden like so:
302
+
303
+ ```ruby
304
+ class MyJsonView
305
+ include Viu::Json
306
+
307
+ json_encoder ->(input) { Oj.dump(input) }
308
+
309
+ end
310
+ ```
311
+
312
+ ## Viu::Xml
313
+
314
+ Similar to `Viu::Json` this is a simple module that can be included in your views, it will add a `to_xml` method,
315
+ this is called by default when rendering in a Rails env. The value returned from `xml_output` will be the output
316
+ of the view.
317
+
318
+ `app/views/api/posts/resource_view.rb`:
319
+ ```ruby
320
+ module Api
321
+ module Posts
322
+ class ResourceView
323
+ include Viu::Xml
324
+
325
+ def initialize(post:)
326
+ @post = post
327
+ end
328
+
329
+ private
330
+
331
+ def author
332
+ @author ||= @post.author
333
+ end
334
+
335
+ def xml_output
336
+ {
337
+ title: @post.title,
338
+ published_on: @post.published_on.to_s(:iso8601),
339
+ author: {
340
+ name: author.name,
341
+ avatar: author.avatar.url
342
+ }
343
+ }
344
+ end
345
+ end
346
+ end
347
+ end
348
+ ```
349
+
350
+ `app/controllers/api/posts_controller.rb`:
351
+ ```ruby
352
+ def show
353
+ post = Post.find(params[:id])
354
+ render xml Api::Posts::ResourceView.new(post: post)
355
+ end
356
+ ```
357
+
358
+ The default `XML` encoder can be overridden like so:
359
+
360
+ ```ruby
361
+ class MyXmlView
362
+ include Viu::Xml
363
+
364
+ xml_encoder ->(input) { Ox.dump(input) }
365
+
366
+ end
367
+ ```
368
+
369
+ ## Known Issues
370
+
371
+ * A `Viu::Layout` doesn't work with `content_for` blocks, it's only available on a regular layout template for now;
372
+ * Templates and partials require the "full" path, eg: `layouts/application` or `posts/index`;
373
+ * Currently layout inheritance isn't working correctly.
374
+
375
+ ## About
376
+
377
+ Inspired by `view_component`, `cells` and others, currently this is a POC (proof of concept) to create a View layer
378
+ for Rails, it uses `ActionView` as the base for HTML views and it aims to work with the least amount of surprises on a
379
+ Rails application, but with a few boundaries, like a View won't be able to automatically access `@ivars` defined
380
+ in a controller, those have to explicitly be passed to them.
381
+
382
+ The project is already been tested on a small scale in our production environment.
data/lib/viu.rb CHANGED
@@ -4,6 +4,8 @@ require "viu/version"
4
4
 
5
5
  require "viu/html"
6
6
  require "viu/layout"
7
+ require "viu/json"
8
+ require "viu/xml"
7
9
  require "viu/test_case"
8
10
 
9
11
  require 'viu/railtie' if defined?(Rails)
@@ -10,6 +10,8 @@ module Viu
10
10
  def render_in(view_context, options = EMPTY_HASH)
11
11
  __setup!(view_context)
12
12
 
13
+ return __render_template_proc if __fetch_template!.is_a?(Proc)
14
+
13
15
  layout = options.fetch(:layout, self.class.layout)
14
16
 
15
17
  if layout.is_a?(Class)
@@ -48,6 +50,10 @@ module Viu
48
50
  self.class.template!(self.class.name.underscore)
49
51
  end
50
52
 
53
+ def __render_template_proc
54
+ self.instance_exec(&__fetch_template!)
55
+ end
56
+
51
57
  def __render_layout_class(layout)
52
58
  layout.new.render_in(@view_context) do
53
59
  @view_renderer.render(self, template: __fetch_template!, layout: nil)
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+ require 'active_support/core_ext/class/attribute'
5
+
6
+ module Viu
7
+ module Json
8
+ extend ActiveSupport::Concern
9
+
10
+ DEFAULT_JSON_ENCODER = ->(input) { input.to_json }.freeze
11
+
12
+ included do
13
+ class_attribute :__json_encoder, instance_accessor: false, instance_predicate: false,
14
+ default: DEFAULT_JSON_ENCODER
15
+
16
+ attr_reader :rendering_options
17
+
18
+ def to_json(options = EMPTY_HASH)
19
+ @rendering_options = options
20
+ self.class.__json_encoder.call(json_output)
21
+ end
22
+ end
23
+
24
+ class_methods do
25
+
26
+ def json_encoder(value)
27
+ self.__json_encoder = value
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Viu
4
- VERSION = "0.0.2"
4
+ VERSION = "0.0.3"
5
5
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+ require 'active_support/core_ext/class/attribute'
5
+
6
+ module Viu
7
+ module Xml
8
+ extend ActiveSupport::Concern
9
+
10
+ DEFAULT_XML_ENCODER = ->(input) { input.to_xml }.freeze
11
+
12
+ included do
13
+ class_attribute :__xml_encoder, instance_accessor: false, instance_predicate: false,
14
+ default: DEFAULT_XML_ENCODER
15
+
16
+ attr_reader :rendering_options
17
+
18
+ def to_xml(options = EMPTY_HASH)
19
+ @rendering_options = options
20
+ self.class.__xml_encoder.call(xml_output)
21
+ end
22
+ end
23
+
24
+ class_methods do
25
+
26
+ def xml_encoder(value)
27
+ self.__xml_encoder = value
28
+ end
29
+ end
30
+ end
31
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: viu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ralf Schmitz Bongiolo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-16 00:00:00.000000000 Z
11
+ date: 2020-09-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -114,12 +114,14 @@ files:
114
114
  - bin/setup
115
115
  - lib/viu.rb
116
116
  - lib/viu/html.rb
117
+ - lib/viu/json.rb
117
118
  - lib/viu/layout.rb
118
119
  - lib/viu/railtie.rb
119
120
  - lib/viu/rendering_helpers.rb
120
121
  - lib/viu/test_case.rb
121
122
  - lib/viu/test_helpers.rb
122
123
  - lib/viu/version.rb
124
+ - lib/viu/xml.rb
123
125
  - viu.gemspec
124
126
  homepage: https://github.com/mrbongiolo/viu
125
127
  licenses: