cells 4.0.0.rc1 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -29,6 +29,7 @@ require "cell/abstract"
29
29
  require "cell/util"
30
30
  require "cell/view_model"
31
31
  require "cell/concept"
32
+ require "cell/escaped"
32
33
 
33
34
 
34
35
  require "cell/railtie" if defined?(Rails)
@@ -1,19 +1,26 @@
1
- class Cell::Concept < Cell::ViewModel
2
- abstract!
3
- self.view_paths = ["app/concepts"]
1
+ module Cell
2
+ class Concept < Cell::ViewModel
3
+ abstract!
4
+ self.view_paths = ["app/concepts"]
4
5
 
5
- # TODO: this should be in Helper or something. this should be the only entry point from controller/view.
6
- class << self
7
- def class_from_cell_name(name)
8
- name.classify.constantize
9
- end
6
+ # TODO: this should be in Helper or something. this should be the only entry point from controller/view.
7
+ class << self
8
+ def class_from_cell_name(name)
9
+ name.classify.constantize
10
+ end
10
11
 
11
- def controller_path
12
- @controller_path ||= util.underscore(name.sub(/::Cell/, ''))
12
+ def controller_path
13
+ @controller_path ||= util.underscore(name.sub(/::Cell/, ''))
14
+ end
13
15
  end
14
- end
15
16
 
16
- alias_method :concept, :cell # the #cell helper to instantiate cells in cells.
17
+ alias_method :concept, :cell # Concept#concept does exactly what #cell does: delegate to class builder.
17
18
 
18
- self_contained!
19
+ # Get nested cell in instance.
20
+ def cell(name, model=nil, options={})
21
+ ViewModel.cell(name, model, options.merge(controller: parent_controller)) # #cell calls need to be delegated to ViewModel.
22
+ end
23
+
24
+ self_contained!
25
+ end
19
26
  end
@@ -0,0 +1,27 @@
1
+ module Cell::ViewModel::Escaped
2
+ def self.included(includer)
3
+ includer.extend Property
4
+ end
5
+
6
+ module Property
7
+ def property(name, *args)
8
+ super.tap do # super defines #title
9
+ mod = Module.new do
10
+ define_method(name) do |options={}|
11
+ value = super() # call the original #title.
12
+ return value unless value.is_a?(String)
13
+ return value if options[:escape] == false
14
+ escape!(value)
15
+ end
16
+ end
17
+ include mod
18
+ end
19
+ end
20
+ end # Property
21
+
22
+ # Can be used as a helper in the cell, too.
23
+ # Feel free to override and use a different escaping implementation.
24
+ def escape!(string)
25
+ ::ERB::Util.html_escape(string)
26
+ end
27
+ end
@@ -41,6 +41,10 @@ module Cell
41
41
  delegates :parent_controller, :session, :params, :request, :config, :env, :url_options
42
42
  end
43
43
 
44
+ def protect_against_forgery? # TODO: implement forgery protection with ActionController.
45
+ false
46
+ end
47
+
44
48
  def call(*)
45
49
  super.html_safe
46
50
  end
@@ -50,9 +50,9 @@ module Cell
50
50
  # TODO: allow to turn off this.
51
51
  initializer "cells.include_template_module", after: "cells.include_default_helpers" do
52
52
  # yepp, this is happening. saves me a lot of coding in each extension.
53
- ViewModel.send(:include, Cell::Erb) if Cell.const_defined?(:Erb)
54
- ViewModel.send(:include, Cell::Haml) if Cell.const_defined?(:Haml)
55
- ViewModel.send(:include, Cell::Slim) if Cell.const_defined?(:Slim)
53
+ ViewModel.send(:include, Cell::Erb) if Cell.const_defined?(:Erb, false)
54
+ ViewModel.send(:include, Cell::Haml) if Cell.const_defined?(:Haml, false)
55
+ ViewModel.send(:include, Cell::Slim) if Cell.const_defined?(:Slim, false)
56
56
  end
57
57
  # ViewModel.template_engine = app.config.app_generators.rails.fetch(:template_engine, 'erb').to_s
58
58
 
@@ -1,3 +1,3 @@
1
1
  module Cell
2
- VERSION = "4.0.0.rc1"
2
+ VERSION = "4.0.0"
3
3
  end
@@ -63,12 +63,19 @@ class ConceptTest < MiniTest::Spec
63
63
  it { Cell::Concept.cell("record/cell/song").show.must_equal "Lalala" }
64
64
  end
65
65
 
66
- describe "#cell (in cell state)" do
66
+
67
+ class RecordCell < Cell::ViewModel
68
+ def description
69
+ "Record! A Tribute To Rancid, with #{@options[:tracks]} songs! [#{parent_controller}]"
70
+ end
71
+ end
72
+
73
+ describe "#cell (in state)" do
67
74
  # test with controller, but remove tests when we don't need it anymore.
