cells 3.8.8 → 3.9.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.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -1
  3. data/.travis.yml +5 -2
  4. data/CHANGES.textile +23 -15
  5. data/Gemfile +1 -1
  6. data/README.md +412 -0
  7. data/Rakefile +2 -2
  8. data/cells.gemspec +5 -6
  9. data/gemfiles/Gemfile.rails3-0 +2 -2
  10. data/gemfiles/Gemfile.rails3-1 +1 -1
  11. data/gemfiles/Gemfile.rails3-2 +1 -2
  12. data/gemfiles/Gemfile.rails4-0 +7 -0
  13. data/lib/cell.rb +27 -0
  14. data/lib/cell/base.rb +31 -18
  15. data/lib/cell/builder.rb +11 -10
  16. data/lib/cell/dsl.rb +7 -0
  17. data/lib/cell/rack.rb +5 -9
  18. data/lib/cell/rails.rb +19 -11
  19. data/lib/cell/rails/view_model.rb +115 -0
  20. data/lib/cell/rails3_0_strategy.rb +1 -1
  21. data/lib/cell/rails3_1_strategy.rb +1 -1
  22. data/lib/cell/rails4_0_strategy.rb +1 -2
  23. data/lib/cell/test_case.rb +11 -11
  24. data/lib/cells.rb +4 -3
  25. data/lib/cells/rails.rb +16 -3
  26. data/lib/cells/version.rb +1 -1
  27. data/test/app/cells/bassist_cell.rb +9 -1
  28. data/test/app/cells/rails_helper_api_test/bassist/edit.html.erb +3 -3
  29. data/test/app/cells/song/dashboard.haml +7 -0
  30. data/test/app/cells/song/details.html.haml +1 -0
  31. data/test/app/cells/song/info.html.haml +1 -0
  32. data/test/app/cells/song/lyrics.html.haml +6 -0
  33. data/test/app/cells/song/plays.haml +1 -0
  34. data/test/app/cells/song/show.html.haml +3 -0
  35. data/test/app/cells/song/title.html.haml +1 -0
  36. data/test/app/cells/view_model_test/comments/show.haml +7 -0
  37. data/test/cell_module_test.rb +39 -41
  38. data/test/cell_test.rb +28 -0
  39. data/test/dummy/app/views/musician/featured_with_block.html.erb +1 -1
  40. data/test/dummy/app/views/musician/title.erb +1 -0
  41. data/test/dummy/config/routes.rb +1 -0
  42. data/test/helper_test.rb +13 -10
  43. data/test/rails/caching_test.rb +75 -73
  44. data/test/rails/cells_test.rb +25 -23
  45. data/test/rails/integration_test.rb +80 -61
  46. data/test/rails/view_model_test.rb +119 -0
  47. data/test/rails_helper_api_test.rb +11 -13
  48. metadata +41 -61
  49. data/README.rdoc +0 -279
  50. data/about.yml +0 -7
  51. data/test/app/cells/producer/capture.html.erb +0 -1
  52. data/test/app/cells/producer/content_for.html.erb +0 -2
  53. 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 'Test the cells gem.'
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'] - ['test/rails/capture_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", ">= 2.8.1"
29
+ s.add_development_dependency "minitest", "~> 4.7.5"
31
30
  end
@@ -1,6 +1,6 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- # Specify your gem's dependencies in roar-rails.gemspec
3
+ # Specify your gem's dependencies in cells.gemspec
4
4
  gemspec path: '../'
5
5
 
6
- gem 'railties', '~> 3.0.11'
6
+ gem 'railties', '3.0.20'
@@ -1,6 +1,6 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- # Specify your gem's dependencies in roar-rails.gemspec
3
+ # Specify your gem's dependencies in cells.gemspec
4
4
  gemspec path: '../'
5
5
 
6
6
  gem 'railties', '~> 3.1.0'
@@ -1,7 +1,6 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- # Specify your gem's dependencies in roar-rails.gemspec
3
+ # Specify your gem's dependencies in cells.gemspec
4
4
  gemspec path: '../'
5
5
 
6
6
  gem 'railties', '~> 3.2.0'
7
- gem 'haml', '~> 3.1.4'
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in cells.gemspec
4
+ gemspec path: '../'
5
+
6
+ gem 'railties', '4.0.0'
7
+ gem 'activemodel'
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? # FIXME: move to tests.
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? # FIXME: move to tests.
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
@@ -0,0 +1,7 @@
1
+ module Cell
2
+ module DSL
3
+ def cell(*args)
4
+ Base.cell_for(*args)
5
+ end
6
+ end
7
+ end
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 rack-compatible request object.
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