From 444b6c4ad6a12835c2214aefc6f6b186c70260d8 Mon Sep 17 00:00:00 2001 From: shvetsovdm Date: Sat, 14 May 2016 00:59:27 +1000 Subject: [PATCH 01/27] Add concise #inpsect to ViewModel in order to make #inpect to not blow up screen wher controller with request and helpers included in instance of the ViewModel --- lib/cell/view_model.rb | 15 +++++++++++++++ test/public_test.rb | 21 +++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/lib/cell/view_model.rb b/lib/cell/view_model.rb index 250a089b..64ccc19e 100644 --- a/lib/cell/view_model.rb +++ b/lib/cell/view_model.rb @@ -112,6 +112,21 @@ def to_s end include Caching + def inspect + parent_controller_s = if @parent_controller + "#<#{@parent_controller.class.name}:#{@parent_controller.object_id}>" + else + nil.inspect + end + + options_s = options.dup + options_s[:controller] = parent_controller_s + + "#<#{self.class.name}:#{self.object_id}" \ + " @parent_controller=#{parent_controller_s}," \ + " @model=#{@model.inspect}, @options=#{options_s}>" + end + private attr_reader :options diff --git a/test/public_test.rb b/test/public_test.rb index 80b8883e..bcf3af7b 100644 --- a/test/public_test.rb +++ b/test/public_test.rb @@ -56,6 +56,27 @@ class Songs < Cell::Concept options.to_s.must_equal "{:genre=>\"Fusion\", :collection=>[Object]}" end + # #inspect + it do + cell = Cell::ViewModel.(model_obj = Object.new, controller: Object.new) + cell.instance_variable_set('@parent_controller', parent_controller_obj = Object.new) + + inspection_s = cell.inspect + + inspection_s.must_match '#" + inspection_s.must_match "@model=#\"#\"}" + end + it do + inspection_s = Cell::ViewModel.().inspect + + inspection_s.must_match '#\"nil\"" + end + # it do # content = "" # Cell::ViewModel.cell("public_test/song", collection: [Object, Module]).each_with_index do |cell, i| From fafbe0a0a1748625e9aea6879a8f8dd660cf98c9 Mon Sep 17 00:00:00 2001 From: shvetsovdm Date: Sat, 14 May 2016 11:38:47 +1000 Subject: [PATCH 02/27] Refactor #inspect to a modular extensible solution --- lib/cell.rb | 1 + lib/cell/inspect.rb | 31 +++++++++++++++++++++++++++++ lib/cell/view_model.rb | 16 +-------------- test/inspect_test.rb | 44 ++++++++++++++++++++++++++++++++++++++++++ test/public_test.rb | 21 -------------------- 5 files changed, 77 insertions(+), 36 deletions(-) create mode 100644 lib/cell/inspect.rb create mode 100644 test/inspect_test.rb diff --git a/lib/cell.rb b/lib/cell.rb index 6e08ad82..88acd9af 100644 --- a/lib/cell.rb +++ b/lib/cell.rb @@ -19,6 +19,7 @@ def initialize(prefixes, view) require "cell/templates" require "cell/abstract" require "cell/util" +require "cell/inspect" require "cell/view_model" require "cell/concept" require "cell/escaped" diff --git a/lib/cell/inspect.rb b/lib/cell/inspect.rb new file mode 100644 index 00000000..0e5c9ad5 --- /dev/null +++ b/lib/cell/inspect.rb @@ -0,0 +1,31 @@ +module Cell + module Inspect + def inspect + if inspect_blacklist.any? + build_inspect_s + else + super + end + end + + private + + def build_inspect_s + ivars = Hash[self.instance_variables.map { |name| [name[1..-1], self.instance_variable_get(name)] }] + + ivars_s = ivars.map do |name, value| + if inspect_blacklist.include?(name) + "@#{name}=#<#{value.class.name}:#{value.object_id}>" + else + "<@#{name}=#{value.inspect}>" + end + end.join(', ') + + "#<#{self.class.name}:#{self.object_id} #{ivars_s}>" + end + + def inspect_blacklist + [] + end + end +end diff --git a/lib/cell/view_model.rb b/lib/cell/view_model.rb index 64ccc19e..b46ded05 100644 --- a/lib/cell/view_model.rb +++ b/lib/cell/view_model.rb @@ -106,27 +106,13 @@ def render_template(template, options, &block) end include Rendering + include Inspect def to_s call end include Caching - def inspect - parent_controller_s = if @parent_controller - "#<#{@parent_controller.class.name}:#{@parent_controller.object_id}>" - else - nil.inspect - end - - options_s = options.dup - options_s[:controller] = parent_controller_s - - "#<#{self.class.name}:#{self.object_id}" \ - " @parent_controller=#{parent_controller_s}," \ - " @model=#{@model.inspect}, @options=#{options_s}>" - end - private attr_reader :options diff --git a/test/inspect_test.rb b/test/inspect_test.rb new file mode 100644 index 00000000..6df3deab --- /dev/null +++ b/test/inspect_test.rb @@ -0,0 +1,44 @@ +require 'test_helper' + +class InspectTest < Minitest::Spec + class FakeModel + def initialize(title:) + @title = title + end + end + + def build_model + InspectTest::FakeModel.new(title: 'Title') + end + # #inspect + it do + cell = Cell::ViewModel.(model_obj = build_model, options = { genre: "Djent" }) + + inspection_s = cell.inspect + + inspection_s.must_match '#" + inspection_s.must_match "@options=#{options.inspect}" + end + end +end diff --git a/test/public_test.rb b/test/public_test.rb index bcf3af7b..80b8883e 100644 --- a/test/public_test.rb +++ b/test/public_test.rb @@ -56,27 +56,6 @@ class Songs < Cell::Concept options.to_s.must_equal "{:genre=>\"Fusion\", :collection=>[Object]}" end - # #inspect - it do - cell = Cell::ViewModel.(model_obj = Object.new, controller: Object.new) - cell.instance_variable_set('@parent_controller', parent_controller_obj = Object.new) - - inspection_s = cell.inspect - - inspection_s.must_match '#" - inspection_s.must_match "@model=#\"#\"}" - end - it do - inspection_s = Cell::ViewModel.().inspect - - inspection_s.must_match '#\"nil\"" - end - # it do # content = "" # Cell::ViewModel.cell("public_test/song", collection: [Object, Module]).each_with_index do |cell, i| From 834aae90fe5028f0fc26e8da99391f71b89ba5c4 Mon Sep 17 00:00:00 2001 From: shvetsovdm Date: Sat, 14 May 2016 12:07:26 +1000 Subject: [PATCH 03/27] Fix inspect_test.rb to work with ruby 2.0.0 --- test/inspect_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/inspect_test.rb b/test/inspect_test.rb index 6df3deab..ca462d32 100644 --- a/test/inspect_test.rb +++ b/test/inspect_test.rb @@ -2,13 +2,13 @@ class InspectTest < Minitest::Spec class FakeModel - def initialize(title:) + def initialize(title) @title = title end end def build_model - InspectTest::FakeModel.new(title: 'Title') + InspectTest::FakeModel.new('Title') end # #inspect it do From d0ed61bb272bc85143d13cdde59e791f51fb3b5f Mon Sep 17 00:00:00 2001 From: George Millo Date: Thu, 11 May 2017 10:18:31 +0100 Subject: [PATCH 04/27] bump cells-erb dev dependency to >= 0.1.0 --- cells.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cells.gemspec b/cells.gemspec index 61af5bcc..d932d840 100644 --- a/cells.gemspec +++ b/cells.gemspec @@ -26,5 +26,5 @@ Gem::Specification.new do |spec| spec.add_development_dependency "rake" spec.add_development_dependency "capybara" - spec.add_development_dependency "cells-erb", ">= 0.0.4" + spec.add_development_dependency "cells-erb", ">= 0.1.0" end From 27c62c1816dcc716ff267f7f7dd8cb27e9d48ac4 Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Tue, 12 Sep 2017 20:00:52 +0200 Subject: [PATCH 05/27] README: Build badge point to trailblazer [ci skip] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7cff4448..10978577 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Gitter Chat](https://badges.gitter.im/trailblazer/chat.svg)](https://gitter.im/trailblazer/chat) [![TRB Newsletter](https://img.shields.io/badge/TRB-newsletter-lightgrey.svg)](http://trailblazer.to/newsletter/) [![Build -Status](https://travis-ci.org/apotonick/cells.svg)](https://travis-ci.org/apotonick/cells) +Status](https://travis-ci.org/trailblazer/cells.svg)](https://travis-ci.org/trailblazer/cells) [![Gem Version](https://badge.fury.io/rb/cells.svg)](http://badge.fury.io/rb/cells) ## Overview From c4e6b9dca42423ada57dcf5d908887a57b857b5c Mon Sep 17 00:00:00 2001 From: Johannes Barre Date: Mon, 19 Mar 2018 10:28:50 +0100 Subject: [PATCH 06/27] Update tested Ruby version Ruby 2.2 is reaching it's end of life at the end of the month. But I think it's useful to test recent Ruby versions as well. --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2e52d378..3f91007b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,9 @@ language: ruby rvm: #- ruby-head - - 2.3.1 - - 2.2.2 + - 2.5 + - 2.4.3 + - 2.3.6 sudo: false cache: bundler From 9ea1ee8776ea0d0e3f4b5e0a561585c767544f10 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Tue, 24 Apr 2018 21:38:30 +0100 Subject: [PATCH 07/27] fix warning for undefined instance variable --- .travis.yml | 9 +++++---- CHANGES.md | 4 ++++ lib/cell.rb | 1 - lib/cell/abstract.rb | 2 +- lib/cell/testing.rb | 2 +- lib/cell/version.rb | 2 +- lib/cells.rb | 1 + 7 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3f91007b..857d5777 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,9 @@ language: ruby rvm: #- ruby-head - - 2.5 - - 2.4.3 - - 2.3.6 + - 2.5.1 + - 2.4.4 + - 2.3.7 sudo: false cache: bundler @@ -14,4 +14,5 @@ matrix: - rvm: ruby-head before_install: - - gem update bundler + - gem update --system + - gem install bundler diff --git a/CHANGES.md b/CHANGES.md index ed4e44ab..1c3927db 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +## 4.1.8 + +* Fix ruby warning with undefined instance variable in codebase + ## 4.1.7 * `Collection#join` can now be called without a block. diff --git a/lib/cell.rb b/lib/cell.rb index ec9c121e..2909da16 100644 --- a/lib/cell.rb +++ b/lib/cell.rb @@ -1,6 +1,5 @@ require "tilt" require "uber/inheritable_attr" -require "cell/version" module Cell autoload :Testing, "cell/testing" diff --git a/lib/cell/abstract.rb b/lib/cell/abstract.rb index 3600ecfd..f38bf519 100644 --- a/lib/cell/abstract.rb +++ b/lib/cell/abstract.rb @@ -4,6 +4,6 @@ def abstract! end def abstract? - @abstract + @abstract if defined?(@abstract) end end \ No newline at end of file diff --git a/lib/cell/testing.rb b/lib/cell/testing.rb index e5b6d737..33d262e7 100644 --- a/lib/cell/testing.rb +++ b/lib/cell/testing.rb @@ -32,7 +32,7 @@ def self.capybara=(value) end def self.capybara? - @capybara + @capybara if defined?(@capybara) end # Extends ViewModel#call by injecting Capybara support. diff --git a/lib/cell/version.rb b/lib/cell/version.rb index 1abdd144..e1c46546 100644 --- a/lib/cell/version.rb +++ b/lib/cell/version.rb @@ -1,3 +1,3 @@ module Cell - VERSION = "4.1.7" + VERSION = "4.1.8" end diff --git a/lib/cells.rb b/lib/cells.rb index f4e138e3..182a65a3 100644 --- a/lib/cells.rb +++ b/lib/cells.rb @@ -1 +1,2 @@ +require "cell/version" require "cell" From 036dea887253c64658d671944d48237f7c82bc25 Mon Sep 17 00:00:00 2001 From: Jorne Kandziora Date: Fri, 27 Apr 2018 16:53:26 +0200 Subject: [PATCH 08/27] Use Tilt::Cache instead of our own cache implementation Tilt::Cache is a generic key-value store. This works exactly the same, if we move some code around. --- lib/cell/templates.rb | 40 +++++++--------------------------------- 1 file changed, 7 insertions(+), 33 deletions(-) diff --git a/lib/cell/templates.rb b/lib/cell/templates.rb index 85ef648e..1dd6d861 100644 --- a/lib/cell/templates.rb +++ b/lib/cell/templates.rb @@ -7,15 +7,17 @@ def [](prefixes, view, options) end private - def cache - @cache ||= Cache.new + @cache ||= Tilt::Cache.new end def find_template(prefixes, view, options) # options is not considered in cache key. - cache.fetch(prefixes, view) do |prefix| - # this block is run once per cell class per process, for each prefix/view tuple. - create(prefix, view, options) + cache.fetch(prefixes, view) do + template = nil + prefixes.find do |prefix| + template = create(prefix, view, options) + end + template end end @@ -26,33 +28,5 @@ def create(prefix, view, options) template_class = options.delete(:template_class) template_class.new("#{prefix}/#{view}", options) # Tilt.new() end - - # {["comment/row/views", comment/views"]["show.haml"] => "Tpl:comment/view/show.haml"} - class Cache - def initialize - @store = {} - end - - # Iterates prefixes and yields block. Returns and caches when block returned template. - # Note that it caches per prefixes set as this will most probably never change. - def fetch(prefixes, view) - template = get(prefixes, view) and return template # cache hit. - - prefixes.find do |prefix| - template = yield(prefix) and return store(prefixes, view, template) - end - end - - private - # ["comment/views"] => "show.haml" - def get(prefixes, view) - @store[prefixes] ||= {} - @store[prefixes][view] - end - - def store(prefix, view, template) - @store[prefix][view] = template # the nested hash is always present here. - end - end end end From ee8133dfffc7769acf799aef29fb1ad8173ff6ea Mon Sep 17 00:00:00 2001 From: Jorne Kandziora Date: Fri, 27 Apr 2018 16:56:25 +0200 Subject: [PATCH 09/27] Split file lookup and template parsing The find loop is necessary to find out which prefix contains the template that we are looking for. Template parsing happens for the first template that is found. If we split the file lookup and template building, the code becomes easier to read and easier to maintain. --- lib/cell/templates.rb | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/lib/cell/templates.rb b/lib/cell/templates.rb index 1dd6d861..846b3e0f 100644 --- a/lib/cell/templates.rb +++ b/lib/cell/templates.rb @@ -13,20 +13,11 @@ def cache def find_template(prefixes, view, options) # options is not considered in cache key. cache.fetch(prefixes, view) do - template = nil - prefixes.find do |prefix| - template = create(prefix, view, options) - end - template + template_prefix = prefixes.find { |prefix| File.exist?("#{prefix}/#{view}") } + return if template_prefix.nil? # We can safely return early. Tilt::Cache does not cache nils. + template_class = options.delete(:template_class) + template_class.new("#{template_prefix}/#{view}", options) # Tilt.new() end end - - def create(prefix, view, options) - # puts "...checking #{prefix}/#{view}" - return unless File.exist?("#{prefix}/#{view}") # DISCUSS: can we use Tilt.new here? - - template_class = options.delete(:template_class) - template_class.new("#{prefix}/#{view}", options) # Tilt.new() - end end end From 0c9f655b4feb4d223887a426284165a8de4fb829 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Fri, 20 Jul 2018 05:56:23 +0100 Subject: [PATCH 10/27] Drop support for old rubies --- .travis.yml | 1 + CHANGES.md | 4 ++++ Gemfile | 10 +++++--- benchmarks/class_builder.rb | 37 ----------------------------- benchmarks/collection.rb | 47 ------------------------------------- cells.gemspec | 18 +++++++------- lib/cell/version.rb | 2 +- lib/cell/view_model.rb | 3 +-- 8 files changed, 23 insertions(+), 99 deletions(-) delete mode 100644 benchmarks/class_builder.rb delete mode 100644 benchmarks/collection.rb diff --git a/.travis.yml b/.travis.yml index 857d5777..c6bec217 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ rvm: - 2.5.1 - 2.4.4 - 2.3.7 + - 2.2.10 sudo: false cache: bundler diff --git a/CHANGES.md b/CHANGES.md index 1c3927db..34dad29e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +## 4.5.0 + +* Drop support for rubies prior 2.2.10 + ## 4.1.8 * Fix ruby warning with undefined instance variable in codebase diff --git a/Gemfile b/Gemfile index 93f5d477..50f84736 100644 --- a/Gemfile +++ b/Gemfile @@ -1,10 +1,14 @@ source "https://rubygems.org" -gem "minitest", "~> 5.2" -gem "cells-erb"#, path: "../cells-erb" gem "benchmark-ips" gem "minitest-line" gemspec -# gem "uber", path: "../uber" +case ENV["GEMS_SOURCE"] + when "local" + gem "cells-erb", path: "../cells-erb" + # gem "erbse", path: "../erbse" + when "github" + gem "cells-erb", github: "trailblazer/cells-erb" +end diff --git a/benchmarks/class_builder.rb b/benchmarks/class_builder.rb deleted file mode 100644 index bc1ae5b0..00000000 --- a/benchmarks/class_builder.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'bundler/setup' -require 'benchmark/ips' -require "cells" -require 'cell/view_model' - -class ACell < Cell::ViewModel - def show - "" - end -end - -class ACellWithBuilder < Cell::ViewModel - include Cell::Builder - - def show - "" - end -end - -Benchmark.ips do |x| - x.report("ACell") { ACell.().() } - x.report("ACellWithBuilder") { ACellWithBuilder.().() } - x.compare! -end - -__END__ - -Calculating ------------------------------------- - ACell 25.710k i/100ms - ACellWithBuilder 19.948k i/100ms -------------------------------------------------- - ACell 419.631k (± 5.0%) i/s - 2.108M - ACellWithBuilder 291.924k (± 5.7%) i/s - 1.476M - -Comparison: - ACell: 419630.8 i/s - ACellWithBuilder: 291923.5 i/s - 1.44x slower diff --git a/benchmarks/collection.rb b/benchmarks/collection.rb deleted file mode 100644 index dafcd118..00000000 --- a/benchmarks/collection.rb +++ /dev/null @@ -1,47 +0,0 @@ -require 'test_helper' -require 'benchmark' -require 'benchmark/ips' - -Song = Struct.new(:title) - - -class SongCell < Cell::ViewModel - self.view_paths = ['test/fixtures'] - property :title - - def show - render - end -end - -ary = 1000.times.collect { |i| Song.new(i) } - -Benchmark.ips do |x| - x.report("collection, call") { SongCell.(collection: ary).() } - x.report("collection, join") { SongCell.(collection: ary).join { |cell, i| cell.() } } - # x.report("collection, joinstr") { SongCell.(collection: ary).joinstr { |cell, i| cell.() } } - # x.report("collection, joincollect") { SongCell.(collection: ary).joincollect { |cell, i| cell.() } } - # x.report("ACellWithBuilder") { ACellWithBuilder.().() } - x.compare! -end - -__END__ - -Calculating ------------------------------------- - collection, call 3.000 i/100ms - collection, join 3.000 i/100ms -------------------------------------------------- - collection, call 33.403 (± 3.0%) i/s - 168.000 - collection, join 33.248 (± 3.0%) i/s - 168.000 - -Comparison: - collection, call: 33.4 i/s - collection, join: 33.2 i/s - 1.00x slower - - - -Comparison: - collection, join: 32.8 i/s - collection, call: 32.6 i/s - 1.01x slower - collection, joinstr: 32.6 i/s - 1.01x slower -collection, joincollect: 32.4 i/s - 1.01x slower # each_with_index.collect.join diff --git a/cells.gemspec b/cells.gemspec index d932d840..cf140df6 100644 --- a/cells.gemspec +++ b/cells.gemspec @@ -1,30 +1,30 @@ -lib = File.expand_path("../lib/", __FILE__) -$:.unshift lib unless $:.include?(lib) +lib = File.expand_path("lib", __dir__) +$LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib) require "cell/version" Gem::Specification.new do |spec| spec.name = "cells" spec.version = Cell::VERSION - spec.platform = Gem::Platform::RUBY spec.authors = ["Nick Sutterer"] spec.email = ["apotonick@gmail.com"] spec.homepage = "https://github.com/apotonick/cells" - spec.summary = %q{View Models for Ruby and Rails.} - spec.description = %q{View Models for Ruby and Rails, replacing helpers and partials while giving you a clean view architecture with proper encapsulation.} + spec.summary = "View Models for Ruby and Rails." + spec.description = "View Models for Ruby and Rails, replacing helpers and partials while giving you a clean view architecture with proper encapsulation." spec.license = "MIT" spec.files = `git ls-files`.split("\n") spec.test_files = `git ls-files -- {test}/*`.split("\n") - spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } spec.require_paths = ["lib"] + spec.required_ruby_version = ">= 2.2.10" - spec.add_dependency "uber", "< 0.2.0" - spec.add_dependency "declarative-option", "< 0.2.0" spec.add_dependency "declarative-builder", "< 0.2.0" + spec.add_dependency "declarative-option", "< 0.2.0" spec.add_dependency "tilt", ">= 1.4", "< 3" + spec.add_dependency "uber", "< 0.2.0" - spec.add_development_dependency "rake" spec.add_development_dependency "capybara" spec.add_development_dependency "cells-erb", ">= 0.1.0" + spec.add_development_dependency "minitest" + spec.add_development_dependency "rake" end diff --git a/lib/cell/version.rb b/lib/cell/version.rb index e1c46546..b3a0cd65 100644 --- a/lib/cell/version.rb +++ b/lib/cell/version.rb @@ -1,3 +1,3 @@ module Cell - VERSION = "4.1.8" + VERSION = "4.5.0" end diff --git a/lib/cell/view_model.rb b/lib/cell/view_model.rb index 5bd25cad..02f13a86 100644 --- a/lib/cell/view_model.rb +++ b/lib/cell/view_model.rb @@ -199,8 +199,7 @@ def process_options!(options) # Computes the view name from the call stack in which `render` was invoked. def state_for_implicit_render(options) - _caller = RUBY_VERSION < "2.0" ? caller(3) : caller(3, 1) # TODO: remove case in 5.0 when dropping 1.9. - _caller[0].match(/`(\w+)/)[1] + caller(3, 1)[0].match(/`(\w+)/)[1] end include Layout From e9f7f87ae6fcce35c27609e4c4eb58ca53dde57f Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Fri, 20 Jul 2018 06:22:39 +0100 Subject: [PATCH 11/27] remove OutputBuffer Module from cells --- lib/cell/view_model.rb | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/lib/cell/view_model.rb b/lib/cell/view_model.rb index 02f13a86..2ac6b305 100644 --- a/lib/cell/view_model.rb +++ b/lib/cell/view_model.rb @@ -136,25 +136,6 @@ def setup!(model, options) # or: create_twin(model, options) end - class OutputBuffer < Array - def encoding - "UTF-8" - end - - def <<(string) - super - end - alias_method :safe_append=, :<< - alias_method :append=, :<< - - def to_s # output_buffer is returned at the end of the precompiled template. - join - end - end - def output_buffer # called from the precompiled template. FIXME: this is currently not used in Haml. - OutputBuffer.new # don't cache output_buffer, for every #render call we get a fresh one. - end - module TemplateFor def find_template(options) template_options = template_options_for(options) # imported by Erb, Haml, etc. From 9d7ed5b96b600e6f1d3487cdb8a35d0ec0726c7a Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Mon, 29 Jul 2019 17:40:44 +0200 Subject: [PATCH 12/27] CI: Drop unused Travis sudo: false directive See https://blog.travis-ci.com/2018-11-19-required-linux-infrastructure-migration for more details --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c6bec217..1fbcc378 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,6 @@ rvm: - 2.3.7 - 2.2.10 -sudo: false cache: bundler matrix: From 4501202447f140b81fe8c899f4ed4a590a30e6ef Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Mon, 20 Jul 2020 07:45:37 +0100 Subject: [PATCH 13/27] update minitest syntax --- .travis.yml | 18 ++++------------- test/builder_test.rb | 16 +++++++-------- test/cache_test.rb | 4 ++-- test/cell_test.rb | 4 ++-- test/concept_test.rb | 30 +++++++++++++-------------- test/context_test.rb | 18 ++++++++--------- test/inspect_test.rb | 20 +++++++++--------- test/layout_test.rb | 14 ++++++------- test/partial_test.rb | 12 +++++------ test/prefixes_test.rb | 46 +++++++++++++++++++++--------------------- test/property_test.rb | 12 +++++------ test/public_test.rb | 38 +++++++++++++++++----------------- test/render_test.rb | 28 ++++++++++++------------- test/templates_test.rb | 6 +++--- test/testing_test.rb | 16 +++++++-------- 15 files changed, 136 insertions(+), 146 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1fbcc378..850177f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,8 @@ language: ruby +before_install: gem install bundler rvm: - #- ruby-head - - 2.5.1 - - 2.4.4 - - 2.3.7 - - 2.2.10 + - 2.7 + - 2.6 + - 2.5 cache: bundler - -matrix: - fast_finish: true - allow_failures: - - rvm: ruby-head - -before_install: - - gem update --system - - gem install bundler diff --git a/test/builder_test.rb b/test/builder_test.rb index f28b953d..7a5c2220 100644 --- a/test/builder_test.rb +++ b/test/builder_test.rb @@ -36,28 +36,28 @@ class EvergreenCell < SongCell end # the original class is used when no builder matches. - it { SongCell.(Song.new("Nation States"), {}).must_be_instance_of SongCell } + it { _(SongCell.(Song.new("Nation States"), {})).must_be_instance_of SongCell } it do cell = SongCell.(Hit.new("New York"), {}) - cell.must_be_instance_of HitCell - cell.options.must_equal({}) + _(cell).must_be_instance_of HitCell + _(cell.options).must_equal({}) end it do cell = SongCell.(Song.new("San Francisco"), evergreen: true) - cell.must_be_instance_of EvergreenCell - cell.options.must_equal({evergreen:true}) + _(cell).must_be_instance_of EvergreenCell + _(cell.options).must_equal({evergreen:true}) end # without arguments. - it { SongCell.(Hit.new("Frenzy")).must_be_instance_of HitCell } + it { _(SongCell.(Hit.new("Frenzy"))).must_be_instance_of HitCell } # with collection. - it { SongCell.(collection: [Song.new("Nation States"), Hit.new("New York")]).().must_equal "* Nation States* **New York**" } + it { _(SongCell.(collection: [Song.new("Nation States"), Hit.new("New York")]).()).must_equal "* Nation States* **New York**" } # with Concept class Track < Cell::Concept end - it { Track.().must_be_instance_of Track } + it { _(Track.()).must_be_instance_of Track } end diff --git a/test/cache_test.rb b/test/cache_test.rb index 66b1a670..8385d957 100644 --- a/test/cache_test.rb +++ b/test/cache_test.rb @@ -25,8 +25,8 @@ class Index < Cell::ViewModel end it do - Index.new(1).().must_equal("1") - Index.new(2).().must_equal("1") + _(Index.new(1).()).must_equal("1") + _(Index.new(2).()).must_equal("1") end end diff --git a/test/cell_test.rb b/test/cell_test.rb index d9476dd6..bcc1c502 100644 --- a/test/cell_test.rb +++ b/test/cell_test.rb @@ -13,8 +13,8 @@ def show_with_block(&block) end # #options - it { SongCell.new(nil, genre: "Punkrock").send(:options)[:genre].must_equal "Punkrock" } + it { _(SongCell.new(nil, genre: "Punkrock").send(:options)[:genre]).must_equal "Punkrock" } # #block - it { SongCell.new(nil, genre: "Punkrock").(:show_with_block) { "hello" }.must_equal "hello\n" } + it { _(SongCell.new(nil, genre: "Punkrock").(:show_with_block) { "hello" }).must_equal "hello\n" } end diff --git a/test/concept_test.rb b/test/concept_test.rb index cfed5c14..80c721d6 100644 --- a/test/concept_test.rb +++ b/test/concept_test.rb @@ -48,40 +48,40 @@ class Song < ::Cell::Concept class ConceptTest < MiniTest::Spec describe "::controller_path" do - it { Record::Cell.new.class.controller_path.must_equal "record" } - it { Record::Cell::Song.new.class.controller_path.must_equal "record/song" } - it { Record::Cells::Cell.new.class.controller_path.must_equal "record/cells" } - it { Record::Cells::Cell::Song.new.class.controller_path.must_equal "record/cells/song" } + it { _(Record::Cell.new.class.controller_path).must_equal "record" } + it { _(Record::Cell::Song.new.class.controller_path).must_equal "record/song" } + it { _(Record::Cells::Cell.new.class.controller_path).must_equal "record/cells" } + it { _(Record::Cells::Cell::Song.new.class.controller_path).must_equal "record/cells/song" } end describe "#_prefixes" do - it { Record::Cell.new._prefixes.must_equal ["test/fixtures/concepts/record/views"] } - it { Record::Cell::Song.new._prefixes.must_equal ["test/fixtures/concepts/record/song/views", "test/fixtures/concepts/record/views"] } - it { Record::Cell::Hit.new._prefixes.must_equal ["test/fixtures/concepts/record/hit/views", "test/fixtures/concepts/record/views"] } # with inherit_views. + it { _(Record::Cell.new._prefixes).must_equal ["test/fixtures/concepts/record/views"] } + it { _(Record::Cell::Song.new._prefixes).must_equal ["test/fixtures/concepts/record/song/views", "test/fixtures/concepts/record/views"] } + it { _(Record::Cell::Hit.new._prefixes).must_equal ["test/fixtures/concepts/record/hit/views", "test/fixtures/concepts/record/views"] } # with inherit_views. end - it { Record::Cell.new("Wayne").call(:show).must_equal "Party on, Wayne!" } + it { _(Record::Cell.new("Wayne").call(:show)).must_equal "Party on, Wayne!" } describe "::cell" do - it { Cell::Concept.cell("record/cell").must_be_instance_of( Record::Cell) } - it { Cell::Concept.cell("record/cell/song").must_be_instance_of Record::Cell::Song } + it { _(Cell::Concept.cell("record/cell")).must_be_instance_of( Record::Cell) } + it { _(Cell::Concept.cell("record/cell/song")).must_be_instance_of Record::Cell::Song } # cell("song", concept: "record/compilation") # record/compilation/cell/song end describe "#render" do - it { Cell::Concept.cell("record/cell/song").show.must_equal "Lalala" } + it { _(Cell::Concept.cell("record/cell/song").show).must_equal "Lalala" } end describe "#cell (in state)" do # test with controller, but remove tests when we don't need it anymore. - it { Cell::Concept.cell("record/cell", nil, context: { controller: Object }).cell("record/cell", nil).must_be_instance_of Record::Cell } - it { Cell::Concept.cell("record/cell", nil, context: { controller: Object }).concept("record/cell", nil, tracks: 24).(:description).must_equal "A Tribute To Rancid, with 24 songs! [{:controller=>Object}]" } + it { _(Cell::Concept.cell("record/cell", nil, context: { controller: Object }).cell("record/cell", nil)).must_be_instance_of Record::Cell } + it { _(Cell::Concept.cell("record/cell", nil, context: { controller: Object }).concept("record/cell", nil, tracks: 24).(:description)).must_equal "A Tribute To Rancid, with 24 songs! [{:controller=>Object}]" } # concept(.., collection: ..) it do - Cell::Concept.cell("record/cell", nil, context: { controller: Object }). - concept("record/cell", collection: [1,2], tracks: 24).(:description).must_equal "A Tribute To Rancid, with 24 songs! [{:controller=>Object}]A Tribute To Rancid, with 24 songs! [{:controller=>Object}]" + _(Cell::Concept.cell("record/cell", nil, context: { controller: Object }). + concept("record/cell", collection: [1,2], tracks: 24).(:description)).must_equal "A Tribute To Rancid, with 24 songs! [{:controller=>Object}]A Tribute To Rancid, with 24 songs! [{:controller=>Object}]" end end end diff --git a/test/context_test.rb b/test/context_test.rb index 280aa180..44fac89c 100644 --- a/test/context_test.rb +++ b/test/context_test.rb @@ -18,16 +18,16 @@ def controller let (:parent) { ParentCell.(model, admin: true, context: { user: user, controller: controller }) } it do - parent.model.must_equal model - parent.controller.must_equal controller - parent.user.must_equal user + _(parent.model).must_equal model + _(parent.controller).must_equal controller + _(parent.user).must_equal user # nested cell child = parent.cell("context_test/parent", "") - child.model.must_equal "" - child.controller.must_equal controller - child.user.must_equal user + _(child.model).must_equal "" + _(child.controller).must_equal controller + _(child.user).must_equal user end # child can add to context @@ -37,8 +37,8 @@ def controller assert_nil(parent.context["is_child?"]) assert_nil(child.model) - child.controller.must_equal controller - child.user.must_equal user - child.context["is_child?"].must_equal true + _(child.controller).must_equal controller + _(child.user).must_equal user + _(child.context["is_child?"]).must_equal true end end diff --git a/test/inspect_test.rb b/test/inspect_test.rb index ca462d32..dbae5337 100644 --- a/test/inspect_test.rb +++ b/test/inspect_test.rb @@ -16,17 +16,17 @@ def build_model inspection_s = cell.inspect - inspection_s.must_match '#" - inspection_s.must_match "@options=#{options.inspect}" + _(inspection_s).must_match '#" + _(inspection_s).must_match "@options=#{options.inspect}" end end end diff --git a/test/layout_test.rb b/test/layout_test.rb index 36cc7aa4..21a50287 100644 --- a/test/layout_test.rb +++ b/test/layout_test.rb @@ -42,7 +42,7 @@ def show_with_layout class LayoutTest < MiniTest::Spec # render show.haml calling method. # same context as content view as layout call method. - it { SongWithLayoutCell.new(nil).show.must_equal "Merry Xmas, Papertiger" } + it { _(SongWithLayoutCell.new(nil).show).must_equal "Merry Xmas, Papertiger\n" } # raises exception when layout not found! @@ -51,10 +51,10 @@ class LayoutTest < MiniTest::Spec it { } # with ::layout. - it { SongWithLayoutOnClassCell.new(nil).show.must_equal "Merry Xmas, Papertiger" } + it { _(SongWithLayoutOnClassCell.new(nil).show).must_equal "Merry Xmas, Papertiger\n" } # with ::layout and :layout, :layout wins. - it { SongWithLayoutOnClassCell.new(nil).show_with_layout.must_equal "Happy Friday!" } + it { _(SongWithLayoutOnClassCell.new(nil).show_with_layout).must_equal "Happy Friday!" } end module Comment @@ -74,15 +74,15 @@ class LayoutCell < Cell::ViewModel class ExternalLayoutTest < Minitest::Spec it do - Comment::ShowCell.new(nil, layout: Comment::LayoutCell, context: { beer: true }). - ().must_equal "$layout.erb{$show.erb, {:beer=>true}$show.erb, {:beer=>true}, {:beer=>true}} + _(Comment::ShowCell.new(nil, layout: Comment::LayoutCell, context: { beer: true }). + ()).must_equal "$layout.erb{$show.erb, {:beer=>true}\n$show.erb, {:beer=>true}\n, {:beer=>true}} " end # collection :layout it do - Cell::ViewModel.cell("comment/show", collection: [Object, Module], layout: Comment::LayoutCell).(). - must_equal "$layout.erb{$show.erb, nil$show.erb, nil$show.erb, nil$show.erb, nil, nil} + _(Cell::ViewModel.cell("comment/show", collection: [Object, Module], layout: Comment::LayoutCell).()). + must_equal "$layout.erb{$show.erb, nil\n$show.erb, nil\n$show.erb, nil\n$show.erb, nil\n, nil} " end end diff --git a/test/partial_test.rb b/test/partial_test.rb index 2df6ed58..8e1baae1 100644 --- a/test/partial_test.rb +++ b/test/partial_test.rb @@ -25,11 +25,11 @@ class WithPartialAndManyViewPaths < WithPartial self.view_paths << ['app/views'] end - it { WithPartial.new(nil).show.must_equal "I Am Wrong And I Am Right" } - it { WithPartial.new(nil).show_with_format.must_equal "I Am Wrong And I Am Right" } - it { WithPartial.new(nil).show_without_partial.must_equal "Adenosine Breakdown" } + it { _(WithPartial.new(nil).show).must_equal "I Am Wrong And I Am Right" } + it { _(WithPartial.new(nil).show_with_format).must_equal "I Am Wrong And I Am Right" } + it { _(WithPartial.new(nil).show_without_partial).must_equal "Adenosine Breakdown" } - it { WithPartialAndManyViewPaths.new(nil).show.must_equal "I Am Wrong And I Am Right" } - it { WithPartialAndManyViewPaths.new(nil).show_with_format.must_equal "I Am Wrong And I Am Right" } - it { WithPartialAndManyViewPaths.new(nil).show_without_partial.must_equal "Adenosine Breakdown" } + it { _(WithPartialAndManyViewPaths.new(nil).show).must_equal "I Am Wrong And I Am Right" } + it { _(WithPartialAndManyViewPaths.new(nil).show_with_format).must_equal "I Am Wrong And I Am Right" } + it { _(WithPartialAndManyViewPaths.new(nil).show_without_partial).must_equal "Adenosine Breakdown" } end diff --git a/test/prefixes_test.rb b/test/prefixes_test.rb index 274fe6f4..b633ce9e 100644 --- a/test/prefixes_test.rb +++ b/test/prefixes_test.rb @@ -42,39 +42,39 @@ def self._local_prefixes describe "::controller_path" do - it { ::BassistCell.new(@controller).class.controller_path.must_equal "bassist" } - it { SingerCell.new(@controller).class.controller_path.must_equal "prefixes_test/singer" } + it { _(::BassistCell.new(@controller).class.controller_path).must_equal "bassist" } + it { _(SingerCell.new(@controller).class.controller_path).must_equal "prefixes_test/singer" } end describe "#_prefixes" do - it { ::BassistCell.new(@controller)._prefixes.must_equal ["test/fixtures/bassist"] } - it { ::BassistCell::FenderCell.new(@controller)._prefixes.must_equal ["app/cells/bassist_cell/fender"] } - it { ::BassistCell::IbanezCell.new(@controller)._prefixes.must_equal ["test/fixtures/bassist_cell/ibanez", "test/fixtures/bassist"] } + it { _(::BassistCell.new(@controller)._prefixes).must_equal ["test/fixtures/bassist"] } + it { _(::BassistCell::FenderCell.new(@controller)._prefixes).must_equal ["app/cells/bassist_cell/fender"] } + it { _(::BassistCell::IbanezCell.new(@controller)._prefixes).must_equal ["test/fixtures/bassist_cell/ibanez", "test/fixtures/bassist"] } - it { SingerCell.new(@controller)._prefixes.must_equal ["app/cells/prefixes_test/singer"] } - it { BackgroundVocalsCell.new(@controller)._prefixes.must_equal ["app/cells/prefixes_test/background_vocals", "app/cells/prefixes_test/singer"] } - it { ChorusCell.new(@controller)._prefixes.must_equal ["app/cells/prefixes_test/chorus", "app/cells/prefixes_test/background_vocals", "app/cells/prefixes_test/singer"] } + it { _(SingerCell.new(@controller)._prefixes).must_equal ["app/cells/prefixes_test/singer"] } + it { _(BackgroundVocalsCell.new(@controller)._prefixes).must_equal ["app/cells/prefixes_test/background_vocals", "app/cells/prefixes_test/singer"] } + it { _(ChorusCell.new(@controller)._prefixes).must_equal ["app/cells/prefixes_test/chorus", "app/cells/prefixes_test/background_vocals", "app/cells/prefixes_test/singer"] } - it { GuitaristCell.new(@controller)._prefixes.must_equal ["stringer", "app/cells/prefixes_test/singer"] } - it { BassistCell.new(@controller)._prefixes.must_equal ["app/cells/prefixes_test/bassist", "basser", "app/cells/prefixes_test/singer"] } + it { _(GuitaristCell.new(@controller)._prefixes).must_equal ["stringer", "app/cells/prefixes_test/singer"] } + it { _(BassistCell.new(@controller)._prefixes).must_equal ["app/cells/prefixes_test/bassist", "basser", "app/cells/prefixes_test/singer"] } # it { DrummerCell.new(@controller)._prefixes.must_equal ["drummer", "stringer", "prefixes_test/singer"] } # multiple view_paths. - it { EngineCell.prefixes.must_equal ["app/cells/engine", "/var/engine/app/cells/engine"] } + it { _(EngineCell.prefixes).must_equal ["app/cells/engine", "/var/engine/app/cells/engine"] } it do - InheritingFromEngineCell.prefixes.must_equal [ + _(InheritingFromEngineCell.prefixes).must_equal [ "app/cells/inheriting_from_engine", "/var/engine/app/cells/inheriting_from_engine", "app/cells/engine", "/var/engine/app/cells/engine"] end # ::_prefixes is cached. it do - WannabeCell.prefixes.must_equal ["test/fixtures/wannabe", "test/fixtures/bassist_cell/ibanez", "test/fixtures/bassist"] + _(WannabeCell.prefixes).must_equal ["test/fixtures/wannabe", "test/fixtures/bassist_cell/ibanez", "test/fixtures/bassist"] WannabeCell.instance_eval { def _local_prefixes; ["more"] end } # _prefixes is cached. - WannabeCell.prefixes.must_equal ["test/fixtures/wannabe", "test/fixtures/bassist_cell/ibanez", "test/fixtures/bassist"] + _(WannabeCell.prefixes).must_equal ["test/fixtures/wannabe", "test/fixtures/bassist_cell/ibanez", "test/fixtures/bassist"] # superclasses don't get disturbed. - ::BassistCell.prefixes.must_equal ["test/fixtures/bassist"] + _(::BassistCell.prefixes).must_equal ["test/fixtures/bassist"] end end @@ -96,12 +96,12 @@ def play class FunkerCell < SlapperCell end - it { SlapperCell.new(nil)._prefixes.must_equal ["test/fixtures/inherit_views_test/slapper", "test/fixtures/bassist"] } - it { FunkerCell.new(nil)._prefixes.must_equal ["test/fixtures/inherit_views_test/funker", "test/fixtures/inherit_views_test/slapper", "test/fixtures/bassist"] } + it { _(SlapperCell.new(nil)._prefixes).must_equal ["test/fixtures/inherit_views_test/slapper", "test/fixtures/bassist"] } + it { _(FunkerCell.new(nil)._prefixes).must_equal ["test/fixtures/inherit_views_test/funker", "test/fixtures/inherit_views_test/slapper", "test/fixtures/bassist"] } # test if normal cells inherit views. - it { cell('inherit_views_test/slapper').play.must_equal 'Doo' } - it { cell('inherit_views_test/funker').play.must_equal 'Doo' } + it { _(cell('inherit_views_test/slapper').play).must_equal 'Doo' } + it { _(cell('inherit_views_test/funker').play).must_equal 'Doo' } # TapperCell @@ -122,12 +122,12 @@ class PopperCell < TapperCell end # Tapper renders its play - it { cell('inherit_views_test/tapper').call(:play).must_equal 'Dooom!' } + it { _(cell('inherit_views_test/tapper').call(:play)).must_equal 'Dooom!' } # Tapper renders its tap - it { cell('inherit_views_test/tapper').call(:tap).must_equal 'Tap tap tap!' } + it { _(cell('inherit_views_test/tapper').call(:tap)).must_equal 'Tap tap tap!' } # Popper renders Tapper's play - it { cell('inherit_views_test/popper').call(:play).must_equal 'Dooom!' } + it { _(cell('inherit_views_test/popper').call(:play)).must_equal 'Dooom!' } # Popper renders its tap - it { cell('inherit_views_test/popper').call(:tap).must_equal "TTttttap I'm not good enough!" } + it { _(cell('inherit_views_test/popper').call(:tap)).must_equal "TTttttap I'm not good enough!" } end diff --git a/test/property_test.rb b/test/property_test.rb index 0e51a802..5152320f 100644 --- a/test/property_test.rb +++ b/test/property_test.rb @@ -11,7 +11,7 @@ def title let (:song) { Struct.new(:title).new("She Sells And Sand Sandwiches") } # ::property creates automatic accessor. - it { SongCell.(song).title.must_equal "She Sells And Sand Sandwiches" } + it { _(SongCell.(song).title).must_equal "She Sells And Sand Sandwiches" } end @@ -38,11 +38,11 @@ def raw_title end # ::property escapes, everywhere. - it { SongCell.(song).title.must_equal "<b>She Sells And Sand Sandwiches" } - it { SongCell.(song).copyright.must_equal "<a>Copy</a>" } - it { SongCell.(song).lyrics.must_equal "<i>Words</i>" } + it { _(SongCell.(song).title).must_equal "<b>She Sells And Sand Sandwiches" } + it { _(SongCell.(song).copyright).must_equal "<a>Copy</a>" } + it { _(SongCell.(song).lyrics).must_equal "<i>Words</i>" } # no escaping for non-strings. - it { SongCell.(song).artist.must_equal Object } + it { _(SongCell.(song).artist).must_equal Object } # no escaping when escape: false - it { SongCell.(song).raw_title.must_equal "She Sells And Sand Sandwiches" } + it { _(SongCell.(song).raw_title).must_equal "She Sells And Sand Sandwiches" } end diff --git a/test/public_test.rb b/test/public_test.rb index 0d16dc91..b5fa35ad 100644 --- a/test/public_test.rb +++ b/test/public_test.rb @@ -20,51 +20,51 @@ class Songs < Cell::Concept end # ViewModel.cell returns the cell instance. - it { Cell::ViewModel.cell("public_test/song").must_be_instance_of SongCell } - it { Cell::ViewModel.cell(PublicTest::SongCell).must_be_instance_of SongCell } + it { _(Cell::ViewModel.cell("public_test/song")).must_be_instance_of SongCell } + it { _(Cell::ViewModel.cell(PublicTest::SongCell)).must_be_instance_of SongCell } # Concept.cell simply camelizes the string before constantizing. - it { Cell::Concept.cell("public_test/songs").must_be_instance_of Songs } + it { _(Cell::Concept.cell("public_test/songs")).must_be_instance_of Songs } - it { Cell::Concept.cell(PublicTest::Songs).must_be_instance_of Songs } + it { _(Cell::Concept.cell(PublicTest::Songs)).must_be_instance_of Songs } # ViewModel.cell passes options to cell. - it { Cell::ViewModel.cell("public_test/song", Object, genre: "Metal").initialize_args.must_equal [Object, {genre:"Metal"}] } + it { _(Cell::ViewModel.cell("public_test/song", Object, genre: "Metal").initialize_args).must_equal [Object, {genre:"Metal"}] } # ViewModel.cell(collection: []) renders cells. - it { Cell::ViewModel.cell("public_test/song", collection: [Object, Module]).to_s.must_equal '[Object, {}][Module, {}]' } + it { _(Cell::ViewModel.cell("public_test/song", collection: [Object, Module]).to_s).must_equal '[Object, {}][Module, {}]' } # DISCUSS: should cell.() be the default? # ViewModel.cell(collection: []) renders cells with custom join. it do Gem::Deprecate::skip_during do - Cell::ViewModel.cell("public_test/song", collection: [Object, Module]).join('
') do |cell| + _(Cell::ViewModel.cell("public_test/song", collection: [Object, Module]).join('
') do |cell| cell.() - end.must_equal '[Object, {}]
[Module, {}]' + end).must_equal '[Object, {}]
[Module, {}]' end end # ViewModel.cell(collection: []) passes generic options to cell. - it { Cell::ViewModel.cell("public_test/song", collection: [Object, Module], genre: 'Metal', context: { ready: true }).to_s.must_equal "[Object, {:genre=>\"Metal\", :context=>{:ready=>true}}][Module, {:genre=>\"Metal\", :context=>{:ready=>true}}]" } + it { _(Cell::ViewModel.cell("public_test/song", collection: [Object, Module], genre: 'Metal', context: { ready: true }).to_s).must_equal "[Object, {:genre=>\"Metal\", :context=>{:ready=>true}}][Module, {:genre=>\"Metal\", :context=>{:ready=>true}}]" } # ViewModel.cell(collection: [], method: :detail) invokes #detail instead of #show. # TODO: remove in 5.0. it do Gem::Deprecate::skip_during do - Cell::ViewModel.cell("public_test/song", collection: [Object, Module], method: :detail).to_s.must_equal '* [Object, {}]* [Module, {}]' + _(Cell::ViewModel.cell("public_test/song", collection: [Object, Module], method: :detail).to_s).must_equal '* [Object, {}]* [Module, {}]' end end # ViewModel.cell(collection: []).() invokes #show. - it { Cell::ViewModel.cell("public_test/song", collection: [Object, Module]).().must_equal '[Object, {}][Module, {}]' } + it { _(Cell::ViewModel.cell("public_test/song", collection: [Object, Module]).()).must_equal '[Object, {}][Module, {}]' } # ViewModel.cell(collection: []).(:detail) invokes #detail instead of #show. - it { Cell::ViewModel.cell("public_test/song", collection: [Object, Module]).(:detail).must_equal '* [Object, {}]* [Module, {}]' } + it { _(Cell::ViewModel.cell("public_test/song", collection: [Object, Module]).(:detail)).must_equal '* [Object, {}]* [Module, {}]' } # #cell(collection: [], genre: "Fusion").() doesn't change options hash. it do Cell::ViewModel.cell("public_test/song", options = { genre: "Fusion", collection: [Object] }).() - options.to_s.must_equal "{:genre=>\"Fusion\", :collection=>[Object]}" + _(options.to_s).must_equal "{:genre=>\"Fusion\", :collection=>[Object]}" end # it do @@ -78,22 +78,22 @@ class Songs < Cell::Concept # cell(collection: []).join captures return value and joins it for you. it do - Cell::ViewModel.cell("public_test/song", collection: [Object, Module]).join do |cell, i| + _(Cell::ViewModel.cell("public_test/song", collection: [Object, Module]).join do |cell, i| i == 1 ? cell.(:detail) : cell.() - end.must_equal '[Object, {}]* [Module, {}]' + end).must_equal '[Object, {}]* [Module, {}]' end # cell(collection: []).join("<") captures return value and joins it for you with join. it do - Cell::ViewModel.cell("public_test/song", collection: [Object, Module]).join(">") do |cell, i| + _(Cell::ViewModel.cell("public_test/song", collection: [Object, Module]).join(">") do |cell, i| i == 1 ? cell.(:detail) : cell.() - end.must_equal '[Object, {}]>* [Module, {}]' + end).must_equal '[Object, {}]>* [Module, {}]' end # 'join' can be used without a block: it do - Cell::ViewModel.cell( + _(Cell::ViewModel.cell( "public_test/song", collection: [Object, Module] - ).join('---').must_equal('[Object, {}]---[Module, {}]') + ).join('---')).must_equal('[Object, {}]---[Module, {}]') end end diff --git a/test/render_test.rb b/test/render_test.rb index 078b5082..50f4d993 100644 --- a/test/render_test.rb +++ b/test/render_test.rb @@ -59,34 +59,34 @@ def title class RenderTest < MiniTest::Spec # render show.haml calling method, implicit render. - it { SongCell.new(nil).show.must_equal "Papertiger" } + it { _(SongCell.new(nil).show).must_equal "Papertiger\n" } # render ivar.haml using instance variable. - it { SongCell.new(nil).ivar.must_equal "Carnage" } + it { _(SongCell.new(nil).ivar).must_equal "Carnage\n" } # render string. - it { SongCell.new(nil).string.must_equal "Right" } + it { _(SongCell.new(nil).string).must_equal "Right" } # #call renders :show - it { SongCell.new(nil).call.must_equal "Papertiger" } + it { _(SongCell.new(nil).call).must_equal "Papertiger\n" } # call(:form) renders :form - it { SongCell.new(nil).call(:with_view_name).must_equal "Man Of Steel" } + it { _(SongCell.new(nil).call(:with_view_name)).must_equal "Man Of Steel\n" } # works with state called `send` - it { SongCell.new(nil).call(:send).must_equal "send" } + it { _(SongCell.new(nil).call(:send)).must_equal "send" } # throws an exception when not found. it do exception = assert_raises(Cell::TemplateMissingError) { SongCell.new(nil).unknown } - exception.message.must_equal "Template missing: view: `unknown.erb` prefixes: [\"test/fixtures/song\"]" + _(exception.message).must_equal "Template missing: view: `unknown.erb` prefixes: [\"test/fixtures/song\"]" end # allows locals - it { SongCell.new(nil).with_locals.must_equal "Shot Across The Bow280" } + it { _(SongCell.new(nil).with_locals).must_equal "Shot Across The Bow\n280\n" } # render :form is a shortcut. - it { SongCell.new(nil).with_view_name.must_equal "Man Of Steel" } + it { _(SongCell.new(nil).with_view_name).must_equal "Man Of Steel\n" } # :template_engine renders ERB. # it { SongCell.new(nil).with_erb.must_equal "ERB:\n\n Papertiger\n" } @@ -94,15 +94,15 @@ class RenderTest < MiniTest::Spec # view: "show.html" # allows passing in options DISCUSS: how to handle that in cache block/builder? - it { SongCell.new(nil).receiving_options.must_equal "default" } - it { SongCell.new(nil).receiving_options(:fancy).must_equal "fancy" } - it { SongCell.new(nil).call(:receiving_options, :fancy).must_equal "fancy" } + it { _(SongCell.new(nil).receiving_options).must_equal "default" } + it { _(SongCell.new(nil).receiving_options(:fancy)).must_equal "fancy" } + it { _(SongCell.new(nil).call(:receiving_options, :fancy)).must_equal "fancy" } # doesn't escape HTML. - it { SongCell.new(nil).call(:with_html).must_equal "

