cells 3.8.8 → 3.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -1
- data/.travis.yml +5 -2
- data/CHANGES.textile +23 -15
- data/Gemfile +1 -1
- data/README.md +412 -0
- data/Rakefile +2 -2
- data/cells.gemspec +5 -6
- data/gemfiles/Gemfile.rails3-0 +2 -2
- data/gemfiles/Gemfile.rails3-1 +1 -1
- data/gemfiles/Gemfile.rails3-2 +1 -2
- data/gemfiles/Gemfile.rails4-0 +7 -0
- data/lib/cell.rb +27 -0
- data/lib/cell/base.rb +31 -18
- data/lib/cell/builder.rb +11 -10
- data/lib/cell/dsl.rb +7 -0
- data/lib/cell/rack.rb +5 -9
- data/lib/cell/rails.rb +19 -11
- data/lib/cell/rails/view_model.rb +115 -0
- data/lib/cell/rails3_0_strategy.rb +1 -1
- data/lib/cell/rails3_1_strategy.rb +1 -1
- data/lib/cell/rails4_0_strategy.rb +1 -2
- data/lib/cell/test_case.rb +11 -11
- data/lib/cells.rb +4 -3
- data/lib/cells/rails.rb +16 -3
- data/lib/cells/version.rb +1 -1
- data/test/app/cells/bassist_cell.rb +9 -1
- data/test/app/cells/rails_helper_api_test/bassist/edit.html.erb +3 -3
- data/test/app/cells/song/dashboard.haml +7 -0
- data/test/app/cells/song/details.html.haml +1 -0
- data/test/app/cells/song/info.html.haml +1 -0
- data/test/app/cells/song/lyrics.html.haml +6 -0
- data/test/app/cells/song/plays.haml +1 -0
- data/test/app/cells/song/show.html.haml +3 -0
- data/test/app/cells/song/title.html.haml +1 -0
- data/test/app/cells/view_model_test/comments/show.haml +7 -0
- data/test/cell_module_test.rb +39 -41
- data/test/cell_test.rb +28 -0
- data/test/dummy/app/views/musician/featured_with_block.html.erb +1 -1
- data/test/dummy/app/views/musician/title.erb +1 -0
- data/test/dummy/config/routes.rb +1 -0
- data/test/helper_test.rb +13 -10
- data/test/rails/caching_test.rb +75 -73
- data/test/rails/cells_test.rb +25 -23
- data/test/rails/integration_test.rb +80 -61
- data/test/rails/view_model_test.rb +119 -0
- data/test/rails_helper_api_test.rb +11 -13
- metadata +41 -61
- data/README.rdoc +0 -279
- data/about.yml +0 -7
- data/test/app/cells/producer/capture.html.erb +0 -1
- data/test/app/cells/producer/content_for.html.erb +0 -2
- data/test/rails/capture_test.rb +0 -70
data/Rakefile
CHANGED
@@ -3,11 +3,11 @@ Bundler::GemHelper.install_tasks
|
|
3
3
|
|
4
4
|
require 'rake/testtask'
|
5
5
|
|
6
|
-
desc '
|
6
|
+
desc 'Default: run unit tests.'
|
7
7
|
task :default => :test
|
8
8
|
|
9
9
|
Rake::TestTask.new(:test) do |test|
|
10
10
|
test.libs << 'test'
|
11
|
-
test.test_files = FileList['test/*_test.rb', 'test/rails/*_test.rb']
|
11
|
+
test.test_files = FileList['test/*_test.rb', 'test/rails/*_test.rb']
|
12
12
|
test.verbose = true
|
13
13
|
end
|
data/cells.gemspec
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
1
|
lib = File.expand_path('../lib/', __FILE__)
|
3
2
|
$:.unshift lib unless $:.include?(lib)
|
4
3
|
|
@@ -13,19 +12,19 @@ Gem::Specification.new do |s|
|
|
13
12
|
s.homepage = "http://cells.rubyforge.org"
|
14
13
|
s.summary = %q{View Components for Rails.}
|
15
14
|
s.description = %q{Cells are view components for Rails. They are lightweight controllers, can be rendered in views and thus provide an elegant and fast way for encapsulation and component-orientation.}
|
16
|
-
|
15
|
+
s.license = 'MIT'
|
16
|
+
|
17
17
|
s.files = `git ls-files`.split("\n")
|
18
18
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
19
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
20
|
s.require_paths = ["lib"]
|
21
|
-
|
21
|
+
|
22
22
|
s.add_dependency "actionpack", ">= 3.0"
|
23
23
|
s.add_dependency "railties", ">= 3.0"
|
24
|
-
|
24
|
+
|
25
25
|
s.add_development_dependency "rake"
|
26
26
|
s.add_development_dependency "haml"
|
27
27
|
s.add_development_dependency "slim"
|
28
|
-
s.add_development_dependency "simple_form"
|
29
28
|
s.add_development_dependency "tzinfo" # FIXME: why the hell do we need this for 3.1?
|
30
|
-
s.add_development_dependency "minitest", "
|
29
|
+
s.add_development_dependency "minitest", "~> 4.7.5"
|
31
30
|
end
|
data/gemfiles/Gemfile.rails3-0
CHANGED
data/gemfiles/Gemfile.rails3-1
CHANGED
data/gemfiles/Gemfile.rails3-2
CHANGED
data/lib/cell.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Cell
|
2
|
+
module OptionsConstructor
|
3
|
+
private
|
4
|
+
def process_args(options={})
|
5
|
+
if options.is_a?(Hash) # TODO: i don't like this too much.
|
6
|
+
process_options(options)
|
7
|
+
else
|
8
|
+
process_model(options)
|
9
|
+
end
|
10
|
+
|
11
|
+
super # Base.
|
12
|
+
end
|
13
|
+
|
14
|
+
# DISCUSS: have 2 classes for that?
|
15
|
+
|
16
|
+
def process_options(options)
|
17
|
+
options.each do |k, v|
|
18
|
+
instance_variable_set("@#{k}", v)
|
19
|
+
singleton_class.class_eval { attr_reader k }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def process_model(model)
|
24
|
+
@model = model
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/cell/base.rb
CHANGED
@@ -2,40 +2,53 @@ require 'abstract_controller'
|
|
2
2
|
require 'cell/builder'
|
3
3
|
require 'cell/caching'
|
4
4
|
require 'cell/rendering'
|
5
|
+
require 'cell/dsl'
|
5
6
|
|
6
7
|
module Cell
|
7
8
|
def self.rails3_0?
|
8
9
|
::ActionPack::VERSION::MAJOR == 3 and ::ActionPack::VERSION::MINOR == 0
|
9
10
|
end
|
10
|
-
|
11
|
+
|
11
12
|
def self.rails3_1_or_more?
|
12
|
-
::ActionPack::VERSION::MAJOR == 3 and ::ActionPack::VERSION::MINOR >= 1
|
13
|
+
(::ActionPack::VERSION::MAJOR == 3 and ::ActionPack::VERSION::MINOR >= 1) or ::ActionPack::VERSION::MAJOR > 3
|
13
14
|
end
|
14
|
-
|
15
|
-
def self.rails3_2_or_more?
|
16
|
-
::ActionPack::VERSION::MAJOR == 3 and ::ActionPack::VERSION::MINOR >= 2
|
15
|
+
|
16
|
+
def self.rails3_2_or_more?
|
17
|
+
(::ActionPack::VERSION::MAJOR == 3 and ::ActionPack::VERSION::MINOR >= 2) or ::ActionPack::VERSION::MAJOR > 3
|
17
18
|
end
|
18
19
|
|
19
|
-
def self.rails4_0_or_more?
|
20
|
-
::ActionPack::VERSION::MAJOR == 4
|
20
|
+
def self.rails4_0_or_more?
|
21
|
+
(::ActionPack::VERSION::MAJOR == 4 and ::ActionPack::VERSION::MINOR >= 0) or ::ActionPack::VERSION::MAJOR > 4
|
21
22
|
end
|
22
|
-
|
23
|
-
|
23
|
+
|
24
|
+
|
24
25
|
class Base < AbstractController::Base
|
26
|
+
# TODO: deprecate Base in favour of Cell.
|
27
|
+
|
25
28
|
abstract!
|
26
29
|
DEFAULT_VIEW_PATHS = [File.join('app', 'cells')]
|
27
|
-
|
30
|
+
|
28
31
|
extend Builder
|
29
32
|
include AbstractController
|
30
33
|
include AbstractController::Rendering, Layouts, Helpers, Callbacks, Translation, Logger
|
31
|
-
|
34
|
+
|
32
35
|
require 'cell/rails3_0_strategy' if Cell.rails3_0?
|
33
36
|
require 'cell/rails3_1_strategy' if Cell.rails3_1_or_more?
|
34
37
|
require 'cell/rails4_0_strategy' if Cell.rails4_0_or_more?
|
35
38
|
include VersionStrategy
|
36
39
|
include Rendering
|
37
40
|
include Caching
|
38
|
-
|
41
|
+
include Cell::DSL
|
42
|
+
|
43
|
+
def initialize(*args)
|
44
|
+
super() # AbC::Base.
|
45
|
+
process_args(*args)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def process_args(*)
|
50
|
+
end
|
51
|
+
|
39
52
|
class View < ActionView::Base
|
40
53
|
def self.prepare(modules)
|
41
54
|
# TODO: remove for 4.0 if PR https://github.com/rails/rails/pull/6826 is merged.
|
@@ -43,27 +56,27 @@ module Cell
|
|
43
56
|
include *modules.reverse
|
44
57
|
end
|
45
58
|
end
|
46
|
-
|
59
|
+
|
47
60
|
def render(*args, &block)
|
48
61
|
options = args.first.is_a?(::Hash) ? args.first : {} # this is copied from #render by intention.
|
49
|
-
|
62
|
+
|
50
63
|
return controller.render(*args, &block) if options[:state] or options[:view]
|
51
64
|
super
|
52
65
|
end
|
53
66
|
end
|
54
|
-
|
55
|
-
|
67
|
+
|
68
|
+
|
56
69
|
def self.view_context_class
|
57
70
|
@view_context_class ||= begin
|
58
71
|
Cell::Base::View.prepare(helper_modules)
|
59
72
|
end
|
60
73
|
end
|
61
|
-
|
74
|
+
|
62
75
|
# Called in Railtie at initialization time.
|
63
76
|
def self.setup_view_paths!
|
64
77
|
self.view_paths = self::DEFAULT_VIEW_PATHS
|
65
78
|
end
|
66
|
-
|
79
|
+
|
67
80
|
def self.controller_path
|
68
81
|
@controller_path ||= name.sub(/Cell$/, '').underscore unless anonymous?
|
69
82
|
end
|
data/lib/cell/builder.rb
CHANGED
@@ -5,19 +5,20 @@ module Cell
|
|
5
5
|
# class with Cell::Base.build - this might lead to a different cell being returned.
|
6
6
|
def create_cell_for(name, *args)
|
7
7
|
class_from_cell_name(name).build_for(*args)
|
8
|
-
end
|
9
|
-
|
8
|
+
end # TODO: rename to #cell_for.
|
9
|
+
alias_method :cell_for, :create_cell_for
|
10
|
+
|
10
11
|
def build_for(*args) # DISCUSS: remove?
|
11
12
|
build_class_for(*args).
|
12
13
|
create_cell(*args)
|
13
14
|
end
|
14
|
-
|
15
|
+
|
15
16
|
# Adds a builder to the cell class. Builders are used in #render_cell to find out the concrete
|
16
17
|
# class for rendering. This is helpful if you frequently want to render subclasses according
|
17
18
|
# to different circumstances (e.g. login situations) and you don't want to place these deciders in
|
18
19
|
# your view code.
|
19
20
|
#
|
20
|
-
# Passes the opts hash from #render_cell into the block. The block is executed in controller context.
|
21
|
+
# Passes the opts hash from #render_cell into the block. The block is executed in controller context.
|
21
22
|
# Multiple build blocks are ORed, if no builder matches the building cell is used.
|
22
23
|
#
|
23
24
|
# Example:
|
@@ -41,17 +42,17 @@ module Cell
|
|
41
42
|
def build(&block)
|
42
43
|
builders << block
|
43
44
|
end
|
44
|
-
|
45
|
+
|
45
46
|
# The cell class constant for +cell_name+.
|
46
47
|
def class_from_cell_name(cell_name)
|
47
48
|
"#{cell_name}_cell".classify.constantize
|
48
49
|
end
|
49
|
-
|
50
|
+
|
50
51
|
# Override this if you want to receive arguments right in the cell constructor.
|
51
52
|
def create_cell(*args)
|
52
|
-
new
|
53
|
+
new(*args)
|
53
54
|
end
|
54
|
-
|
55
|
+
|
55
56
|
private
|
56
57
|
def build_class_for(*args)
|
57
58
|
builders.each do |blk|
|
@@ -59,11 +60,11 @@ module Cell
|
|
59
60
|
end
|
60
61
|
self
|
61
62
|
end
|
62
|
-
|
63
|
+
|
63
64
|
def run_builder_block(block, *args)
|
64
65
|
block.call(*args)
|
65
66
|
end
|
66
|
-
|
67
|
+
|
67
68
|
def builders
|
68
69
|
@builders ||= []
|
69
70
|
end
|
data/lib/cell/dsl.rb
ADDED
data/lib/cell/rack.rb
CHANGED
@@ -5,7 +5,7 @@ module Cell
|
|
5
5
|
# in the cell. This is especially useful when using gems like devise with your cell, without the
|
6
6
|
# entire Cell::Rails overhead.
|
7
7
|
#
|
8
|
-
# The only dependency these kinds of cells have is a
|
8
|
+
# The only dependency these kinds of cells have is a Rack-compatible request object.
|
9
9
|
#
|
10
10
|
# Example:
|
11
11
|
#
|
@@ -16,20 +16,16 @@ module Cell
|
|
16
16
|
class Rack < Base
|
17
17
|
attr_reader :request
|
18
18
|
delegate :session, :params, :to => :request
|
19
|
-
|
19
|
+
|
20
20
|
class << self
|
21
21
|
# DISCUSS: i don't like these class methods. maybe a RenderingStrategy?
|
22
|
-
def create_cell(request, *args) # defined in Builder.
|
23
|
-
new(request)
|
24
|
-
end
|
25
|
-
|
26
22
|
def render_cell_state(cell, state, request, *args) # defined in Rendering.
|
27
23
|
super(cell, state, *args)
|
28
24
|
end
|
29
25
|
end
|
30
|
-
|
31
|
-
def initialize(request)
|
32
|
-
super()
|
26
|
+
|
27
|
+
def initialize(request, *args)
|
28
|
+
super(*args)
|
33
29
|
@request = request
|
34
30
|
end
|
35
31
|
end
|
data/lib/cell/rails.rb
CHANGED
@@ -4,10 +4,10 @@ module Cell
|
|
4
4
|
class Rails < Rack
|
5
5
|
# When this file is included we can savely assume that a rails environment with caching, etc. is available.
|
6
6
|
include ActionController::RequestForgeryProtection
|
7
|
-
|
7
|
+
|
8
8
|
abstract!
|
9
9
|
delegate :session, :params, :request, :config, :env, :url_options, :to => :parent_controller
|
10
|
-
|
10
|
+
|
11
11
|
class << self
|
12
12
|
def cache_store
|
13
13
|
# FIXME: i'd love to have an initializer in the cells gem that _sets_ the cache_store attr instead of overriding here.
|
@@ -15,33 +15,41 @@ module Cell
|
|
15
15
|
# DISCUSS: should this be in Cell::Rails::Caching ?
|
16
16
|
ActionController::Base.cache_store
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
def expire_cache_key(key, *args) # FIXME: move to Rails.
|
20
20
|
expire_cache_key_for(key, cache_store ,*args)
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
private
|
24
24
|
# Run builder block in controller instance context.
|
25
25
|
def run_builder_block(block, controller, *args)
|
26
26
|
controller.instance_exec(*args, &block)
|
27
27
|
end
|
28
28
|
end
|
29
|
-
|
30
|
-
|
29
|
+
|
30
|
+
|
31
31
|
attr_reader :parent_controller
|
32
32
|
alias_method :controller, :parent_controller
|
33
|
-
|
34
|
-
def initialize(parent_controller)
|
35
|
-
super
|
33
|
+
|
34
|
+
def initialize(parent_controller, *args)
|
35
|
+
super(parent_controller, *args) # FIXME: huh?
|
36
36
|
@parent_controller = parent_controller
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
def cache_configured?
|
40
40
|
ActionController::Base.send(:cache_configured?) # DISCUSS: why is it private?
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
def cache_store
|
44
44
|
self.class.cache_store # in Rails, we have a global cache store.
|
45
45
|
end
|
46
|
+
|
47
|
+
module DSL
|
48
|
+
def cell(name, *args)
|
49
|
+
# TODO: this method should be an instance method everywhere.
|
50
|
+
Base.cell_for(name, parent_controller, *args)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
include DSL
|
46
54
|
end
|
47
55
|
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# no helper_method calls
|
2
|
+
# no instance variables
|
3
|
+
# no locals
|
4
|
+
# options are automatically made instance methods via constructor.
|
5
|
+
# call "helpers" in class
|
6
|
+
class Cell::Rails
|
7
|
+
module ViewModel
|
8
|
+
include Cell::OptionsConstructor
|
9
|
+
#include ActionView::Helpers::UrlHelper
|
10
|
+
include ActionView::Context # this includes CompiledTemplates, too.
|
11
|
+
# properties :title, :body
|
12
|
+
attr_reader :model
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def property(name)
|
16
|
+
delegate name, :to => :model
|
17
|
+
end
|
18
|
+
end
|
19
|
+
extend ActiveSupport::Concern
|
20
|
+
|
21
|
+
|
22
|
+
def render(options={})
|
23
|
+
if options.is_a?(Hash)
|
24
|
+
options.reverse_merge!(:view => state_for_implicit_render)
|
25
|
+
else
|
26
|
+
options = {:view => options.to_s}
|
27
|
+
end
|
28
|
+
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
def call
|
33
|
+
render implicit_state
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def view_context
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def state_for_implicit_render()
|
42
|
+
caller[1].match(/`(\w+)/)[1]
|
43
|
+
end
|
44
|
+
|
45
|
+
def implicit_state
|
46
|
+
controller_path.split("/").last
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
# FIXME: this module is to fix a design flaw in Rails 4.0. the problem is that AV::UrlHelper mixes in the wrong #url_for.
|
52
|
+
# if we could mix in everything else from the helper except for the #url_for, it would be fine.
|
53
|
+
module LinkToHelper
|
54
|
+
include ActionView::Helpers::TagHelper
|
55
|
+
|
56
|
+
def link_to(name = nil, options = nil, html_options = nil, &block)
|
57
|
+
html_options, options, name = options, name, block if block_given?
|
58
|
+
options ||= {}
|
59
|
+
|
60
|
+
html_options = convert_options_to_data_attributes(options, html_options)
|
61
|
+
|
62
|
+
url = url_for(options)
|
63
|
+
html_options['href'] ||= url
|
64
|
+
|
65
|
+
content_tag(:a, name || url, html_options, &block)
|
66
|
+
end
|
67
|
+
|
68
|
+
def convert_options_to_data_attributes(options, html_options)
|
69
|
+
if html_options
|
70
|
+
html_options = html_options.stringify_keys
|
71
|
+
html_options['data-remote'] = 'true' if link_to_remote_options?(options) || link_to_remote_options?(html_options)
|
72
|
+
|
73
|
+
disable_with = html_options.delete("disable_with")
|
74
|
+
confirm = html_options.delete('confirm')
|
75
|
+
method = html_options.delete('method')
|
76
|
+
|
77
|
+
if confirm
|
78
|
+
message = ":confirm option is deprecated and will be removed from Rails 4.1. " \
|
79
|
+
"Use 'data: { confirm: \'Text\' }' instead."
|
80
|
+
ActiveSupport::Deprecation.warn message
|
81
|
+
|
82
|
+
html_options["data-confirm"] = confirm
|
83
|
+
end
|
84
|
+
|
85
|
+
add_method_to_attributes!(html_options, method) if method
|
86
|
+
|
87
|
+
if disable_with
|
88
|
+
message = ":disable_with option is deprecated and will be removed from Rails 4.1. " \
|
89
|
+
"Use 'data: { disable_with: \'Text\' }' instead."
|
90
|
+
ActiveSupport::Deprecation.warn message
|
91
|
+
|
92
|
+
html_options["data-disable-with"] = disable_with
|
93
|
+
end
|
94
|
+
|
95
|
+
html_options
|
96
|
+
else
|
97
|
+
link_to_remote_options?(options) ? {'data-remote' => 'true'} : {}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def link_to_remote_options?(options)
|
102
|
+
if options.is_a?(Hash)
|
103
|
+
options.delete('remote') || options.delete(:remote)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# FIXME: fix that in rails core.
|
109
|
+
if Cell.rails4_0_or_more?
|
110
|
+
include LinkToHelper
|
111
|
+
else
|
112
|
+
include ActionView::Helpers::UrlHelper
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|