Skip to content
Open
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 .rspec
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
--color
--warnings
--require spec_helper
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ source 'https://rubygems.org'
gemspec

gem 'sequelize', github: 'Ptico/sequelize'
gem 'pry'
3 changes: 2 additions & 1 deletion dml.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ Gem::Specification.new do |spec|

spec.add_dependency 'sequel', '~> 4.6'
spec.add_dependency 'virtus'
spec.add_dependency 'inflecto'

spec.add_development_dependency 'bundler', '~> 1.6'
spec.add_development_dependency 'rake'
spec.add_development_dependency 'rspec', '~> 3.0.0'
spec.add_development_dependency 'rspec', '~> 3.0'
spec.add_development_dependency 'sequel_pg'
end
8 changes: 7 additions & 1 deletion lib/dml.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
require 'sequel'

require 'inflecto'

require 'dml/relation'

##
# Data manipulation layer
# Something like data-mapper on top of Sequel
#
module Dml

def self.define_relations(&block)
@relations = Relation::DSL.new(&block)
end
end

require 'dml/collection'
Expand Down
45 changes: 45 additions & 0 deletions lib/dml/relation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module Dml
class Relation
UndefinedDependencyError = Class.new(StandardError)

attr_reader :name

attr_reader :primary_key

attr_reader :associations

attr_reader :dependencies

def freeze
@associations.freeze
@dependencies.freeze

super
end

private

def initialize(name, pk=nil, associations={})
@name = name
@primary_key = pk.kind_of?(Array) ? pk.map(&:to_sym) : pk.to_sym

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it's better for this var to has type Array in any situation.

@primary_key = Array(pk).map(&:to_sym)

@associations = associations

@dependencies = Set[]

associations.values.each do |assoc|
dependencies.add(assoc.target_relation)
end

freeze
end

end
end

require 'dml/relation/list'

require 'dml/relation/associations/to_one'
require 'dml/relation/associations/one_to_one'
require 'dml/relation/associations/many_to_one'

require 'dml/relation/dsl'
15 changes: 15 additions & 0 deletions lib/dml/relation/associations/many_to_one.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Dml
class Relation
module Associations

class ManyToOne < ToOne

def type
:many_to_one
end

end

end
end
end
15 changes: 15 additions & 0 deletions lib/dml/relation/associations/one_to_one.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Dml
class Relation
module Associations

class OneToOne < ToOne

def type
:one_to_one
end

end

end
end
end
155 changes: 155 additions & 0 deletions lib/dml/relation/associations/to_one.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
module Dml
class Relation
module Associations

class ToOne

##
# Source relation name
#
# Returns: {Symbol}
#
attr_reader :source_relation

##
# Association name
#
# Returns: {Symbol}
#
attr_reader :target_name

##
# Associated relation name
#
# Returns: {Symbol}
#
attr_reader :target_relation

##
# Source foreign key list
#
# Example:
#
# association.foreign_keys #=> [:company_id, :company_country]
#
# Returns: {Array(Symbol)}
#
attr_reader :foreign_keys

##
# Associated relation primary keys list
#
# Example:
#
# association.target_keys #=> [:id, :country_id]
#
# Returns: {Array(Symbol)}
#
attr_reader :target_keys

##
# Foreign key => primary key list
#
# Example:
#
# association.reference_keys #=> { :company_id => :id, :company_country => :country_id }
#
# Returns: {Hash}
#
attr_reader :reference_keys

##
# Association options
#
# Returns: {Hash}
#
attr_reader :options

##
# Abstract: Association type
#
# Returns: {Symbol}
#
def type
fail(NotImplementedError)
end

##
# Deeply freeze object
#
def freeze
@options.freeze
@foreign_keys.freeze
@target_keys.freeze
@reference_keys.freeze

super
end

private

##
# Constructor:
#
# Params:
# - source_name {Symbol} source relation name
# - target_name {Symbol} association name
# - options {Hash}
# - target_relation {Symbol|String} target relation name
# - foreign_keys {Symbol|Array} source foreign key(s)
# - target_keys {Symbol|Array} target primary key(s)
#
def initialize(source_name, target_name, options={})
@source_relation = source_name
@target_name = target_name

@options = options

set_target_relation
set_foreign_keys
set_target_keys
set_reference_keys

freeze
end

##
# Private: set target relation name
#
# If name given in options - take it unchanged,
# otherwise – pluralize association name
#
def set_target_relation
@target_relation = if options.has_key?(:target_relation)
options[:target_relation]
else
Inflecto.pluralize(target_name)
end.to_sym
end

##
# Private: set source relation foreign key list
#
def set_foreign_keys
@foreign_keys = Array(options[:foreign_keys] || "#{target_name}_id").map(&:to_sym)
end

##
# Private: set target relation primary key list
#
def set_target_keys
@target_keys = Array(options[:target_keys] || :id).map(&:to_sym)
end

##
# Private: create hash where keys is a source foreign keys
# and values is a target primary keys
#
def set_reference_keys
@reference_keys = Hash[foreign_keys.zip(target_keys)]
end
end

end
end
end
92 changes: 92 additions & 0 deletions lib/dml/relation/dsl.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
module Dml
class Relation

class DSL
class Relation
attr_reader :associations

def key(*names)
@key = names
end

def belongs_to(association, &block)
assoc_name = association.to_sym
@associations[assoc_name] = BelongsTo.new(@name, assoc_name, &block).association
end

def belongs_to_one(association, &block)
assoc_name = association.to_sym
@associations[assoc_name] = BelongsToOne.new(@name, assoc_name, &block).association
end

def relation
Dml::Relation.new(@name, @key, @associations)
end

private

def initialize(name, &block)
@name = name.to_sym
@key = [:id]
@associations = {}

instance_eval(&block) if block_given?
end
end

class BelongsTo
def relation(name)
@options[:target_relation] = name
end

def foreign_key(*keys)
@options[:foreign_keys] = keys
end

def reference_key(*keys)
@options[:target_keys] = keys
end

def association
klass.new(@source_name, @target_name, @options)
end

private

def initialize(source_name, target_name, &block)
@source_name = source_name
@target_name = target_name

@options = {}

instance_eval(&block) if block_given?
end

def klass
Dml::Relation::Associations::ManyToOne
end
end

class BelongsToOne < BelongsTo
def klass
Dml::Relation::Associations::OneToOne
end
end

attr_reader :result

def relation(name, &block)
@relations << Relation.new(name, &block).relation
end

private

def initialize(&block)
@relations = []
instance_eval(&block) if block_given?
@result = Dml::Relation::List.new(@relations)
end
end

end
end
Loading