diff --git a/lib/trestle/resource/builder.rb b/lib/trestle/resource/builder.rb index 26e23c81..67a2daaf 100644 --- a/lib/trestle/resource/builder.rb +++ b/lib/trestle/resource/builder.rb @@ -76,14 +76,16 @@ def count(&block) admin.define_adapter_method(:count, &block) end - def scopes(options={}, &block) - admin.scopes.options.merge!(options) - admin.scopes.append(options, &block) if block_given? + def scopes(defaults: {}, **options, &block) + defaults = defaults.merge(options.slice(:count)) + + admin.scopes.apply_options!(options) + admin.scopes.append(**defaults, &block) if block_given? end - def scope(name, scope=nil, options={}, &block) + def scope(name, scope=nil, **options, &block) scopes do - scope(name, scope, options, &block) + scope(name, scope, **options, &block) end end diff --git a/lib/trestle/scopes/block.rb b/lib/trestle/scopes/block.rb index a8b13ae1..32915f29 100644 --- a/lib/trestle/scopes/block.rb +++ b/lib/trestle/scopes/block.rb @@ -1,37 +1,33 @@ module Trestle class Scopes class Block - attr_reader :block, :options + attr_reader :block, :defaults - def initialize(options={}, &block) - @options, @block = options, block + def initialize(**defaults, &block) + @defaults, @block = defaults, block end # Evaluates the scope block within the given admin context # and returns an array of the scopes that were defined. def scopes(context) - context = Evaluator.new(context, options) + context = Evaluator.new(context, **defaults) context.instance_exec(context, &block) context.scopes end + protected class Evaluator include EvaluationContext attr_reader :scopes - def initialize(context=nil, defaults={}) + def initialize(context=nil, **defaults) @context, @defaults = context, defaults @scopes = [] end - def scope(name, scope=nil, options={}, &block) - if scope.is_a?(Hash) - options = scope - scope = nil - end - - scopes << Scope.new(@context, name, @defaults.merge(options), &(scope || block)) + def scope(name, scope=nil, **options, &block) + scopes << Scope.new(@context, name, **@defaults.merge(options), &(scope || block)) end end end diff --git a/lib/trestle/scopes/definition.rb b/lib/trestle/scopes/definition.rb index 2c9a2b9a..6c63559a 100644 --- a/lib/trestle/scopes/definition.rb +++ b/lib/trestle/scopes/definition.rb @@ -8,8 +8,12 @@ def initialize @options = {} end - def append(options={}, &block) - @blocks << Block.new(options, &block) + def append(**defaults, &block) + @blocks << Block.new(**defaults, &block) + end + + def apply_options!(options) + @options.merge!(options) end # Evaluates each of the scope blocks within the given admin context diff --git a/lib/trestle/scopes/scope.rb b/lib/trestle/scopes/scope.rb index 46ec6682..55444a67 100644 --- a/lib/trestle/scopes/scope.rb +++ b/lib/trestle/scopes/scope.rb @@ -1,10 +1,11 @@ module Trestle class Scopes class Scope - attr_reader :name, :options, :block + attr_reader :name, :group, :block - def initialize(admin, name, options={}, &block) - @admin, @name, @options, @block = admin, name, options, block + def initialize(admin, name, label: nil, group: nil, default: false, count: true, &block) + @admin, @name, @block = admin, name, block + @label, @group, @default, @count = label, group, default, count end def to_param @@ -12,19 +13,15 @@ def to_param end def label - @options[:label] || @admin.t("scopes.#{name}", default: name.to_s.humanize.titleize) - end - - def group - @options[:group] + @label || default_label end def default? - @options[:default] == true + @default end def count? - @options[:count] != false + @count end def apply(collection) @@ -40,6 +37,7 @@ def apply(collection) end def count(collection) + return unless count? @admin.count(@admin.merge_scopes(collection, apply(collection))) end @@ -52,6 +50,11 @@ def active?(params) default? end end + + protected + def default_label + @admin.t("scopes.#{name}", default: name.to_s.humanize.titleize) + end end end end diff --git a/sandbox/app/admin/users_admin.rb b/sandbox/app/admin/users_admin.rb index 09c47950..289b364b 100644 --- a/sandbox/app/admin/users_admin.rb +++ b/sandbox/app/admin/users_admin.rb @@ -7,6 +7,14 @@ model.alphabetical.includes(:office) end + scopes layout: :columns do + Office.order(country: :asc, city: :asc).each do |office| + scope office.city, group: office.country do + office.users + end + end + end + table do column :avatar, header: false, align: :center do |user| avatar(fallback: user.initials, style: "background: #{user.avatar_color}") { gravatar(user.email, d: user.avatar_type_value) } diff --git a/sandbox/db/seeds.rb b/sandbox/db/seeds.rb index f1d40b63..e2c79864 100644 --- a/sandbox/db/seeds.rb +++ b/sandbox/db/seeds.rb @@ -1,7 +1,9 @@ offices = Office.create!([ { city: "London", country: "United Kingdom", address_1: "97 Crown Street", address_2: "", phone: "077 3677 3986", url: "https://www.example.co.uk" }, + { city: "Austin", country: "United States", address_1: "3095 Brentwood Drive", address_2: "", phone: "512-836-3538", url: "https://www.example.com" }, { city: "San Francisco", country: "United States", address_1: "3617 Delaware Avenue", address_2: "", phone: "415-335-2512", url: "https://www.example.com" }, { city: "New York", country: "United States", address_1: "2442 Geneva Street", address_2: "", phone: "917-437-9093", url: "https://www.example.com" }, + { city: "Melbourne", country: "Australia", address_1: "151/153 Brunswick St", address_2: "", phone: "(03) 9417 3434", url: "https://www.example.com.au" }, { city: "Sydney", country: "Australia", address_1: "79 Ocean Street", address_2: "", phone: "(02) 8595 0018", url: "https://www.example.com.au" }, { city: "Auckland", country: "New Zealand", address_1: "278 Steele Street", address_2: "", phone: "(021) 2545-544", url: "https://www.example.co.nz" }, { city: "Singapore", country: "Singapore", address_1: "155 North Bridge Road", address_2: "#26-01 Peninsula Plaza", phone: "65-6336 4010", url: "https://www.example.sg" } diff --git a/spec/trestle/resource/builder_spec.rb b/spec/trestle/resource/builder_spec.rb index 52d6c1e6..167fed10 100644 --- a/spec/trestle/resource/builder_spec.rb +++ b/spec/trestle/resource/builder_spec.rb @@ -345,7 +345,7 @@ class TestDecorator; end scope = admin.scopes.first expect(scope.name).to eq(:my_scope) - expect(scope.options).to eq(label: "Custom Label") + expect(scope.label).to eq("Custom Label") expect(scope.block).to eq(b) end @@ -364,7 +364,7 @@ class TestDecorator; end scope = admin.scopes.first expect(scope.name).to eq(:my_scope) - expect(scope.options).to eq(label: "Custom Label") + expect(scope.label).to eq("Custom Label") expect(scope.block).to eq(b) end end @@ -387,7 +387,7 @@ class TestDecorator; end scope = admin.scopes.first expect(scope.name).to eq(:my_scope) - expect(scope.options).to eq(label: "Custom Label") + expect(scope.label).to eq("Custom Label") expect(scope.block).to eq(b) end diff --git a/spec/trestle/scopes/block_spec.rb b/spec/trestle/scopes/block_spec.rb index dd84231d..6037df27 100644 --- a/spec/trestle/scopes/block_spec.rb +++ b/spec/trestle/scopes/block_spec.rb @@ -5,7 +5,7 @@ let(:options) { {} } subject(:block) do - Trestle::Scopes::Block.new(options) do + Trestle::Scopes::Block.new(**options) do scope :first scope :second, count: false end @@ -16,21 +16,21 @@ scope1, scope2 = block.scopes(admin) expect(scope1.name).to eq(:first) - expect(scope1.options).to eq({}) + expect(scope1.count?).to be true expect(scope2.name).to eq(:second) - expect(scope2.options).to eq({ count: false }) + expect(scope2.count?).to be false end - end - context "with options" do - let(:options) { { count: false } } + context "with options on the block" do + let(:options) { { count: false } } - it "applies the block options to each scope" do - scope1, scope2 = block.scopes(admin) + it "applies the block options as defaults to each scope" do + scope1, scope2 = block.scopes(admin) - expect(scope1.options).to eq({ count: false }) - expect(scope2.options).to eq({ count: false }) + expect(scope1.count?).to be false + expect(scope2.count?).to be false + end end end end diff --git a/spec/trestle/scopes/scope_spec.rb b/spec/trestle/scopes/scope_spec.rb index 88d8eb89..89b649d2 100644 --- a/spec/trestle/scopes/scope_spec.rb +++ b/spec/trestle/scopes/scope_spec.rb @@ -5,7 +5,7 @@ let(:options) { {} } let(:block) { nil } - subject(:scope) { Trestle::Scopes::Scope.new(admin, :my_scope, options, &block) } + subject(:scope) { Trestle::Scopes::Scope.new(admin, :my_scope, **options, &block) } describe "#to_param" do it "returns the scope name" do @@ -114,34 +114,30 @@ end describe "#default?" do - context "with options[:default] = true" do + it "returns false if default option not specified" do + expect(scope.default?).to be false + end + + context "with default: true" do let(:options) { { default: true } } it "returns true" do expect(scope.default?).to be true end end - - context "without options[:default]" do - it "returns false" do - expect(scope.default?).to be false - end - end end describe "#count?" do - context "with options[:count] = false" do + it "returns true if count option not specified" do + expect(scope.count?).to be true + end + + context "with count: false" do let(:options) { { count: false } } it "returns true" do expect(scope.count?).to be false end end - - context "without options[:count]" do - it "returns true by default" do - expect(scope.count?).to be true - end - end end end diff --git a/spec/trestle/scopes_spec.rb b/spec/trestle/scopes_spec.rb index 0e9a4806..df7dcc8c 100644 --- a/spec/trestle/scopes_spec.rb +++ b/spec/trestle/scopes_spec.rb @@ -6,7 +6,7 @@ subject(:scopes) { Trestle::Scopes.new(definition, self) } - before(:each) { definition.options.merge!(options) } + before(:each) { definition.apply_options!(options) } describe "#classes" do it "includes scopes" do