Skip to content

Commit

Permalink
Merge b2085a9 into 6621ab1
Browse files Browse the repository at this point in the history
  • Loading branch information
hlegius committed Oct 8, 2015
2 parents 6621ab1 + b2085a9 commit a967dc8
Show file tree
Hide file tree
Showing 18 changed files with 630 additions and 57 deletions.
38 changes: 37 additions & 1 deletion lib/lotus/model/adapters/memory/query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def initialize(dataset, collection, &blk)
@collection = collection
@conditions = []
@modifiers = []
@associations = []
instance_eval(&blk) if block_given?
end

Expand All @@ -68,7 +69,7 @@ def initialize(dataset, collection, &blk)
#
# @since 0.1.0
def all
@collection.deserialize(run)
@collection.deserialize(run, @associations)
end

# Adds a condition that behaves like SQL `WHERE`.
Expand Down Expand Up @@ -554,6 +555,41 @@ def group
raise NotImplementedError
end

# Preload a given association into root's aggregation
# This should be implemented inside Repository in a class method. See example below.
#
# @since x.x.x
# @return Lotus::Model::Adapters::Memory::Query
#
# @example
#
# mapping do
# collections :users do
# entity User
# attribute :id, Integer
# association :articles, [Article], foreign_key: :user_id, collection: :articles
# end
#
# collections :articles do
# entity Article
# attribute :id, Integer
# attribute :user_id, Integer
# association :user, User, foreign_key: :id, collection: :articles
# end
# end
#
# class UserRepository
# include Lotus::Repository
#
# def with_articles
# query.preload(:articles)
# end
# end
def preload(association)
@associations << association
self
end

protected
def method_missing(m, *args, &blk)
if @context.respond_to?(m)
Expand Down
72 changes: 57 additions & 15 deletions lib/lotus/model/adapters/sql/collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,28 @@ module Sql
# @see http://sequel.jeremyevans.net/rdoc/files/doc/dataset_basics_rdoc.html
# @see http://sequel.jeremyevans.net/rdoc/files/doc/dataset_filtering_rdoc.html
class Collection < SimpleDelegator
# @attr_reader associations [Array] symbols names to preload mapped collections
#
# @since x.x.x
# @api private
attr_reader :associations

# Initialize a collection
#
# @param dataset [Sequel::Dataset] the dataset that maps a table or a
# subset of it.
# @param mapped_collection [Lotus::Model::Mapping::Collection] a
# mapped collection
# @param associations [Array] association symbols names to preload
#
# @return [Lotus::Model::Adapters::Sql::Collection]
#
# @api private
# @since 0.1.0
def initialize(dataset, mapped_collection)
def initialize(dataset, mapped_collection, associations = [])
super(dataset)
@mapped_collection = mapped_collection
@associations = associations
end

# Filters the current scope with an `exclude` directive.
Expand All @@ -41,7 +49,7 @@ def initialize(dataset, mapped_collection)
# @api private
# @since 0.1.0
def exclude(*args)
Collection.new(super, @mapped_collection)
Collection.new(super, @mapped_collection, @associations)
end

# Creates a record for the given entity and assigns an id.
Expand Down Expand Up @@ -73,7 +81,7 @@ def insert(entity)
# @api private
# @since 0.1.0
def limit(*args)
Collection.new(super, @mapped_collection)
Collection.new(super, @mapped_collection, @associations)
end

# Filters the current scope with an `offset` directive.
Expand All @@ -88,7 +96,7 @@ def limit(*args)
# @api private
# @since 0.1.0
def offset(*args)
Collection.new(super, @mapped_collection)
Collection.new(super, @mapped_collection, @associations)
end

# Filters the current scope with an `or` directive.
Expand All @@ -103,7 +111,7 @@ def offset(*args)
# @api private
# @since 0.1.0
def or(*args)
Collection.new(super, @mapped_collection)
Collection.new(super, @mapped_collection, @associations)
end

# Filters the current scope with an `order` directive.
Expand All @@ -118,7 +126,7 @@ def or(*args)
# @api private
# @since 0.1.0
def order(*args)
Collection.new(super, @mapped_collection)
Collection.new(super, @mapped_collection, @associations)
end

# Filters the current scope with an `order` directive.
Expand All @@ -133,7 +141,7 @@ def order(*args)
# @api private
# @since 0.1.0
def order_more(*args)
Collection.new(super, @mapped_collection)
Collection.new(super, @mapped_collection, @associations)
end

# Filters the current scope with a `select` directive.
Expand All @@ -149,15 +157,14 @@ def order_more(*args)
# @since 0.1.0
if RUBY_VERSION >= '2.1'
def select(*args)
Collection.new(super, @mapped_collection)
Collection.new(super, @mapped_collection, @associations)
end
else
def select(*args)
Collection.new(__getobj__.select(*Lotus::Utils::Kernel.Array(args)), @mapped_collection)
Collection.new(__getobj__.select(*Lotus::Utils::Kernel.Array(args)), @mapped_collection, @associations)
end
end


# Filters the current scope with a `group` directive.
#
# @param args [Array] the array of arguments
Expand All @@ -170,7 +177,7 @@ def select(*args)
# @api private
# @since 0.5.0
def group(*args)
Collection.new(super, @mapped_collection)
Collection.new(super, @mapped_collection, @associations)
end

