Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support automatic schema inferring #345

Merged
merged 2 commits into from
May 22, 2016
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
1 change: 0 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ group :test do
gem 'anima', '~> 0.2.0'
gem 'minitest'
gem 'thread_safe'
gem 'activesupport'
gem 'inflecto', '~> 0.0', '>= 0.0.2'

platforms :rbx do
Expand Down
12 changes: 8 additions & 4 deletions lib/rom/relation/class_interface.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ def inherited(klass)
klass.class_eval do
use :registry_reader

defines :gateway, :dataset, :dataset_proc, :register_as, :schema_dsl
defines :gateway, :dataset, :dataset_proc, :register_as, :schema_dsl, :schema_inferrer

gateway :default
schema_dsl Schema::DSL
schema_inferrer nil

dataset default_name

Expand Down Expand Up @@ -146,13 +147,16 @@ def [](adapter)
# @return [Schema]
#
# @param [Symbol] dataset An optional dataset name
# @param [Boolean] infer Whether to do an automatic schema inferring
#
# @api public
def schema(dataset = nil, &block)
def schema(dataset = nil, infer: false, &block)
if defined?(@schema)
@schema
elsif block
@schema = schema_dsl.new(dataset || self.dataset, &block).call
elsif block || infer
@schema = schema_dsl.new(dataset || self.dataset,
inferrer: infer ? schema_inferrer : nil,
&block).call

if dataset
self.dataset(dataset)
Expand Down
39 changes: 30 additions & 9 deletions lib/rom/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,27 @@ module ROM
#
# @api public
class Schema
include Dry::Equalizer(:dataset, :attributes, :meta)
include Dry::Equalizer(:dataset, :attributes, :inferrer)
include Enumerable

attr_reader :dataset, :attributes, :meta
attr_reader :dataset, :attributes, :inferrer

# @api public
class DSL < BasicObject
attr_reader :dataset, :attributes
attr_reader :dataset, :attributes, :inferrer

# @api private
def initialize(dataset = nil, &block)
@attributes = {}
def initialize(dataset = nil, inferrer: nil, &block)
@attributes = nil
@dataset = dataset
instance_exec(&block)
@inferrer = inferrer

if block
instance_exec(&block)
elsif inferrer.nil?
raise ArgumentError,
'You must pass a block to define a schema or set an inferrer for automatic inferring'
end
end

# Defines a relation attribute with its type
Expand All @@ -28,6 +35,7 @@ def initialize(dataset = nil, &block)
#
# @api public
def attribute(name, type)
@attributes ||= {}
@attributes[name] = type.meta(name: name)
end

Expand All @@ -43,15 +51,17 @@ def primary_key(*names)

# @api private
def call
Schema.new(dataset, attributes)
Schema.new(dataset, attributes, inferrer: inferrer && inferrer.new(self))
end
end

# @api private
def initialize(dataset, attributes)
def initialize(dataset, attributes, inferrer: nil)
@dataset = dataset
@attributes = attributes
freeze
@inferrer = inferrer

freeze if self.defined?
end

# Iterate over schema's attributes
Expand Down Expand Up @@ -79,5 +89,16 @@ def primary_key
def foreign_key(relation)
detect { |attr| attr.meta[:foreign_key] && attr.meta[:relation] == relation }
end

# @api public
def defined?
!@attributes.nil?
end

# @api private
def infer!(gateway)
@attributes = inferrer.call(dataset, gateway)
freeze
end
end
end
4 changes: 4 additions & 0 deletions lib/rom/setup/finalize/finalize_relations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ def run!
# where klass' gateway points to non-existant repo
gateway = @gateways.fetch(klass.gateway)
ds_proc = klass.dataset_proc || -> _ { self }

if klass.schema && !klass.schema.defined?
klass.schema.infer!(gateway)
end
dataset = gateway.dataset(klass.dataset).instance_exec(klass, &ds_proc)

relation = klass.new(dataset, __registry__: registry)
Expand Down