Yew!

" } + it { _(SongCell.new(nil).call(:with_html)).must_equal "

Yew!

" } # render {} with block - it { SongCell.new(nil).with_block.must_equal "Yo! Clean Sheets

Yew!

" } + it { _(SongCell.new(nil).with_block).must_equal "Yo! Clean Sheets

Yew!

\n" } end # test inheritance diff --git a/test/templates_test.rb b/test/templates_test.rb index 44cb1db5..f491f666 100644 --- a/test/templates_test.rb +++ b/test/templates_test.rb @@ -5,7 +5,7 @@ class TemplatesTest < MiniTest::Spec Templates = Cell::Templates # existing. - it { Templates.new[['test/fixtures/bassist'], 'play.erb', {template_class: Cell::Erb::Template}].file.must_equal 'test/fixtures/bassist/play.erb' } + it { _(Templates.new[['test/fixtures/bassist'], 'play.erb', {template_class: Cell::Erb::Template}].file).must_equal 'test/fixtures/bassist/play.erb' } # not existing. it { assert_nil(Templates.new[['test/fixtures/bassist'], 'not-here.erb', {}]) } @@ -32,13 +32,13 @@ def show it do cell = cell("templates_caching_test/song") - cell.call(:show).must_equal 'The Great Mind Eraser' + _(cell.call(:show)).must_equal 'The Great Mind Eraser' SongCell.templates.instance_eval do def create; raise; end end # cached, NO new tilt template. - cell.call(:show).must_equal 'The Great Mind Eraser' + _(cell.call(:show)).must_equal 'The Great Mind Eraser' end end diff --git a/test/testing_test.rb b/test/testing_test.rb index d78b35db..c9b6a2c6 100644 --- a/test/testing_test.rb +++ b/test/testing_test.rb @@ -18,18 +18,18 @@ class Cell < Cell::Concept describe "#cell" do subject { cell("test_case_test/song", song) } - it { subject.must_be_instance_of SongCell } - it { subject.model.must_equal song } + it { _(subject).must_be_instance_of SongCell } + it { _(subject.model).must_equal song } - it { cell("test_case_test/song", collection: [song, song]).().must_equal "Give It All!Give It All!" } + it { _(cell("test_case_test/song", collection: [song, song]).()).must_equal "Give It All!Give It All!" } end describe "#concept" do subject { concept("test_case_test/song/cell", song) } - it { subject.must_be_instance_of Song::Cell } - it { subject.model.must_equal song } + it { _(subject).must_be_instance_of Song::Cell } + it { _(subject.model).must_equal song } end end @@ -49,11 +49,11 @@ def show before { Cell::Testing.capybara = true } # yes, a global switch! after { Cell::Testing.capybara = false } - it { subject.(:show).has_selector?('b').must_equal true } + it { _(subject.(:show).has_selector?('b')).must_equal true } - it { cell("capybara_test/capybara", collection: [1, 2]).().has_selector?('b').must_equal true } + it { _(cell("capybara_test/capybara", collection: [1, 2]).().has_selector?('b')).must_equal true } # FIXME: this kinda sucks, what if you want the string in a Capybara environment? - it { subject.(:show).to_s.must_match "Grunt" } + it { _(subject.(:show).to_s).must_match "Grunt" } end end From bbbaffa81c5d785d545dc34604046b0c18d032ca Mon Sep 17 00:00:00 2001 From: Thomas Himbert Date: Fri, 2 Oct 2020 19:32:23 +0200 Subject: [PATCH 14/27] Update documentation links --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 10978577..fae5cead 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ *View Components for Ruby and Rails.* -[![Gitter Chat](https://badges.gitter.im/trailblazer/chat.svg)](https://gitter.im/trailblazer/chat) -[![TRB Newsletter](https://img.shields.io/badge/TRB-newsletter-lightgrey.svg)](http://trailblazer.to/newsletter/) +[![Zulip Chat](https://badges.gitter.im/trailblazer/chat.svg)](https://trailblazer.zulipchat.com/login/) +[![TRB Newsletter](https://img.shields.io/badge/TRB-newsletter-lightgrey.svg)](https://trailblazer.to/2.0/newsletter.html) [![Build Status](https://travis-ci.org/trailblazer/cells.svg)](https://travis-ci.org/trailblazer/cells) [![Gem Version](https://badge.fury.io/rb/cells.svg)](http://badge.fury.io/rb/cells) @@ -12,13 +12,13 @@ Status](https://travis-ci.org/trailblazer/cells.svg)](https://travis-ci.org/trai Cells allow you to encapsulate parts of your UI into components into _view models_. View models, or cells, are simple ruby classes that can render templates. -Nevertheless, a cell gives you more than just a template renderer. They allow proper OOP, polymorphic builders, [nesting](#nested-cells), view inheritance, using Rails helpers, [asset packaging](http://trailblazer.to/gems/cells/rails.html#asset-pipeline) to bundle JS, CSS or images, simple distribution via gems or Rails engines, encapsulated testing, [caching](#caching), and [integrate with Trailblazer](https://github.com/trailblazer/trailblazer-cells). +Nevertheless, a cell gives you more than just a template renderer. They allow proper OOP, polymorphic builders, [nesting](#nested-cells), view inheritance, using Rails helpers, [asset packaging](https://trailblazer.to/2.1/docs/cells.html#cells-rails-asset-pipeline) to bundle JS, CSS or images, simple distribution via gems or Rails engines, encapsulated testing, [caching](#caching), and [integrate with Trailblazer](https://github.com/trailblazer/trailblazer-cells). ## Full Documentation -Cells is part of the Trailblazer framework. [Full documentation](http://trailblazer.to/gems/cells) is available on the project site. +Cells is part of the Trailblazer framework. [Full documentation](https://trailblazer.to/2.1/docs/cells.html) is available on the project site. -Cells is completely decoupled from Rails. However, Rails-specific functionality is to be found [here](http://trailblazer.to/gems/cells/rails.html). +Cells is completely decoupled from Rails. However, Rails-specific functionality is to be found [here](https://trailblazer.to/2.1/docs/cells.html#cells-rails). ## Rendering Cells @@ -151,7 +151,7 @@ Capybara.string(html).must_have_css "h3" It is completely up to you how you test, whether it's RSpec, MiniTest or whatever. All the cell does is return HTML. -[In Rails, there's support](http://trailblazer.to/gems/cells/testing.html) for TestUnit, MiniTest and RSpec available, along with Capybara integration. +[In Rails, there's support](https://trailblazer.to/2.1/docs/cells.html#cells-testing) for TestUnit, MiniTest and RSpec available, along with Capybara integration. ## Properties @@ -182,7 +182,7 @@ song.title #=> "" Comment::Cell.(song).title #=> <script>Dangerous</script> ``` -Properties and escaping are [documented here](http://trailblazer.to/gems/cells/api.html#html-escaping). +Properties and escaping are [documented here](https://trailblazer.to/2.1/docs/cells.html#cells-api-html-escaping). ## Installation @@ -270,7 +270,7 @@ end ## Asset Packaging -Cells can easily ship with their own JavaScript, CSS and more and be part of Rails' asset pipeline. Bundling assets into a cell allows you to implement super encapsulated widgets that are stand-alone. Asset pipeline is [documented here](http://trailblazer.to/gems/cells/rails.html#asset-pipeline). +Cells can easily ship with their own JavaScript, CSS and more and be part of Rails' asset pipeline. Bundling assets into a cell allows you to implement super encapsulated widgets that are stand-alone. Asset pipeline is [documented here](https://trailblazer.to/2.1/docs/cells.html#cells-rails-asset-pipeline). ## Render API @@ -344,7 +344,7 @@ This works both in cell views and on the instance, in states. ## View Inheritance -You can not only inherit code across cell classes, but also views. This is extremely helpful if you want to override parts of your UI, only. It's [documented here](http://trailblazer.to/gems/cells/api.html#view-inheritance). +You can not only inherit code across cell classes, but also views. This is extremely helpful if you want to override parts of your UI, only. It's [documented here](https://trailblazer.to/2.1/docs/cells.html#cells-api-view-inheritance). ## Collections @@ -357,7 +357,7 @@ cell(:comment, collection: comments).() This will invoke `cell(:comment, comment).()` three times and concatenate the rendered output automatically. -Learn more [about collections here](http://trailblazer.to/gems/cells/api.html#collection). +Learn more [about collections here](https://trailblazer.to/2.1/docs/cells.html#cells-api-collection). ## Builder @@ -382,7 +382,7 @@ The `#cell` helper takes care of instantiating the right cell class for you. cell(:comment, Post.find(1)) #=> creates a PostCell. ``` -Learn more [about builders here](http://trailblazer.to/gems/cells/api.html#builder). +Learn more [about builders here](https://trailblazer.to/2.1/docs/cells.html#cells-api-builder). ## Caching @@ -401,7 +401,7 @@ The `::cache` method will forward options to the caching engine. cache :show, expires_in: 10.minutes ``` -You can also compute your own cache key, use dynamic keys, cache tags, and conditionals using `:if`. Caching is documented [here](http://trailblazer.to/gems/cells/api.html#caching) and in chapter 8 of the [Trailblazer book](http://leanpub.com/trailblazer). +You can also compute your own cache key, use dynamic keys, cache tags, and conditionals using `:if`. Caching is documented [here](https://trailblazer.to/2.1/docs/cells.html#cells-api-caching) and in chapter 8 of the [Trailblazer book](http://leanpub.com/trailblazer). ## The Book @@ -422,11 +422,11 @@ The book picks up where the README leaves off. Go grab a copy and support us - i ## This is not Cells 3.x! -Temporary note: This is the README and API for Cells 4. Many things have improved. If you want to upgrade, [follow this guide](https://github.com/apotonick/cells/wiki/From-Cells-3-to-Cells-4---Upgrading-Guide). When in trouble, join the [Gitter channel](https://gitter.im/trailblazer/chat). +Temporary note: This is the README and API for Cells 4. Many things have improved. If you want to upgrade, [follow this guide](https://github.com/apotonick/cells/wiki/From-Cells-3-to-Cells-4---Upgrading-Guide). When in trouble, join the [Zulip channel](https://trailblazer.zulipchat.com/login/). ## LICENSE -Copyright (c) 2007-2015, Nick Sutterer +Copyright (c) 2007-2020, Nick Sutterer Copyright (c) 2007-2008, Solide ICT by Peter Bex and Bob Leers From 595baa75289d6cead3f1622c68f6f160ffcd50a1 Mon Sep 17 00:00:00 2001 From: Yogesh Khater Date: Sun, 30 May 2021 19:55:08 +0530 Subject: [PATCH 15/27] Setup GH action CI --- .github/workflows/ci.yml | 17 +++++++++++++++++ .travis.yml | 8 -------- 2 files changed, 17 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..52966690 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,17 @@ +name: CI +on: [push, pull_request] +jobs: + test: + strategy: + fail-fast: false + matrix: + # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0' + ruby: [2.5, 2.6, 2.7, '3.0', head] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true # runs 'bundle install' and caches installed gems automatically + - run: bundle exec rake diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 850177f2..00000000 --- a/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -language: ruby -before_install: gem install bundler -rvm: - - 2.7 - - 2.6 - - 2.5 - -cache: bundler From e20413e1e01fb1ea01c456ca8d2b32651cd83770 Mon Sep 17 00:00:00 2001 From: Yogesh Khater Date: Fri, 11 Jun 2021 08:56:57 +0530 Subject: [PATCH 16/27] Use trailblazer-option over declarative-option and upgrade declarative-builder (#492) --- cells.gemspec | 4 +-- lib/cell/caching.rb | 18 +++++------ lib/cell/option.rb | 35 +++++++++++++++++++++ test/cache_test.rb | 76 +++++++++++++++++++++++++++++++++++++++------ 4 files changed, 111 insertions(+), 22 deletions(-) create mode 100644 lib/cell/option.rb diff --git a/cells.gemspec b/cells.gemspec index cf140df6..635a47ee 100644 --- a/cells.gemspec +++ b/cells.gemspec @@ -18,8 +18,8 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.required_ruby_version = ">= 2.2.10" - spec.add_dependency "declarative-builder", "< 0.2.0" - spec.add_dependency "declarative-option", "< 0.2.0" + spec.add_dependency "declarative-builder", "~> 0.2.0" + spec.add_dependency "trailblazer-option", "~> 0.1.0" spec.add_dependency "tilt", ">= 1.4", "< 3" spec.add_dependency "uber", "< 0.2.0" diff --git a/lib/cell/caching.rb b/lib/cell/caching.rb index f72ea99c..10d97d8d 100644 --- a/lib/cell/caching.rb +++ b/lib/cell/caching.rb @@ -1,4 +1,4 @@ -require "declarative/options" +require "cell/option" module Cell module Caching @@ -17,12 +17,10 @@ def self.included(includer) end module ClassMethods - def cache(state, *args, &block) - options = args.last.is_a?(Hash) ? args.pop : {} # I have to admit, Array#extract_options is a brilliant tool. - - conditional_procs[state] = Declarative::Option(options.delete(:if) || true, instance_exec: true) - version_procs[state] = Declarative::Option(args.first || block, instance_exec: true) - cache_options[state] = Declarative::Options(options, instance_exec: true) + def cache(state, *args, **kwargs, &block) + conditional_procs[state] = Cell::Option(kwargs.delete(:if) || true) + version_procs[state] = Cell::Option(args.first || block) + cache_options[state] = Cell::Options(kwargs) end # Computes the complete, namespaced cache key for +state+. @@ -45,8 +43,8 @@ def render_state(state, *args) state = state.to_sym return super(state, *args) unless cache?(state, *args) - key = self.class.state_cache_key(state, self.class.version_procs[state].(self, *args)) - options = self.class.cache_options[state].(self, *args) + key = self.class.state_cache_key(state, self.class.version_procs[state].(*args, exec_context: self)) + options = self.class.cache_options[state].(*args, exec_context: self) fetch_from_cache_for(key, options) { super(state, *args) } end @@ -56,7 +54,7 @@ def cache_store # we want to use DI to set a cache store in cell/rails. end def cache?(state, *args) - perform_caching? and state_cached?(state) and self.class.conditional_procs[state].(self, *args) + perform_caching? and state_cached?(state) and self.class.conditional_procs[state].(*args, exec_context: self) end private diff --git a/lib/cell/option.rb b/lib/cell/option.rb new file mode 100644 index 00000000..316fabb8 --- /dev/null +++ b/lib/cell/option.rb @@ -0,0 +1,35 @@ +require "trailblazer/option" +require "uber/callable" + +module Cell + # Extend `Trailblazer::Option` to make static values as callables too. + class Option < ::Trailblazer::Option + def self.build(value) + callable = case value + when Proc, Symbol, Uber::Callable + value + else + ->(*) { value } # Make non-callable value to callable. + end + + super(callable) + end + end + + class Options < Hash + # Evaluates every element and returns a hash. Accepts arbitrary arguments. + def call(*args, **options, &block) + Hash[ collect { |k,v| [k,v.(*args, **options, &block) ] } ] + end + end + + def self.Option(value) + ::Cell::Option.build(value) + end + + def self.Options(options) + Options.new.tap do |hsh| + options.each { |k,v| hsh[k] = Option(v) } + end + end +end diff --git a/test/cache_test.rb b/test/cache_test.rb index 8385d957..5e671a3c 100644 --- a/test/cache_test.rb +++ b/test/cache_test.rb @@ -1,7 +1,5 @@ require "test_helper" -# TODO: test caching without rails - class CacheTest < Minitest::Spec STORE = Class.new(Hash) do def fetch(key, options, &block) @@ -10,23 +8,81 @@ def fetch(key, options, &block) end.new module Cache - def show + def show(*) "#{@model}" end def cache_store STORE end + + def has_changed?(*) + @model < 3 + end end - class Index < Cell::ViewModel - cache :show - include Cache + Component = ->(*args, **kwargs, &block) { + Class.new(Cell::ViewModel) do + cache :show, *args, **kwargs, &block + include Cache + end + } + + it "without any options" do + WithoutOptions = Component.() + + _(WithoutOptions.new(1).()).must_equal("1") + _(WithoutOptions.new(2).()).must_equal("1") end - it do - _(Index.new(1).()).must_equal("1") - _(Index.new(2).()).must_equal("1") + it "with specified version" do + version = ->(options) { options[:version] } + + # Cache invalidation using version as a proc + WithVersionArg = Component.(version) + + _(WithVersionArg.new(1).(:show, version: 1)).must_equal("1") + _(WithVersionArg.new(2).(:show, version: 1)).must_equal("1") + + _(WithVersionArg.new(3).(:show, version: 2)).must_equal("3") + + # Cache invalidation using version as a block + WithVersionBlock = Component.(&version) + + _(WithVersionBlock.new(1).(:show, version: 1)).must_equal("1") + _(WithVersionBlock.new(2).(:show, version: 1)).must_equal("1") + + _(WithVersionBlock.new(3).(:show, version: 2)).must_equal("3") end -end + it "with conditional" do + WithConditional = Component.(if: :has_changed?) + + _(WithConditional.new(1).()).must_equal("1") + _(WithConditional.new(2).()).must_equal("1") + + _(WithConditional.new(3).()).must_equal("3") + end + + it "forwards remaining options to cache store" do + WithOptions = Class.new(Cell::ViewModel) do + cache :show, if: :has_changed?, expires_in: 10, tags: ->(*args) { Hash(args.first)[:tags] } + include Cache + + CACHE_WITH_OPTIONS_STORE = Class.new(Hash) do + def fetch(key, options) + value = self[key] || self[key] = yield + [value, options] + end + end.new + + def cache_store + CACHE_WITH_OPTIONS_STORE + end + end + + _(WithOptions.new(1).()).must_equal(%{["1", {:expires_in=>10, :tags=>nil}]}) + _(WithOptions.new(2).()).must_equal(%{["1", {:expires_in=>10, :tags=>nil}]}) + _(WithOptions.new(2).(:show, tags: [:a, :b])).must_equal(%{["1", {:expires_in=>10, :tags=>[:a, :b]}]}) + end +end From 307040465375987f32c860f0d8a19ba45ef87877 Mon Sep 17 00:00:00 2001 From: Peter Goldstein Date: Mon, 17 Jan 2022 23:30:04 -0800 Subject: [PATCH 17/27] Add Ruby 3.1 to CI (#495) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 52966690..dc99d1e1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ jobs: fail-fast: false matrix: # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0' - ruby: [2.5, 2.6, 2.7, '3.0', head] + ruby: [2.5, 2.6, 2.7, '3.0', 3.1, head] runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 From 8eae3a1329bacfc6b48d3f05afa0bc6b89c1d784 Mon Sep 17 00:00:00 2001 From: Nick Sutterer Date: Tue, 1 Feb 2022 14:42:18 +0100 Subject: [PATCH 18/27] Expect {*args, **kws} everywhere so this code is *really* Ruby 3 safe. In `Caching`, we compile `cache_fillter_args` from `args` and `kws`. As per the existing implementation, cache filters do not expect/can't receive keyword arguments anyway. --- lib/cell/caching.rb | 21 +++++++++++++-------- lib/cell/view_model.rb | 12 ++++++------ test/cache_test.rb | 5 +++++ 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/cell/caching.rb b/lib/cell/caching.rb index 10d97d8d..d4fca400 100644 --- a/lib/cell/caching.rb +++ b/lib/cell/caching.rb @@ -17,10 +17,10 @@ def self.included(includer) end module ClassMethods - def cache(state, *args, **kwargs, &block) - conditional_procs[state] = Cell::Option(kwargs.delete(:if) || true) + def cache(state, *args, **kws, &block) + conditional_procs[state] = Cell::Option(kws.delete(:if) || true) version_procs[state] = Cell::Option(args.first || block) - cache_options[state] = Cell::Options(kwargs) + cache_options[state] = Cell::Options(kws) end # Computes the complete, namespaced cache key for +state+. @@ -39,14 +39,19 @@ def expand_cache_key(key) end end - def render_state(state, *args) + def render_state(state, *args, **kws) state = state.to_sym - return super(state, *args) unless cache?(state, *args) - key = self.class.state_cache_key(state, self.class.version_procs[state].(*args, exec_context: self)) - options = self.class.cache_options[state].(*args, exec_context: self) + # Before Ruby 3.0, this wasn't necessary, but since cache filters don't receive kwargs as per the "old" (existing cells version) implementation, we can make it one array. + cache_filter_args = args + [kws] - fetch_from_cache_for(key, options) { super(state, *args) } + return super(state, *args, **kws) unless cache?(state, *cache_filter_args) + + + key = self.class.state_cache_key(state, self.class.version_procs[state].(*cache_filter_args, exec_context: self)) + options = self.class.cache_options[state].(*cache_filter_args, exec_context: self) + + fetch_from_cache_for(key, options) { super(state, *cache_filter_args) } end def cache_store # we want to use DI to set a cache store in cell/rails. diff --git a/lib/cell/view_model.rb b/lib/cell/view_model.rb index 2ac6b305..d4a27f24 100644 --- a/lib/cell/view_model.rb +++ b/lib/cell/view_model.rb @@ -28,9 +28,9 @@ def self.controller_path module Helpers # Constantizes name if needed, call builders and returns instance. - def cell(name, *args, &block) # classic Rails fuzzy API. + def cell(name, *args, **kws, &block) # classic Rails fuzzy API. constant = name.is_a?(Class) ? name : class_from_cell_name(name) - constant.(*args, &block) + constant.(*args, **kws, &block) end end extend Helpers @@ -88,8 +88,8 @@ def self.[](options, context) module Rendering # Invokes the passed method (defaults to :show) while respecting caching. # In Rails, the return value gets marked html_safe. - def call(state=:show, *args, &block) - content = render_state(state, *args, &block) + def call(state=:show, *args, **kws, &block) + content = render_state(state, *args, **kws, &block) content.to_s end @@ -110,8 +110,8 @@ def render_to_string(options, &block) render_template(template, options, &block) end - def render_state(*args, &block) - __send__(*args, &block) + def render_state(*args, **kws, &block) + __send__(*args, **kws, &block) end def render_template(template, options, &block) diff --git a/test/cache_test.rb b/test/cache_test.rb index 5e671a3c..b3a67f3b 100644 --- a/test/cache_test.rb +++ b/test/cache_test.rb @@ -67,6 +67,8 @@ def has_changed?(*) it "forwards remaining options to cache store" do WithOptions = Class.new(Cell::ViewModel) do cache :show, if: :has_changed?, expires_in: 10, tags: ->(*args) { Hash(args.first)[:tags] } + ## We can use kwargs in the cache key filter + # cache :new, expires_in: 10, tags: ->(*, my_tags:, **) { my_tags } # FIXME: allow this in Cells 5. include Cache CACHE_WITH_OPTIONS_STORE = Class.new(Hash) do @@ -84,5 +86,8 @@ def cache_store _(WithOptions.new(1).()).must_equal(%{["1", {:expires_in=>10, :tags=>nil}]}) _(WithOptions.new(2).()).must_equal(%{["1", {:expires_in=>10, :tags=>nil}]}) _(WithOptions.new(2).(:show, tags: [:a, :b])).must_equal(%{["1", {:expires_in=>10, :tags=>[:a, :b]}]}) + + # FIXME: allow this in Cells 5. + # _(WithOptions.new(2).(:new, my_tags: [:a, :b])).must_equal(%{["1", {:expires_in=>10, :tags=>[:a, :b]}]}) end end From 88796e3a29cbd07887a6161e721164a75c3218a7 Mon Sep 17 00:00:00 2001 From: Nick Sutterer Date: Tue, 1 Feb 2022 15:25:50 +0100 Subject: [PATCH 19/27] two versions of `#render_state` for Ruby <2.7 and after. --- lib/cell/view_model.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/cell/view_model.rb b/lib/cell/view_model.rb index d4a27f24..118366de 100644 --- a/lib/cell/view_model.rb +++ b/lib/cell/view_model.rb @@ -111,15 +111,23 @@ def render_to_string(options, &block) end def render_state(*args, **kws, &block) - __send__(*args, **kws, &block) + __send__(*args, **kws, &block) # Ruby 2.7+ end def render_template(template, options, &block) template.render(self, options[:locals], &block) # DISCUSS: hand locals to layout? end + + module RubyPre2_7_RenderState + def render_state(*args, **kws, &block) + args = args + [kws] if kws.any? + __send__(*args, &block) + end + end end include Rendering + include Rendering::RubyPre2_7_RenderState if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7.0') include Inspect def to_s From b7c4b1fae3850c2b7bc1748aaaeccf5690378a5c Mon Sep 17 00:00:00 2001 From: Yogesh Khater Date: Tue, 7 Jun 2022 16:17:15 +0530 Subject: [PATCH 20/27] Forward args in ruby3 style after cache hit (#497) --- lib/cell/caching.rb | 2 +- test/cache_test.rb | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/cell/caching.rb b/lib/cell/caching.rb index d4fca400..3cd516af 100644 --- a/lib/cell/caching.rb +++ b/lib/cell/caching.rb @@ -51,7 +51,7 @@ def render_state(state, *args, **kws) key = self.class.state_cache_key(state, self.class.version_procs[state].(*cache_filter_args, exec_context: self)) options = self.class.cache_options[state].(*cache_filter_args, exec_context: self) - fetch_from_cache_for(key, options) { super(state, *cache_filter_args) } + fetch_from_cache_for(key, options) { super(state, *args, **kws) } end def cache_store # we want to use DI to set a cache store in cell/rails. diff --git a/test/cache_test.rb b/test/cache_test.rb index b3a67f3b..786032fe 100644 --- a/test/cache_test.rb +++ b/test/cache_test.rb @@ -90,4 +90,24 @@ def cache_store # FIXME: allow this in Cells 5. # _(WithOptions.new(2).(:new, my_tags: [:a, :b])).must_equal(%{["1", {:expires_in=>10, :tags=>[:a, :b]}]}) end + + it "forwards all arguments to renderer after cache hit" do + SongCell = Class.new(Cell::ViewModel) do + cache :show + + def show(type, title:, part:, **) + "#{type} #{title} #{part}" + end + + def cache_store + STORE + end + end + + # cache miss for the first render + _(SongCell.new.(:show, "Album", title: "IT", part: "1")).must_equal("Album IT 1") + + # cache hit for the second render + _(SongCell.new.(:show, "Album", title: "IT", part: "1")).must_equal("Album IT 1") + end end From 93add13e4794838eef479d75dc7b985fd5e900a9 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Tue, 6 Dec 2022 22:44:16 +0100 Subject: [PATCH 21/27] fix Gemfile --- cells.gemspec | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/cells.gemspec b/cells.gemspec index 635a47ee..04deab94 100644 --- a/cells.gemspec +++ b/cells.gemspec @@ -1,30 +1,29 @@ -lib = File.expand_path("lib", __dir__) -$LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib) +# frozen_string_literal: true -require "cell/version" +require_relative 'lib/cell/version' Gem::Specification.new do |spec| - spec.name = "cells" + spec.name = 'cells' spec.version = Cell::VERSION - spec.authors = ["Nick Sutterer"] - spec.email = ["apotonick@gmail.com"] - spec.homepage = "https://github.com/apotonick/cells" - spec.summary = "View Models for Ruby and Rails." - spec.description = "View Models for Ruby and Rails, replacing helpers and partials while giving you a clean view architecture with proper encapsulation." - spec.license = "MIT" + spec.authors = ['Nick Sutterer'] + spec.email = ['apotonick@gmail.com'] + spec.homepage = 'https://github.com/trailblazer/cells' + spec.summary = 'View Models for Ruby and Rails.' + spec.description = 'View Models for Ruby and Rails, replacing helpers and partials while giving you a clean view architecture with proper encapsulation.' + spec.license = 'MIT' spec.files = `git ls-files`.split("\n") spec.test_files = `git ls-files -- {test}/*`.split("\n") - spec.require_paths = ["lib"] - spec.required_ruby_version = ">= 2.2.10" + spec.require_paths = ['lib'] + spec.required_ruby_version = '>= 2.2.10' - spec.add_dependency "declarative-builder", "~> 0.2.0" - spec.add_dependency "trailblazer-option", "~> 0.1.0" - spec.add_dependency "tilt", ">= 1.4", "< 3" - spec.add_dependency "uber", "< 0.2.0" + spec.add_dependency 'declarative-builder', '~> 0.2.0' + spec.add_dependency 'tilt', '>= 1.4', '< 3' + spec.add_dependency 'trailblazer-option', '~> 0.1.0' + spec.add_dependency 'uber', '< 0.2.0' - spec.add_development_dependency "capybara" - spec.add_development_dependency "cells-erb", ">= 0.1.0" - spec.add_development_dependency "minitest" - spec.add_development_dependency "rake" + spec.add_development_dependency 'capybara' + spec.add_development_dependency 'cells-erb', '>= 0.1.0' + spec.add_development_dependency 'minitest' + spec.add_development_dependency 'rake' end From b437c5b56ba357d88815a66fb4c0e636e21fcdb9 Mon Sep 17 00:00:00 2001 From: Orien Madgwick <497874+orien@users.noreply.github.com> Date: Tue, 17 Jan 2023 06:02:54 +1100 Subject: [PATCH 22/27] CI: add Ruby 3.2 to the test matrix --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dc99d1e1..51998eb8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ jobs: fail-fast: false matrix: # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0' - ruby: [2.5, 2.6, 2.7, '3.0', 3.1, head] + ruby: [2.5, 2.6, 2.7, '3.0', 3.1, 3.2, head] runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 From 75bbcc0906ed7059029928134ac60bb64174a24b Mon Sep 17 00:00:00 2001 From: Orien Madgwick <497874+orien@users.noreply.github.com> Date: Tue, 17 Jan 2023 06:11:47 +1100 Subject: [PATCH 23/27] Add project metadata to the gemspec As per https://guides.rubygems.org/specification-reference/#metadata, add metadata to the gemspec file. This'll allow people to more easily access the source code, raise issues and read the changelog. These bug_tracker_uri, changelog_uri, documentation_uri, homepage_uri, source_code_uri, and wiki_uri links will appear on the rubygems page at https://rubygems.org/gems/cells and be available via the Rubygems API after the next release. --- cells.gemspec | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cells.gemspec b/cells.gemspec index 04deab94..556fa7dd 100644 --- a/cells.gemspec +++ b/cells.gemspec @@ -12,6 +12,13 @@ Gem::Specification.new do |spec| spec.description = 'View Models for Ruby and Rails, replacing helpers and partials while giving you a clean view architecture with proper encapsulation.' spec.license = 'MIT' + spec.metadata['bug_tracker_uri'] = "#{spec.homepage}/issues" + spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/HEAD/CHANGES.md" + spec.metadata['documentation_uri'] = 'https://trailblazer.to/2.1/docs/cells' + spec.metadata['homepage_uri'] = spec.homepage + spec.metadata['source_code_uri'] = spec.homepage + spec.metadata['wiki_uri'] = "#{spec.homepage}/wiki" + spec.files = `git ls-files`.split("\n") spec.test_files = `git ls-files -- {test}/*`.split("\n") spec.require_paths = ['lib'] From 26ddc436e828cb2527be7cd2d0d1bb5e8b10b868 Mon Sep 17 00:00:00 2001 From: Orien Madgwick <497874+orien@users.noreply.github.com> Date: Tue, 21 Feb 2023 23:30:55 +1100 Subject: [PATCH 24/27] Remove development and test files from the gem package (#503) There are several files in the gem package that aren't useful for downstream projects. Removing these files reduces the gem package size from 34K to 24K! --- cells.gemspec | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cells.gemspec b/cells.gemspec index 556fa7dd..6ccfe9a9 100644 --- a/cells.gemspec +++ b/cells.gemspec @@ -19,8 +19,11 @@ Gem::Specification.new do |spec| spec.metadata['source_code_uri'] = spec.homepage spec.metadata['wiki_uri'] = "#{spec.homepage}/wiki" - spec.files = `git ls-files`.split("\n") - spec.test_files = `git ls-files -- {test}/*`.split("\n") + spec.files = Dir.chdir(__dir__) do + `git ls-files -z`.split("\x0").reject do |file| + file.start_with?(*%w[.git Gemfile Rakefile TODO test]) + end + end spec.require_paths = ['lib'] spec.required_ruby_version = '>= 2.2.10' From 1fb75e0a9d4f842e5fdd87a51392681879b72f9d Mon Sep 17 00:00:00 2001 From: Taketo Takashima Date: Thu, 2 Mar 2023 11:22:10 +0900 Subject: [PATCH 25/27] Added cache test with cache condtion helper method --- test/cache_test.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/cache_test.rb b/test/cache_test.rb index 786032fe..35859907 100644 --- a/test/cache_test.rb +++ b/test/cache_test.rb @@ -110,4 +110,24 @@ def cache_store # cache hit for the second render _(SongCell.new.(:show, "Album", title: "IT", part: "1")).must_equal("Album IT 1") end + + it "with cache condition helper method" do + WithCondition = Class.new(Cell::ViewModel) do + cache :show, if: :enable_cache?, expires_in: 10 + + def show + "Test" + end + + def cache_store + STORE + end + + def enable_cache? + true + end + end + + _(WithCondition.new.(:show)).must_equal("Test") + end end From a31a3f4f4b6ba36092d6413ddad091436a74e065 Mon Sep 17 00:00:00 2001 From: Taketo Takashima Date: Thu, 2 Mar 2023 11:23:00 +0900 Subject: [PATCH 26/27] Fixed caching render_state method with keyword args --- lib/cell/caching.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cell/caching.rb b/lib/cell/caching.rb index 3cd516af..05965976 100644 --- a/lib/cell/caching.rb +++ b/lib/cell/caching.rb @@ -43,7 +43,7 @@ def render_state(state, *args, **kws) state = state.to_sym # Before Ruby 3.0, this wasn't necessary, but since cache filters don't receive kwargs as per the "old" (existing cells version) implementation, we can make it one array. - cache_filter_args = args + [kws] + cache_filter_args = args + [**kws] return super(state, *args, **kws) unless cache?(state, *cache_filter_args) From 23984c823e5168dce1821929967170b8b2e710ff Mon Sep 17 00:00:00 2001 From: Taketo Takashima Date: Thu, 2 Mar 2023 17:08:07 +0900 Subject: [PATCH 27/27] Fixed caching render_state method with keyword args for ruby < 2.7 --- lib/cell/caching.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/cell/caching.rb b/lib/cell/caching.rb index 05965976..149719f1 100644 --- a/lib/cell/caching.rb +++ b/lib/cell/caching.rb @@ -43,7 +43,12 @@ def render_state(state, *args, **kws) state = state.to_sym # Before Ruby 3.0, this wasn't necessary, but since cache filters don't receive kwargs as per the "old" (existing cells version) implementation, we can make it one array. - cache_filter_args = args + [**kws] + if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.7.0') + cache_filter_args = args + [**kws] + else + cache_filter_args = args + cache_filter_args += [**kws] if kws.size > 0 + end return super(state, *args, **kws) unless cache?(state, *cache_filter_args)