# Filters the current scope with a `where` directive.
Expand All @@ -185,7 +192,7 @@ def group(*args)
# @api private
# @since 0.1.0
def where(*args)
Collection.new(super, @mapped_collection)
Collection.new(super, @mapped_collection, @associations)
end

# Updates the record corresponding to the given entity.
Expand All @@ -211,7 +218,42 @@ def update(entity)
# @api private
# @since 0.1.0
def to_a
@mapped_collection.deserialize(self)
@mapped_collection.deserialize(self, @associations)
end

# Preload a given association into root's aggregation
# This should be implemented inside Repository in a class method. See example below.
#
# @since x.x.x
# @return Lotus::Model::Adapters::Sql::Collection
#
# @example
#
# mapping do
# collections :users do
# entity User
# attribute :id, Integer
# association :articles, [Article], foreign_key: :user_id, collection: :articles
# end
#
# collections :articles do
# entity Article
# attribute :id, Integer
# attribute :user_id, Integer
# association :user, User, foreign_key: :id, collection: :articles
# end
# end
#
# class UserRepository
# include Lotus::Repository
#
# def with_articles
# query.preload(:articles)
# end
# end
def preload(association)
@associations << association
self
end

# Select all attributes for current scope
Expand All @@ -224,7 +266,7 @@ def to_a
#
# @see http://www.rubydoc.info/github/jeremyevans/sequel/Sequel%2FDataset%3Aselect_all
def select_all
Collection.new(super(table_name), @mapped_collection)
Collection.new(super(table_name), @mapped_collection, @associations)
end

# Use join table for current scope
Expand All @@ -237,7 +279,7 @@ def select_all
#
# @see http://www.rubydoc.info/github/jeremyevans/sequel/Sequel%2FDataset%3Ajoin_table
def join_table(*args)
Collection.new(super, @mapped_collection)
Collection.new(super, @mapped_collection, @associations)
end

# Return table name mapped collection
Expand Down
47 changes: 46 additions & 1 deletion lib/lotus/model/adapters/sql/query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ class Query
# @api private
attr_reader :conditions

# @attr_reader collection [Lotus::Model::Adapters::Sql::Collection]
#
# @since x.x.x
# @api private
attr_reader :collection

# Initialize a query
#
# @param collection [Lotus::Model::Adapters::Sql::Collection] the
Expand All @@ -59,7 +65,8 @@ class Query
#
# @return [Lotus::Model::Adapters::Sql::Query]
def initialize(collection, context = nil, &blk)
@collection, @context = collection, context
@collection = collection
@context = context
@conditions = []

instance_eval(&blk) if block_given?
Expand Down Expand Up @@ -596,6 +603,41 @@ def negate!
end
end

# Preload a given association into root's aggregation
# This should be implemented inside Repository in a class method. See example below.
#
# @since x.x.x
# @return Lotus::Model::Adapters::Sql::Query
#
# @example
#
# mapping do
# collections :users do
# entity User
# attribute :id, Integer
# association :articles, [Article], foreign_key: :user_id, collection: :articles
# end
#
# collections :articles do
# entity Article
# attribute :id, Integer
# attribute :user_id, Integer
# association :user, [User], foreign_key: :id, collection: :articles
# end
# end
#
# class UserRepository
# include Lotus::Repository
#
# def with_articles
# query.preload(:articles)
# end
# end
def preload(association)
@collection.preload(association)
self
end

# Apply all the conditions and returns a filtered collection.
#
# This operation is idempotent, and the returned result didn't
Expand Down Expand Up @@ -754,6 +796,9 @@ def _join(collection, options = {})
# # You're welcome ;)
def apply(query)
dup.tap do |result|
query.collection.associations.each do |association|
result.preload(association)
end
result.conditions.push(*query.conditions)
end
end
Expand Down
53 changes: 53 additions & 0 deletions lib/lotus/model/associations/many_to_one.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
module Lotus
module Model
module Associations
# Entities Association ManyToOne relationship
class ManyToOne
# @attr_reader related collection (the other side of relationship)
#
# @since x.x.x
attr_reader :collection

# @attr_reader Repository responsible will roots this association
#
# @since x.x.x
attr_accessor :repository

# Collects initial data to build ManyToOne Association
#
# @since x.x.x
#
# @example
#
# collection: :articles, foreign_key: :article_id, repository: ArticlesRepository
def initialize(opts)
@name = opts.fetch(:name)
@collection = opts.fetch(:collection)
@foreign_key = opts.fetch(:foreign_key) {default_foreign_key}
@repository = nil
end

# @since x.x.x
# @api private
def associate_entities!(entities)
entities.map do |entity|
entity.send("#{@name}=", @repository.find(entity.send(@foreign_key)))
entity
end
end

private
# Default Foreign key's name
#
# It's possible to overwrite this through foreign_key attribute
# inside mapping via association method.
#
# @api private
# @since x.x.x
def default_foreign_key
"#{@name}_id"
end
end
end
end
end
Loading

0 comments on commit a967dc8

Please sign in to comment.