Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/generators/rails/content/templates/model.rb.tt
Original file line number Diff line number Diff line change
Expand Up @@ -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
---
---
Expand Down
39 changes: 31 additions & 8 deletions lib/perron/resource/sourceable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Expand All @@ -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)
Expand All @@ -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

Expand Down
16 changes: 8 additions & 8 deletions test/dummy/app/models/content/product.rb
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion test/generators/perron/content_generator_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 35 additions & 0 deletions test/perron/resource/sourceable_test.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "test_helper"
require "support/data_api_source"

class Perron::Resource::SourceableTest < ActiveSupport::TestCase
setup do
Expand Down Expand Up @@ -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
16 changes: 16 additions & 0 deletions test/support/data_api_source.rb
Original file line number Diff line number Diff line change
@@ -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
Loading