diff --git a/lib/generators/rails/content/templates/model.rb.tt b/lib/generators/rails/content/templates/model.rb.tt index 92df3d4..f520b63 100644 --- a/lib/generators/rails/content/templates/model.rb.tt +++ b/lib/generators/rails/content/templates/model.rb.tt @@ -2,7 +2,7 @@ class Content::<%= class_name %> < Perron::Resource <% if data_sources? -%> sources <%= data_sources.map { ":#{it}" }.join(", ") %> - def self.source_template(sources) + def self.source_template(source) <<~MARKDOWN --- --- diff --git a/lib/perron/resource/sourceable.rb b/lib/perron/resource/sourceable.rb index 351c87a..f8a8b61 100644 --- a/lib/perron/resource/sourceable.rb +++ b/lib/perron/resource/sourceable.rb @@ -15,6 +15,18 @@ def source_definitions @source_definitions || {} end + def resolve(name) + definition = source_definitions[name] + + data = if definition[:class] + definition[:class].all + else + Perron::DataSource.new(name.to_s).to_a + end + + definition[:scope] ? definition[:scope].call(data) : data + end + def source_names = source_definitions.keys def generate_from_sources! @@ -36,20 +48,29 @@ def source_backed? = source_names.any? def parsed(*arguments) return {} if arguments.empty? - arguments.flat_map { it.is_a?(Hash) ? it.to_a : [[it, {primary_key: :id}]] }.to_h + arguments.flat_map do |argument| + case argument + when Hash + argument.to_a + when Proc + [[SecureRandom.hex(8).to_sym, {scope: argument, primary_key: :id}]] + else + [[argument, {primary_key: :id}]] + end + end.to_h end def combinations - datasets = source_names.map { Perron::DataSource.new(it.to_s) } + datasets = source_names.map { resolve it } datasets.first.product(*datasets[1..]) end def content_with(combo) data = source_names.each.with_index.to_h { |name, index| [name, combo[index]] } - sources = Source.new(data) + source = Source.new(data) - source_template(sources) + source_template(source) end def filename_with(combo) @@ -65,21 +86,23 @@ def output_dir = Perron.configuration.input.join(model_name.collection) def source_backed? = self.class.source_backed? - def sources - @sources ||= begin + def source + @source ||= begin data = self.class.source_definitions.each_with_object({}) do |(name, options), hash| primary_key = options[:primary_key] singular_name = name.to_s.singularize identifier = frontmatter["#{singular_name}_#{primary_key}"] - hash[name] = Perron::DataSource.new(name.to_s).find { it.public_send(primary_key).to_s == identifier.to_s } + dataset = self.class.send(:resolve, name) + hash[name] = dataset.find { it.public_send(primary_key).to_s == identifier.to_s } end Source.new(data) end end + alias_method :sources, :source - def source_template(sources) + def source_template(source) raise NotImplementedError, "#{self.class.name} must implement #source_template" end diff --git a/test/dummy/app/models/content/product.rb b/test/dummy/app/models/content/product.rb index a7a0334..6194a84 100644 --- a/test/dummy/app/models/content/product.rb +++ b/test/dummy/app/models/content/product.rb @@ -1,20 +1,20 @@ class Content::Product < Perron::Resource sources :countries, products: { primary_key: :code } - def self.source_template(sources) + def self.source_template(source) <<~TEMPLATE --- - product_code: #{sources.products.code} - country_id: #{sources.countries.id} - title: #{sources.products.name} in #{sources.countries.name} - slug: #{sources.products.slug}-#{sources.countries.code.downcase} + product_code: #{source.products.code} + country_id: #{source.countries.id} + title: #{source.products.name} in #{source.countries.name} + slug: #{source.products.slug}-#{source.countries.code.downcase} --- - # #{sources.products.name} + # #{source.products.name} - Available in #{sources.countries.name} (#{sources.countries.code}) for $#{sources.products.price}. + Available in #{source.countries.name} (#{source.countries.code}) for $#{source.products.price}. - Product code: #{sources.products.code} + Product code: #{source.products.code} TEMPLATE end end diff --git a/test/generators/perron/content_generator_test.rb b/test/generators/perron/content_generator_test.rb index afef8ff..751a16c 100644 --- a/test/generators/perron/content_generator_test.rb +++ b/test/generators/perron/content_generator_test.rb @@ -112,7 +112,7 @@ class ContentGeneratorTest < Rails::Generators::TestCase assert_file "app/content/data/products.yml" assert_file "app/models/content/product.rb", /sources :countries, :products/ - assert_file "app/models/content/product.rb", /def self\.source_template\(sources\)/ + assert_file "app/models/content/product.rb", /def self\.source_template\(source\)/ end test "--data flag creates data source files with custom extensions" do diff --git a/test/perron/resource/sourceable_test.rb b/test/perron/resource/sourceable_test.rb index 2ded5de..588c622 100644 --- a/test/perron/resource/sourceable_test.rb +++ b/test/perron/resource/sourceable_test.rb @@ -1,4 +1,5 @@ require "test_helper" +require "support/data_api_source" class Perron::Resource::SourceableTest < ActiveSupport::TestCase setup do @@ -74,4 +75,38 @@ class Perron::Resource::SourceableTest < ActiveSupport::TestCase assert resource.source_backed? end + + test ".sources with custom class uses class.all method" do + test_class = Class.new(Perron::Resource) do + sources products: { class: DataApiSource } + + def self.source_template(source) + "test template" + end + end + + combinations = test_class.send(:combinations) + + assert_equal 2, combinations.length + assert_equal "product-1", combinations.first.first.id + assert_equal "product-2", combinations.last.first.id + end + + test ".sources with custom class and scope combines both" do + test_class = Class.new(Perron::Resource) do + sources products: { + class: DataApiSource, + scope: -> (products) { products.select(&:active) } + } + + def self.source_template(source) + "test template" + end + end + + combinations = test_class.send(:combinations) + + assert_equal 1, combinations.length + assert_equal "product-1", combinations.first.first.id + end end diff --git a/test/support/data_api_source.rb b/test/support/data_api_source.rb new file mode 100644 index 0000000..017a806 --- /dev/null +++ b/test/support/data_api_source.rb @@ -0,0 +1,16 @@ +class DataApiProduct + attr_reader :id, :name, :active + + def initialize(id:, name:, active:) + @id, @name, @active = id, name, active + end +end + +class DataApiSource + def self.all + [ + DataApiProduct.new(id: "product-1", name: "API Product 1", active: true), + DataApiProduct.new(id: "product-2", name: "API Product 2", active: false) + ] + end +end