Created
March 17, 2010 23:52
Revisions
-
dkubb revised this gist
Apr 6, 2010 . 1 changed file with 6 additions and 10 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -173,13 +173,13 @@ def initialize(*) before_hooks.unshift BeforeHook.new(resource, 'before_save_hook') after_hooks.push AfterHook.new(resource, 'after_save_hook') end def call resource.persisted_state = resource.persisted_state.commit end end class Create < Save def add_to_session(session) super @@ -195,11 +195,7 @@ def add_to_session(session) end end class Update < Save; end class Destroy < Command include HookableCommand @@ -210,7 +206,7 @@ def initialize(*) end def call resource.persisted_state = resource.persisted_state.delete.commit end end @@ -223,7 +219,7 @@ def initialize(resource, relationship) end def call resource.__send__("#{relationship.name}=", relationship.get(resource)) end def ==(other) -
dkubb revised this gist
Apr 6, 2010 . 1 changed file with 3 additions and 9 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -87,7 +87,7 @@ class Command def initialize(resource) @resource = resource @parents = resource.send(:parent_associations).map do |parent| parent.save_command end end @@ -333,13 +333,13 @@ def save return if session.include?(self) # add parents to the UoW parent_associations.each { |parent| parent.save } # add resource to the UoW session << self # add children to the UoW child_associations.flatten.each { |child| child.save } end end @@ -351,12 +351,6 @@ def save_command Session::Save.new(self) end end end -
dkubb revised this gist
Mar 22, 2010 . 1 changed file with 6 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -204,6 +204,11 @@ def call class Destroy < Command include HookableCommand def initialize(*) super @parents.clear # XXX: hack, reset what the parent class sets end def call resource.send(:_destroy, false) end @@ -396,7 +401,7 @@ class Person parent.save puts '-' * 80 parent.children.destroy parent.destroy __END__ -
dkubb revised this gist
Mar 20, 2010 . 1 changed file with 21 additions and 21 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -403,45 +403,45 @@ class Person OUTPUT: ~ (0.000151) SELECT sqlite_version(*) ~ (0.000179) DROP TABLE IF EXISTS "people" ~ (0.000025) PRAGMA table_info("people") ~ (0.000457) CREATE TABLE "people" ("id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "name" VARCHAR(50) NOT NULL, "parent_id" INTEGER) ~ (0.000146) CREATE INDEX "index_people_parent" ON "people" ("parent_id") ~ (0.000128) CREATE UNIQUE INDEX "unique_people_name" ON "people" ("name") -------------------------------------------------------------------------------- Before Saving Dan Kubb Before Creating Dan Kubb Before Saving Alex Kubb Before Creating Alex Kubb Before Saving Katie Kubb Before Creating Katie Kubb ~ (0.000107) INSERT INTO "people" ("name") VALUES ('Dan Kubb') ~ (0.000063) INSERT INTO "people" ("name", "parent_id") VALUES ('Alex Kubb', 1) ~ (0.000055) INSERT INTO "people" ("name", "parent_id") VALUES ('Katie Kubb', 1) After Creating Dan Kubb After Saving Dan Kubb After Creating Alex Kubb After Saving Alex Kubb After Creating Katie Kubb After Saving Katie Kubb -------------------------------------------------------------------------------- Before Saving Barbara-Ann Kubb Before Updating Barbara-Ann Kubb Before Saving Alexander Kubb Before Updating Alexander Kubb Before Saving Katherine Kubb Before Updating Katherine Kubb ~ (0.000123) UPDATE "people" SET "name" = 'Barbara-Ann Kubb' WHERE "id" = 1 ~ (0.000054) UPDATE "people" SET "name" = 'Alexander Kubb' WHERE "id" = 2 ~ (0.000052) UPDATE "people" SET "name" = 'Katherine Kubb' WHERE "id" = 3 After Updating Barbara-Ann Kubb After Saving Barbara-Ann Kubb After Updating Alexander Kubb After Saving Alexander Kubb After Updating Katherine Kubb After Saving Katherine Kubb -------------------------------------------------------------------------------- Before Destroying Barbara-Ann Kubb ~ (0.000064) DELETE FROM "people" WHERE "id" = 1 After Destroying Barbara-Ann Kubb -
dkubb revised this gist
Mar 20, 2010 . 1 changed file with 16 additions and 26 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -16,7 +16,7 @@ module DataMapper class Session attr_reader :before_hooks, :dependencies, :after_hooks def self.scope original = Thread.current[:dm_session] @@ -34,7 +34,9 @@ def self.scope end def initialize(&block) @before_hooks = CommandDependencies.new @dependencies = CommandDependencies.new @after_hooks = CommandDependencies.new end def valid? @@ -66,7 +68,9 @@ def destroy(resource) end def commit before_hooks.call dependencies.call after_hooks.call freeze end @@ -115,8 +119,6 @@ def add_to_session(session) end module HookableCommand def initialize(*) super @@ -125,39 +127,26 @@ def initialize(*) end def add_to_session(session) add_hook_dependencies(session, :before_hooks) super add_hook_dependencies(session, :after_hooks) end private attr_reader :before_hooks, :after_hooks def add_hook_dependencies(session, name) hooks = send(name) # make hooks dependent on the parent(s) hooks to ensure they # are executed in the same order as the parent commands parents.each do |parent| hooks.each { |hook| hook.parents.concat(parent.send(name)) } end # add hooks to dependencies session.send(name).concat(hooks) end def command_name @@ -248,6 +237,7 @@ class Hook < Command def initialize(resource, name) super(resource) @name = name @parents.clear # XXX: hack, reset what the parent class sets end def call -
dkubb revised this gist
Mar 20, 2010 . 1 changed file with 15 additions and 15 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -43,7 +43,7 @@ def valid? def <<(resource) if command = resource.save_command command.add_to_session(self) else # TODO: remove from the dependencies list # - would need to remove *all* references from all commands @@ -61,7 +61,7 @@ def include?(resource) end def destroy(resource) Destroy.new(resource).add_to_session(self) self end @@ -108,8 +108,8 @@ def hash resource.object_id.hash end def add_to_session(session) session.dependencies << self end end @@ -124,15 +124,15 @@ def initialize(*) @after_hooks = [ AfterHook.new(resource, "after_#{command_name}_hook") ] end def add_to_session(session) add_before_hook_dependencies(session) super add_after_hook_dependencies(session) end private def add_before_hook_dependencies(session) # make before hooks dependent on the parent(s) before hooks parents.each do |parent| next unless parent.respond_to?(:before_hooks) @@ -143,10 +143,10 @@ def add_before_hook_dependencies(dependencies) parents.concat(before_hooks) # add before hooks to dependencies session.dependencies.concat(before_hooks) end def add_after_hook_dependencies(session) # make after hooks dependent on the parent(s) after hooks parents.each do |parent| next unless parent.respond_to?(:after_hooks) @@ -157,7 +157,7 @@ def add_after_hook_dependencies(dependencies) after_hooks.each { |hook| hook.parents << self } # add after hooks to dependencies session.dependencies.concat(after_hooks) end def command_name @@ -191,17 +191,17 @@ def call resource.send(:_create) end def add_to_session(session) super # make setting the FK dependent on saving the parent, and # make the current command dependent on the FK being set resource.send(:parent_relationships).each do |relationship| parent = relationship.get!(resource) foreign_key = SetForeignKey.new(resource, relationship) foreign_key.parents << parent.save_command parents << foreign_key session.dependencies << foreign_key end end end -
dkubb revised this gist
Mar 20, 2010 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -142,7 +142,7 @@ def add_before_hook_dependencies(dependencies) # make current command dependent on before_hook parents.concat(before_hooks) # add before hooks to dependencies dependencies.concat(before_hooks) end @@ -156,7 +156,7 @@ def add_after_hook_dependencies(dependencies) # make after hooks dependent on curent command after_hooks.each { |hook| hook.parents << self } # add after hooks to dependencies dependencies.concat(after_hooks) end -
dkubb revised this gist
Mar 20, 2010 . 1 changed file with 3 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -5,6 +5,9 @@ # order, and foreign keys set before children are saved, but after parents # are saved. # The hooks should fire in the following order: # https://gist.github.com/6666d2818b14296a28ab require 'tsort' require 'rubygems' -
dkubb revised this gist
Mar 20, 2010 . 1 changed file with 25 additions and 18 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -408,40 +408,47 @@ class Person __END__ OUTPUT: ~ (0.000168) SELECT sqlite_version(*) ~ (0.000186) DROP TABLE IF EXISTS "people" ~ (0.000030) PRAGMA table_info("people") ~ (0.000476) CREATE TABLE "people" ("id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "name" VARCHAR(50) NOT NULL, "parent_id" INTEGER) ~ (0.000132) CREATE INDEX "index_people_parent" ON "people" ("parent_id") ~ (0.000127) CREATE UNIQUE INDEX "unique_people_name" ON "people" ("name") -------------------------------------------------------------------------------- Before Saving Dan Kubb Before Creating Dan Kubb ~ (0.000116) INSERT INTO "people" ("name") VALUES ('Dan Kubb') After Creating Dan Kubb After Saving Dan Kubb Before Saving Alex Kubb Before Creating Alex Kubb ~ (0.000069) INSERT INTO "people" ("name", "parent_id") VALUES ('Alex Kubb', 1) After Creating Alex Kubb After Saving Alex Kubb Before Saving Katie Kubb Before Creating Katie Kubb ~ (0.000062) INSERT INTO "people" ("name", "parent_id") VALUES ('Katie Kubb', 1) After Creating Katie Kubb After Saving Katie Kubb -------------------------------------------------------------------------------- Before Saving Barbara-Ann Kubb Before Updating Barbara-Ann Kubb ~ (0.000127) UPDATE "people" SET "name" = 'Barbara-Ann Kubb' WHERE "id" = 1 After Updating Barbara-Ann Kubb After Saving Barbara-Ann Kubb Before Saving Alexander Kubb Before Updating Alexander Kubb ~ (0.000073) UPDATE "people" SET "name" = 'Alexander Kubb' WHERE "id" = 2 After Updating Alexander Kubb After Saving Alexander Kubb Before Saving Katherine Kubb Before Updating Katherine Kubb ~ (0.000087) UPDATE "people" SET "name" = 'Katherine Kubb' WHERE "id" = 3 After Updating Katherine Kubb After Saving Katherine Kubb -------------------------------------------------------------------------------- Before Destroying Barbara-Ann Kubb ~ (0.000136) DELETE FROM "people" WHERE "id" = 1 After Destroying Barbara-Ann Kubb -
dkubb revised this gist
Mar 20, 2010 . 1 changed file with 28 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -23,7 +23,6 @@ def self.scope # commit in the outer-most block session.commit if original.nil? rescue Exception session.rollback if original.nil? raise @@ -175,6 +174,13 @@ def self.new(resource) super end end def initialize(*) super before_hooks.unshift BeforeHook.new(resource, 'before_save_hook') after_hooks.push AfterHook.new(resource, 'after_save_hook') end end class Create < Save @@ -222,22 +228,37 @@ def initialize(resource, relationship) def call relationship.set(resource, relationship.get!(resource)) end def ==(other) super && relationship == other.relationship end def eql?(other) super && relationship.eql?(other.relationship) end end class Hook < Command attr_reader :name def initialize(resource, name) super(resource) @name = name end def call resource.send(name) true end def ==(other) super && name == other.name end def eql?(other) super && name.eql?(other.name) end end @@ -329,7 +350,7 @@ def destroy end def save_command Session::Save.new(self) end private -
dkubb revised this gist
Mar 19, 2010 . 1 changed file with 30 additions and 14 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -113,12 +113,13 @@ def add_to_dependencies(dependencies) end module HookableCommand attr_reader :before_hooks, :after_hooks def initialize(*) super @before_hooks = [ BeforeHook.new(resource, "before_#{command_name}_hook") ] @after_hooks = [ AfterHook.new(resource, "after_#{command_name}_hook") ] end def add_to_dependencies(dependencies) @@ -130,21 +131,31 @@ def add_to_dependencies(dependencies) private def add_before_hook_dependencies(dependencies) # make before hooks dependent on the parent(s) before hooks parents.each do |parent| next unless parent.respond_to?(:before_hooks) before_hooks.each { |hook| hook.parents.concat(parent.before_hooks) } end # make current command dependent on before_hook parents.concat(before_hooks) # add before hooks to depdendencies dependencies.concat(before_hooks) end def add_after_hook_dependencies(dependencies) # make after hooks dependent on the parent(s) after hooks parents.each do |parent| next unless parent.respond_to?(:after_hooks) after_hooks.each { |hook| hook.parents.concat(parent.after_hooks) } end # make after hooks dependent on curent command after_hooks.each { |hook| hook.parents << self } # add after hooks to depdendencies dependencies.concat(after_hooks) end def command_name @@ -254,6 +265,11 @@ def <<(command) self end def concat(commands) commands.each { |command| self << command } self end def valid? all? { |node| node.valid? } end -
dkubb renamed this gist
Mar 19, 2010 . 1 changed file with 38 additions and 43 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -15,6 +15,22 @@ module DataMapper class Session attr_reader :dependencies def self.scope original = Thread.current[:dm_session] session = Thread.current[:dm_session] ||= new yield session # commit in the outer-most block session.commit if original.nil? rescue Exception session.rollback if original.nil? raise ensure Thread.current[:dm_session] = original end def initialize(&block) @dependencies = CommandDependencies.new end @@ -97,12 +113,12 @@ def add_to_dependencies(dependencies) end module HookableCommand def before_hook(name = "before_#{command_name}_hook") @before_hook ||= BeforeHook.new(resource, name) end def after_hook(name = "after_#{command_name}_hook") @after_hook ||= AfterHook.new(resource, name) end def add_to_dependencies(dependencies) @@ -131,6 +147,10 @@ def add_after_hook_dependencies(dependencies) dependencies << after_hook end def command_name self.class.name.split('::').last.downcase end end class Save < Command @@ -194,11 +214,11 @@ def call end class Hook < Command attr_reader :hook_name def initialize(resource, hook_name) super(resource) @hook_name = hook_name end def call @@ -208,22 +228,10 @@ def call private end class BeforeHook < Hook; end class AfterHook < Hook; end class CommandDependencies include Enumerable, TSort @@ -284,12 +292,8 @@ def insertion_order end module Resource def save Session.scope do |session| # only allow a resource to be saved once return if session.include?(self) @@ -305,28 +309,19 @@ def save end def destroy Session.scope { |session| session.destroy(self) } end def save_command @save_command ||= Session::Save.new(self) end private def child_resources child_collections.flatten end end end -
dkubb revised this gist
Mar 19, 2010 . 1 changed file with 180 additions and 26 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,3 +1,10 @@ # NOTE: this is a code spike, and the following code will probably # not make it into dm-core in it's current form. I wanted to see if it # were possible to use a topographical sort to ensure parents were # saved before children, before/after filters were fired in the correct # order, and foreign keys set before children are saved, but after parents # are saved. require 'tsort' require 'rubygems' @@ -18,9 +25,9 @@ def valid? def <<(resource) if command = resource.save_command command.add_to_dependencies(dependencies) else # TODO: remove from the dependencies list # - would need to remove *all* references from all commands end self @@ -31,8 +38,12 @@ def concat(resources) self end def include?(resource) dependencies.include?(resource.save_command) end def destroy(resource) Destroy.new(resource).add_to_dependencies(dependencies) self end @@ -47,6 +58,8 @@ def rollback freeze end private class Command attr_reader :resource, :parents @@ -74,12 +87,55 @@ def eql?(other) end def hash resource.object_id.hash end def add_to_dependencies(dependencies) dependencies << self end end module HookableCommand def before_hook @before_hook ||= BeforeHook.new(self) end def after_hook @after_hook ||= AfterHook.new(self) end def add_to_dependencies(dependencies) add_before_hook_dependencies(dependencies) super add_after_hook_dependencies(dependencies) end private def add_before_hook_dependencies(dependencies) # make before hook dependent on the parent(s) before hook parents.each { |parent| before_hook.parents << parent.before_hook if parent.respond_to?(:before_hook) } # make current command dependent on before_hook parents << before_hook dependencies << before_hook end def add_after_hook_dependencies(dependencies) # make after hook dependent on the parent(s) after hook parents.each { |parent| after_hook.parents << parent.after_hook if parent.respond_to?(:after_hook) } # make after hook dependent on curent command after_hook.parents << self dependencies << after_hook end end class Save < Command include HookableCommand def self.new(resource) if equal?(Save) klass = resource.new? ? Create : Update @@ -91,7 +147,11 @@ def self.new(resource) end class Create < Save def call resource.send(:_create) end def add_to_dependencies(dependencies) super # make setting the FK dependent on saving the parent, and @@ -101,12 +161,9 @@ def initialize(resource) foreign_key = SetForeignKey.new(resource, relationship) foreign_key.parents << parent.save_command parents << foreign_key dependencies << foreign_key end end end class Update < Save @@ -116,6 +173,8 @@ def call end class Destroy < Command include HookableCommand def call resource.send(:_destroy, false) end @@ -134,13 +193,45 @@ def call end end class Hook < Command attr_reader :command def initialize(command) super(command.resource) @command = command end def call resource.send(hook_name) true end private def command_name command.class.name.split('::').last.downcase end end class BeforeHook < Hook def hook_name "before_#{command_name}_hook" end end class AfterHook < Hook def hook_name "after_#{command_name}_hook" end end class CommandDependencies include Enumerable, TSort def initialize @commands = [] @index_for = Hash.new do |hash, command| hash[command] = commands.index(command) end end @@ -198,23 +289,34 @@ def save_command end def save session do |session| # only allow a resource to be saved once return if session.include?(self) # add parents to the UoW parent_resources.each { |parent| parent.save } # add resource to the UoW session << self # add children to the UoW child_resources.each { |child| child.save } end end def destroy session { |session| session.destroy(self) } end def child_resources child_collections.flatten end def session original = Thread.current[:dm_session] session = Thread.current[:dm_session] ||= DataMapper::Session.new yield session # commit in the outer-most block session.commit if original.nil? @@ -225,10 +327,6 @@ def save ensure Thread.current[:dm_session] = original end end end @@ -243,6 +341,18 @@ class Person belongs_to :parent, self, :required => false has n, :children, self, :inverse => :parent before(:save) { puts "Before Saving #{name}" } after(:save) { puts "After Saving #{name}" } before(:create) { puts "Before Creating #{name}" } after(:create) { puts "After Creating #{name}" } before(:update) { puts "Before Updating #{name}" } after(:update) { puts "After Updating #{name}" } before(:destroy) { puts "Before Destroying #{name}" } after(:destroy) { puts "After Destroying #{name}" } end DataMapper.auto_migrate! @@ -259,3 +369,47 @@ class Person parent.children(:name => 'Alex Kubb').each { |child| child.name = 'Alexander Kubb' } parent.children(:name => 'Katie Kubb').each { |child| child.name = 'Katherine Kubb' } parent.save puts '-' * 80 #parent.children.destroy parent.destroy __END__ Getting close. The only thing I need to do is make sure before(:save) filters are fired before before(:create) and before(:update) filters, and than after(:save) filers are figred after after(:create) and after(:update) filters. OUTPUT: ~ (0.000150) SELECT sqlite_version(*) ~ (0.000189) DROP TABLE IF EXISTS "people" ~ (0.000039) PRAGMA table_info("people") ~ (0.000475) CREATE TABLE "people" ("id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "name" VARCHAR(50) NOT NULL, "parent_id" INTEGER) ~ (0.000133) CREATE INDEX "index_people_parent" ON "people" ("parent_id") ~ (0.000117) CREATE UNIQUE INDEX "unique_people_name" ON "people" ("name") -------------------------------------------------------------------------------- Before Creating Dan Kubb ~ (0.000109) INSERT INTO "people" ("name") VALUES ('Dan Kubb') After Creating Dan Kubb Before Creating Alex Kubb ~ (0.000066) INSERT INTO "people" ("name", "parent_id") VALUES ('Alex Kubb', 1) After Creating Alex Kubb Before Creating Katie Kubb ~ (0.000059) INSERT INTO "people" ("name", "parent_id") VALUES ('Katie Kubb', 1) After Creating Katie Kubb -------------------------------------------------------------------------------- Before Updating Barbara-Ann Kubb ~ (0.000133) UPDATE "people" SET "name" = 'Barbara-Ann Kubb' WHERE "id" = 1 After Updating Barbara-Ann Kubb Before Updating Alexander Kubb ~ (0.000071) UPDATE "people" SET "name" = 'Alexander Kubb' WHERE "id" = 2 After Updating Alexander Kubb Before Updating Katherine Kubb ~ (0.000069) UPDATE "people" SET "name" = 'Katherine Kubb' WHERE "id" = 3 After Updating Katherine Kubb -------------------------------------------------------------------------------- Before Destroying Barbara-Ann Kubb ~ (0.000070) DELETE FROM "people" WHERE "id" = 1 After Destroying Barbara-Ann Kubb -
dkubb revised this gist
Mar 18, 2010 . 1 changed file with 25 additions and 6 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -91,12 +91,20 @@ def self.new(resource) end class Create < Save def initialize(resource) super # make setting the FK dependent on saving the parent, and # make the current command dependent on the FK being set resource.send(:parent_relationships).each do |relationship| parent = relationship.get!(resource) foreign_key = SetForeignKey.new(resource, relationship) foreign_key.parents << parent.save_command parents << foreign_key end end def call resource.send(:_create) end end @@ -113,13 +121,26 @@ def call end end class SetForeignKey < Command attr_reader :relationship def initialize(resource, relationship) super(resource) @relationship = relationship end def call relationship.set(resource, relationship.get!(resource)) end end class CommandDependencies include Enumerable, TSort def initialize @commands = [] @index_for = Hash.new do |hash, command| hash[command] = commands.index(command) || -1 end end @@ -187,8 +208,6 @@ def save parent_resources.each { |parent| parent.save } # TODO: add before hook to be dependent on the parent(s) before hook session << self -
dkubb revised this gist
Mar 18, 2010 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -165,7 +165,7 @@ def tsort_each_child(node, &block) end def insertion_order lambda { |command| index_for[command] || raise("XXX: DEBUG: unknown command #{command.inspect}") } end end -
dkubb revised this gist
Mar 18, 2010 . 1 changed file with 68 additions and 54 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -17,9 +17,11 @@ def valid? end def <<(resource) if command = resource.save_command dependencies << command else # TOOD: remove from the dependencies list # - would need to remove *all* references from all commands end self end @@ -30,7 +32,7 @@ def concat(resources) end def delete(resource) dependencies << Delete.new(resource) self end @@ -45,62 +47,69 @@ def rollback freeze end class Command attr_reader :resource, :parents def initialize(resource) @resource = resource @parents = resource.send(:parent_resources).map do |parent| parent.save_command end end def valid? resource.valid? end def call raise NotImplementedError, "#{self.class}#call not implemented" end def ==(other) kind_of?(other.class) && resource == other.resource end def eql?(other) instance_of?(other.class) && resource.eql?(other.resource) end def hash self.class.hash ^ resource.model.hash end end class Save < Command def self.new(resource) if equal?(Save) klass = resource.new? ? Create : Update klass.new(resource) else super end end end class Create < Save def call # XXX: move this to an explicit hook resource.send(:parent_relationships).each do |relationship| relationship.set(resource, relationship.get!(resource)) end resource.send(:_create) end end class Update < Save def call resource.send(:_update) end end class Destroy < Command def call resource.send(:_destroy, false) end end @@ -156,20 +165,23 @@ def tsort_each_child(node, &block) end def insertion_order lambda { |command| index_for[command] } end end end module Resource def save_command Session::Save.new(self) end def save original = Thread.current[:dm_session] session = Thread.current[:dm_session] ||= DataMapper::Session.new # only allow a resource to be saved once return if session.dependencies.include?(save_command) # add parents to the UoW parent_resources.each { |parent| parent.save } @@ -187,8 +199,9 @@ def save # commit in the outer-most block session.commit if original.nil? rescue Exception session.rollback if original.nil? raise ensure Thread.current[:dm_session] = original @@ -211,10 +224,6 @@ class Person belongs_to :parent, self, :required => false has n, :children, self, :inverse => :parent end DataMapper.auto_migrate! @@ -224,5 +233,10 @@ def inspect parent.children.new(:name => 'Katie Kubb') puts '-' * 80 parent.save puts '-' * 80 parent.attributes = { :name => 'Barbara-Ann Kubb' } parent.children(:name => 'Alex Kubb').each { |child| child.name = 'Alexander Kubb' } parent.children(:name => 'Katie Kubb').each { |child| child.name = 'Katherine Kubb' } parent.save -
dkubb revised this gist
Mar 17, 2010 . 1 changed file with 3 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -84,21 +84,21 @@ def call class Create < Command def call puts "XXX: DEBUG: #{self.class.name.split('::').last} #{resource.name}" resource.send(:_create) end end class Update < Command def call puts "XXX: DEBUG: #{self.class.name.split('::').last} #{resource.name}" resource.send(:_update) end end class Destroy < Command def call puts "XXX: DEBUG: #{self.class.name.split('::').last} #{resource.name}" resource.send(:_destroy, false) end end -
dkubb revised this gist
Mar 17, 2010 . 1 changed file with 0 additions and 12 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -72,18 +72,6 @@ def initialize(resource) @resource = resource end def valid? resource.valid? end -
dkubb revised this gist
Mar 17, 2010 . 1 changed file with 3 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -186,13 +186,13 @@ def save # add parents to the UoW parent_resources.each { |parent| parent.save } # TODO: add before hook to be dependent on the parent(s) before hook # TODO: add command to set the FK, dependent on the parent(s) save # TODO: set save command to be dependent on the FK setting command session << self # TODO: add after hook to be dependent on the parent(s) after hook # add children to the UoW child_resources.each { |child| child.save } -
dkubb created this gist
Mar 17, 2010 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,240 @@ require 'tsort' require 'rubygems' require 'dm-core' require 'dm-validations' module DataMapper class Session attr_reader :dependencies def initialize(&block) @dependencies = CommandDependencies.new end def valid? dependencies.valid? end def <<(resource) if resource.new? then register_new(resource) elsif resource.dirty? then register_dirty(resource) else register_clean(resource) end self end def concat(resources) resources.each { |resource| self << resource } self end def delete(resource) register_deleted(resource) self end def commit dependencies.call freeze end def rollback # TODO: undo changes made in this session dependencies.clear freeze end private def register_new(resource) dependencies << Persistence::Create.new(resource) end def register_dirty(resource) dependencies << Persistence::Update.new(resource) end def register_clean(resource) # TOOD: remove from the dependencies list # - would need to remove *all* references from all commands end def register_deleted(resource) dependencies << Persistence::Delete.new(resource) end module Persistence class Command attr_reader :resource def initialize(resource) @resource = resource end def new? resource.new? end def saved? resource.saved? end def dirty? resource.send(:dirty_self?) end def valid? resource.valid? end def call raise NotImplementedError, "#{self.class}#call not implemented" end end class Create < Command def call puts "#{self.class.name.split('::').last} #{resource.name}" resource.send(:_create) end end class Update < Command def call puts "#{self.class.name.split('::').last} #{resource.name}" resource.send(:_update) end end class Destroy < Command def call puts "#{self.class.name.split('::').last} #{resource.name}" resource.send(:_destroy, false) end end end class CommandDependencies include Enumerable, TSort def initialize @commands = [] @index_for = Hash.new do |hash, command| hash[command] = commands.index(command) end end def clear @commands.clear @index_for.clear self end def <<(command) commands << command self end def valid? all? { |node| node.valid? } end def each tsort_each { |node| yield node } self end def call all? { |node| node.call } end private attr_reader :commands, :index_for def tsort_each_node(&block) commands.sort_by(&insertion_order).each(&block) end def tsort_each_child(node, &block) # tsort places child nodes before parent nodes, yet this makes # no sense from a UoW pov. Parents should always be saved first. # I have no idea why this method in tsort is named this way. if commands.include?(node) && node.respond_to?(:parents) node.parents.sort_by(&insertion_order).each(&block) end end def insertion_order lambda { |command| index_for[command] || raise("command #{command.inspect} was not found") } end end end module Resource def save original = Thread.current[:dm_session] session = Thread.current[:dm_session] ||= DataMapper::Session.new # only allow a resource to be seen once return if session.dependencies.any? { |command| command.resource == self } # add parents to the UoW parent_resources.each { |parent| parent.save } # TODO: add before hook to be dependent on the parent before hook # TODO: add command to set the FK, dependent on the parent save # TODO: set save command to be dependent on the FK setting command session << self # TODO: add after hook to be dependent on the parent after hook # add children to the UoW child_resources.each { |child| child.save } # commit in the outer-most block session.commit if original.nil? rescue Exception rollback raise ensure Thread.current[:dm_session] = original end def child_resources child_collections.flatten end end end DataMapper::Logger.new($stdout, :debug) DataMapper.setup(:default, 'sqlite3::memory:') class Person include DataMapper::Resource property :id, Serial property :name, String, :length => 1..50, :required => true, :unique => true, :unique_index => true belongs_to :parent, self, :required => false has n, :children, self, :inverse => :parent def inspect name end end DataMapper.auto_migrate! parent = Person.new(:name => 'Dan Kubb') parent.children.new(:name => 'Alex Kubb') parent.children.new(:name => 'Katie Kubb') puts '-' * 80 parent.save