68
- it { Cell::Concept.cell("record/cell", nil, controller: Object).cell("record/cell", nil, tracks: 24).(:description).must_equal "A Tribute To Rancid, with 24 songs! [Object]" }
75
+ it { Cell::Concept.cell("record/cell", nil, controller: Object).cell("concept_test/record", nil, tracks: 24).(:description).must_equal "Record! A Tribute To Rancid, with 24 songs! [Object]" }
69
76
  it { Cell::Concept.cell("record/cell", nil, controller: Object).concept("record/cell", nil, tracks: 24).(:description).must_equal "A Tribute To Rancid, with 24 songs! [Object]" }
70
77
  # concept(.., collection: ..)
71
- it("xx") { Cell::Concept.cell("record/cell", nil, controller: Object).
78
+ it { Cell::Concept.cell("record/cell", nil, controller: Object).
72
79
  concept("record/cell", collection: [1,2], tracks: 24, method: :description).must_equal "A Tribute To Rancid, with 24 songs! [Object]A Tribute To Rancid, with 24 songs! [Object]" }
73
80
  end
74
81
  end
@@ -0,0 +1,41 @@
1
+ require "test_helper"
2
+
3
+ class PropertyTest < MiniTest::Spec
4
+ class SongCell < Cell::ViewModel
5
+ property :title
6
+
7
+ def title
8
+ super + "</b>"
9
+ end
10
+ end
11
+
12
+ let (:song) { Struct.new(:title).new("<b>She Sells And Sand Sandwiches") }
13
+ # ::property creates automatic accessor.
14
+ it { SongCell.(song).title.must_equal "<b>She Sells And Sand Sandwiches</b>" }
15
+ end
16
+
17
+
18
+ class EscapedPropertyTest < MiniTest::Spec
19
+ class SongCell < Cell::ViewModel
20
+ include Escaped
21
+ property :title
22
+ property :artist
23
+
24
+ def title(*)
25
+ "#{super}</b>" # super + "</b>" still escapes, but this is Rails.
26
+ end
27
+
28
+ def raw_title
29
+ title(escape: false)
30
+ end
31
+ end
32
+
33
+ let (:song) { Struct.new(:title, :artist).new("<b>She Sells And Sand Sandwiches", Object) }
34
+
35
+ # ::property escapes, everywhere.
36
+ it { SongCell.(song).title.must_equal "&lt;b&gt;She Sells And Sand Sandwiches</b>" }
37
+ # no escaping for non-strings.
38
+ it { SongCell.(song).artist.must_equal Object }
39
+ # no escaping when escape: false
40
+ it { SongCell.(song).raw_title.must_equal "<b>She Sells And Sand Sandwiches</b>" }
41
+ end
@@ -2,10 +2,6 @@ class FormForCell < Cell::ViewModel
2
2
  include ActionView::RecordIdentifier
3
3
  include ActionView::Helpers::FormHelper
4
4
 
5
- def protect_against_forgery?
6
- false
7
- end
8
-
9
5
  def show
10
6
  render
11
7
  end
@@ -3,10 +3,6 @@ class FormtasticCell < Cell::ViewModel
3
3
  include ActionView::Helpers::FormHelper
4
4
  include Formtastic::Helpers::FormHelper
5
5
 
6
- def protect_against_forgery?
7
- false
8
- end
9
-
10
6
  def show
11
7
  render
12
8
  end
@@ -6,10 +6,6 @@ class SimpleFormCell < Cell::ViewModel
6
6
  # include ActiveSupport::Configurable
7
7
  # include ActionController::RequestForgeryProtection # FIXME: this does NOT activate any protection.
8
8
 
9
- def protect_against_forgery?
10
- false
11
- end
12
-
13
9
  def show
14
10
  render
15
11
  end
@@ -0,0 +1 @@
1
+ <b><%= title %></b>
@@ -1,8 +1,14 @@
1
1
  class SongCell < Cell::ViewModel
2
+ include Escaped
3
+ property :title
4
+
2
5
  def show
3
6
  "happy"
4
7
  end
5
8
 
9
+ def with_escaped
10
+ render
11
+ end
6
12
  # include ActionView::Helpers::AssetUrlHelper
7
13
  # include Sprockets::Rails::Helper
8
14
 
@@ -14,4 +14,12 @@ class SongsController < ApplicationController
14
14
  def edit
15
15
  render text: cell(:song).video_path(1)
16
16
  end
17
+
18
+ def with_image_tag
19
+ render text: cell(:song).image_tag("logo.png")
20
+ end
21
+
22
+ def with_escaped
23
+ render layout: false
24
+ end
17
25
  end
@@ -1,4 +1,4 @@
1
- class Song
1
+ class Song < OpenStruct
2
2
  require "active_model"
3
3
  include ActiveModel::Conversion
4
4
  extend ActiveModel::Naming
@@ -0,0 +1 @@
1
+ <h1>Yeah!</h1><%= cell(:song, Song.new(title: "<script>")).(:with_escaped) %>
@@ -3,4 +3,7 @@ Rails.application.routes.draw do
3
3
  root to: "index#index"
4
4
 
5
5
  resources :songs
6
+
7
+ get "songs/with_image_tag"
8
+ get "songs/with_escaped"
6
9
  end
@@ -3,10 +3,14 @@ require "test_helper"
3
3
  class ControllerTest < ActionController::TestCase
4
4
  tests SongsController
5
5
 
6
- # TODO: test url stuff in Song#show.
7
6
  it do
8
7
  get :index
9
8
  response.body.must_equal "happy"
10
9
  end
11
10
 
11
+ # HTML escaping.
12
+ it do
13
+ get :with_escaped
14
+ response.body.must_equal "<h1>Yeah!</h1><b>&lt;script&gt;</b>" # only the property is escaped.
15
+ end
12
16
  end
@@ -33,4 +33,15 @@ class UrlTest < ActionDispatch::IntegrationTest
33
33
  # visit "/songs/1/edit"
34
34
  # page.text.must_equal "http://www.example.com/songs/1"
35
35
  # end
36
+
37
+
38
+ end
39
+
40
+ class AssetsHelperTest < ActionController::TestCase
41
+ tests SongsController
42
+
43
+ it do
44
+ get :with_image_tag
45
+ response.body.must_equal "<img src=\"/images/logo.png\" alt=\"Logo\" />"
46
+ end
36
47
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cells
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0.rc1
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-05 00:00:00.000000000 Z
11
+ date: 2015-06-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: uber
@@ -126,7 +126,7 @@ files:
126
126
  - lib/cell/caching/notification.rb
127
127
  - lib/cell/concept.rb
128
128
  - lib/cell/development.rb
129
- - lib/cell/engines.rb
129
+ - lib/cell/escaped.rb
130
130
  - lib/cell/layout.rb
131
131
  - lib/cell/partial.rb
132
132
  - lib/cell/prefixes.rb
@@ -210,6 +210,7 @@ files:
210
210
  - test/layout_test.rb
211
211
  - test/partial_test.rb
212
212
  - test/prefixes_test.rb
213
+ - test/property_test.rb
213
214
  - test/public_test.rb
214
215
  - test/rails4.2/.gitignore
215
216
  - test/rails4.2/Gemfile
@@ -224,6 +225,7 @@ files:
224
225
  - test/rails4.2/app/cells/simple_form/show.erb
225
226
  - test/rails4.2/app/cells/simple_form_cell.rb
226
227
  - test/rails4.2/app/cells/song/song.css
228
+ - test/rails4.2/app/cells/song/with_escaped.erb
227
229
  - test/rails4.2/app/cells/song_cell.rb
228
230
  - test/rails4.2/app/controllers/application_controller.rb
229
231
  - test/rails4.2/app/controllers/concerns/.keep
@@ -238,6 +240,7 @@ files:
238
240
  - test/rails4.2/app/views/index/index.html.erb
239
241
  - test/rails4.2/app/views/layouts/application.html.erb
240
242
  - test/rails4.2/app/views/songs/show.html.erb
243
+ - test/rails4.2/app/views/songs/with_escaped.html.erb
241
244
  - test/rails4.2/bin/bundle
242
245
  - test/rails4.2/bin/rails
243
246
  - test/rails4.2/bin/rake
@@ -289,11 +292,6 @@ files:
289
292
  - test/rails4.2/lib/assets/.keep
290
293
  - test/rails4.2/lib/tasks/.keep
291
294
  - test/rails4.2/log/.keep
292
- - test/rails4.2/public/404.html
293
- - test/rails4.2/public/422.html
294
- - test/rails4.2/public/500.html
295
- - test/rails4.2/public/favicon.ico
296
- - test/rails4.2/public/robots.txt
297
295
  - test/rails4.2/test/integration/asset_pipeline_test.rb
298
296
  - test/rails4.2/test/integration/controller_test.rb
299
297
  - test/rails4.2/test/integration/form_for_test.rb
@@ -323,9 +321,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
323
321
  version: '0'
324
322
  required_rubygems_version: !ruby/object:Gem::Requirement
325
323
  requirements:
326
- - - ">"
324
+ - - ">="
327
325
  - !ruby/object:Gem::Version
328
- version: 1.3.1
326
+ version: '0'
329
327
  requirements: []
330
328
  rubyforge_project:
331
329
  rubygems_version: 2.2.2
@@ -1,61 +0,0 @@
1
- module Cell
2
- # Now <tt>Rails::Engine</tt>s can contribute to Cells view paths.
3
- # By default, any 'app/cells' found inside any Engine is automatically included into Cells view paths.
4
- #
5
- # You can customize the view paths changing/appending to the <tt>'app/cell_views'</tt> path configuration:
6
- #
7
- # module MyAwesome
8
- # class Engine < Rails::Engine
9
- # # loads views from 'cell/views' and NOT from 'app/cells'
10
- # config.paths.add 'app/cell_views', :with => 'cell/views'
11
- #
12
- # # appends 'lib/my_cells_view_path' to this Railtie view path contribution
13
- # config.paths['app/cell_views'] << 'lib/my_cells_view_path'
14
- # end
15
- # end
16
- #
17
- # You can manually specify which Engines will be added to Cell view paths
18
- #
19
- # Cell::Base.config.view_path_engines = [MyAwesome::Engine]
20
- #
21
- # And even disable the automatic loading
22
- #
23
- # Cell::Base.config.view_path_engines = false
24
- #
25
- # You can programatically append a Rails::Engine to Cell view path
26
- #
27
- # Cells.setup do |config|
28
- # config.append_engine_view_path!(MyEngine::Engine)
29
- # end
30
- #
31
- module Engines
32
- extend VersionStrategy # adds #registered_engines and #existent_directories_for.
33
-
34
- # Appends all <tt>Rails::Engine</tt>s cell-views path to Cell::Base#view_paths
35
- #
36
- # All <tt>Rails::Engine</tt>s specified at <tt>config.view_path_engines</tt> will have its cell-views path appended to Cell::Base#view_paths
37
- #
38
- # Defaults <tt>config.view_path_engines</tt> to all loaded <tt>Rails::Engine</tt>s.
39
- #
40
- def self.append_engines_view_paths_for(config)
41
- return if config.view_path_engines == false
42
-
43
- engines = config.view_path_engines || registered_engines #::Rails::Application::Railties.engines
44
- engines.each {|engine| append_engine_view_path!(engine) }
45
- end
46
-
47
- # Appends a <tt>Rails::Engine</tt> cell-views path to @Cell::Base@
48
- #
49
- # The <tt>Rails::Engine</tt> cell-views path is obtained from the <tt>paths['app/cell_views']</tt> configuration.
50
- # All existing directories specified at cell-views path will be appended do Cell::Base#view_paths
51
- #
52
- # Defaults <tt>paths['app/cell_views']</tt> to 'app/cells'
53
- #
54
- def self.append_engine_view_path!(engine)
55
- return unless engine.is_a?(::Rails::Engine) # In Rails 4.1, this could be a Rails::Railtie, which doesn't make sense.
56
-
57
- engine.paths['app/cell_views'] || engine.paths.add('app/cell_views', :with => 'app/cells')
58
- Cell::Rails.append_view_path(existent_directories_for(engine.paths["app/cell_views"]))
59
- end
60
- end
61
- end
@@ -1,67 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>The page you were looking for doesn't exist (404)</title>
5
- <meta name="viewport" content="width=device-width,initial-scale=1">
6
- <style>
7
- body {
8
- background-color: #EFEFEF;
9
- color: #2E2F30;
10
- text-align: center;
11
- font-family: arial, sans-serif;
12
- margin: 0;
13
- }
14
-
15
- div.dialog {
16
- width: 95%;
17
- max-width: 33em;
18
- margin: 4em auto 0;
19
- }
20
-
21
- div.dialog > div {
22
- border: 1px solid #CCC;
23
- border-right-color: #999;
24
- border-left-color: #999;
25
- border-bottom-color: #BBB;
26
- border-top: #B00100 solid 4px;
27
- border-top-left-radius: 9px;
28
- border-top-right-radius: 9px;
29
- background-color: white;
30
- padding: 7px 12% 0;
31
- box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
- }
33
-
34
- h1 {
35
- font-size: 100%;
36
- color: #730E15;
37
- line-height: 1.5em;
38
- }
39
-
40
- div.dialog > p {
41
- margin: 0 0 1em;
42
- padding: 1em;
43
- background-color: #F7F7F7;
44
- border: 1px solid #CCC;
45
- border-right-color: #999;
46
- border-left-color: #999;
47
- border-bottom-color: #999;
48
- border-bottom-left-radius: 4px;
49
- border-bottom-right-radius: 4px;
50
- border-top-color: #DADADA;
51
- color: #666;
52
- box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
- }
54
- </style>
55
- </head>
56
-
57
- <body>
58
- <!-- This file lives in public/404.html -->
59
- <div class="dialog">
60
- <div>
61
- <h1>The page you were looking for doesn't exist.</h1>
62
- <p>You may have mistyped the address or the page may have moved.</p>
63
- </div>
64
- <p>If you are the application owner check the logs for more information.</p>
65
- </div>
66
- </body>
67
- </html>