Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' of git@github.com:sam/dm-core

  • Loading branch information...
commit a2e7c57f5889044b2203d187becb59ed5aafc177 2 parents 01d1e2a + ad25cbf
@david david authored
Showing with 651 additions and 10,536 deletions.
  1. +14 −7 Rakefile
  2. +0 −111 burn/associations.rb
  3. +0 −160 burn/associations/belongs_to_association.rb
  4. +0 −437 burn/associations/has_and_belongs_to_many_association.rb
  5. +0 −283 burn/associations/has_many_association.rb
  6. +0 −143 burn/associations/has_n_association.rb
  7. +0 −47 burn/associations/reference.rb
  8. +0 −107 burn/callbacks.rb
  9. +0 −166 burn/container.rb
  10. +0 −109 burn/context.rb
  11. +0 −145 burn/embedded_value.rb
  12. +0 −7 burn/example.rb
  13. +0 −155 burn/migration.rb
  14. +0 −13 burn/model.rb
  15. +0 −769 burn/persistable.rb
  16. +0 −1  burn/plugins/.gitignore
  17. +0 −23 burn/plugins/can_has_sphinx/LICENSE
  18. +0 −4 burn/plugins/can_has_sphinx/README
  19. +0 −1  burn/plugins/can_has_sphinx/REVISION
  20. +0 −22 burn/plugins/can_has_sphinx/Rakefile
  21. +0 −1  burn/plugins/can_has_sphinx/init.rb
  22. +0 −1  burn/plugins/can_has_sphinx/install.rb
  23. +0 −123 burn/plugins/can_has_sphinx/lib/acts_as_sphinx.rb
  24. +0 −460 burn/plugins/can_has_sphinx/lib/sphinx.rb
  25. +0 −47 burn/plugins/can_has_sphinx/scripts/sphinx.sh
  26. +0 −45 burn/plugins/can_has_sphinx/tasks/acts_as_sphinx_tasks.rake
  27. +0 −67 burn/spec/acts_as_tree_spec.rb
  28. +0 −98 burn/spec/associations/belongs_to_association_spec.rb
  29. +0 −377 burn/spec/associations/has_and_belongs_to_many_association_spec.rb
  30. +0 −337 burn/spec/associations/has_many_association_spec.rb
  31. +0 −52 burn/spec/attributes_spec.rb
  32. +0 −100 burn/spec/auto_migrations_spec.rb
  33. +0 −186 burn/spec/callbacks_spec.rb
  34. +0 −41 burn/spec/coersion_spec.rb
  35. +0 −114 burn/spec/column_spec.rb
  36. +0 −83 burn/spec/container_spec.rb
  37. +0 −45 burn/spec/count_command_spec.rb
  38. +0 −18 burn/spec/database_spec.rb
  39. +0 −27 burn/spec/dataobjects_spec.rb
  40. +0 −11 burn/spec/delete_command_spec.rb
  41. +0 −161 burn/spec/embedded_value_spec.rb
  42. +0 −33 burn/spec/fixtures/animals.yaml
  43. +0 −2  burn/spec/fixtures/animals_exhibits.yaml
  44. +0 −5 burn/spec/fixtures/careers.yaml
  45. +0 −1  burn/spec/fixtures/comments.yaml
  46. +0 −90 burn/spec/fixtures/exhibits.yaml
  47. +0 −6 burn/spec/fixtures/fruit.yaml
  48. +0 −37 burn/spec/fixtures/people.yaml
  49. +0 −3  burn/spec/fixtures/posts.yaml
  50. +0 −13 burn/spec/fixtures/projects.yaml
  51. +0 −5 burn/spec/fixtures/sections.yaml
  52. +0 −6 burn/spec/fixtures/serializers.yaml
  53. +0 −6 burn/spec/fixtures/tasks.yaml
  54. +0 −2  burn/spec/fixtures/tasks_tasks.yaml
  55. +0 −1  burn/spec/fixtures/tomatoes.yaml
  56. +0 −1  burn/spec/fixtures/users.yaml
  57. +0 −24 burn/spec/fixtures/zoos.yaml
  58. +0 −149 burn/spec/is_a_tree_spec.rb
  59. +0 −16 burn/spec/legacy_spec.rb
  60. +0 −322 burn/spec/load_command_spec.rb
  61. +0 −26 burn/spec/magic_columns_spec.rb
  62. +0 −267 burn/spec/migration_spec.rb
  63. +0 −12 burn/spec/models/animal.rb
  64. +0 −8 burn/spec/models/candidate.rb
  65. +0 −7 burn/spec/models/career.rb
  66. +0 −8 burn/spec/models/chain.rb
  67. +0 −6 burn/spec/models/comment.rb
  68. +0 −14 burn/spec/models/exhibit.rb
  69. +0 −7 burn/spec/models/fence.rb
  70. +0 −8 burn/spec/models/fruit.rb
  71. +0 −9 burn/spec/models/job.rb
  72. +0 −31 burn/spec/models/person.rb
  73. +0 −14 burn/spec/models/post.rb
  74. +0 −41 burn/spec/models/project.rb
  75. +0 −5 burn/spec/models/sales_person.rb
  76. +0 −8 burn/spec/models/section.rb
  77. +0 −5 burn/spec/models/serializer.rb
  78. +0 −9 burn/spec/models/task.rb
  79. +0 −25 burn/spec/models/tomato.rb
  80. +0 −13 burn/spec/models/user.rb
  81. +0 −13 burn/spec/models/zoo.rb
  82. +0 −36 burn/spec/natural_key_spec.rb
  83. +0 −38 burn/spec/paranoia_spec.rb
  84. +0 −479 burn/spec/persistable_spec.rb
  85. +0 −96 burn/spec/postgres_spec.rb
  86. +0 −77 burn/spec/query_spec.rb
  87. +0 −94 burn/spec/save_command_spec.rb
  88. +0 −5 burn/spec/schema_spec.rb
  89. +0 −19 burn/spec/serialize_spec.rb
  90. +0 −43 burn/spec/single_table_inheritance_spec.rb
  91. +0 −41 burn/spec/support/inflector_spec.rb
  92. +0 −61 burn/spec/support/serialization_spec.rb
  93. +0 −15 burn/spec/support/silence_spec.rb
  94. +0 −28 burn/spec/support/symbolic_operators_spec.rb
  95. +0 −66 burn/spec/support/typed_set_spec.rb
  96. +0 −79 burn/spec/table_spec.rb
  97. +0 −81 burn/spec/types/string.rb
  98. +0 −55 burn/spec/validates_confirmation_of_spec.rb
  99. +0 −77 burn/spec/validates_format_of_spec.rb
  100. +0 −117 burn/spec/validates_length_of_spec.rb
  101. +0 −92 burn/spec/validates_uniqueness_of_spec.rb
  102. +0 −59 burn/spec/validations/number_validator.rb
  103. +0 −14 burn/spec/validations/string_validator.rb
  104. +0 −141 burn/spec/validations_spec.rb
  105. +0 −134 burn/sql/coersion.rb
  106. +0 −545 burn/sql/commands/load_command.rb
  107. +0 −34 burn/sql/mappings/associations_set.rb
  108. +0 −279 burn/sql/mappings/column.rb
  109. +0 −172 burn/sql/mappings/conditions.rb
  110. +0 −459 burn/sql/mappings/table.rb
  111. +0 −24 burn/sql/quoting.rb
  112. +0 −117 burn/support/connection_pool.rb
  113. +0 −10 burn/support/silence.rb
  114. +0 −68 burn/support/typed_set.rb
  115. +0 −12 burn/validatable_extensions/errors.rb
  116. +0 −7 burn/validatable_extensions/macros.rb
  117. +0 −62 burn/validatable_extensions/validatable_instance_methods.rb
  118. +0 −18 burn/validatable_extensions/validation_base.rb
  119. +0 −43 burn/validatable_extensions/validations/formats/email.rb
  120. +0 −7 burn/validatable_extensions/validations/validates_acceptance_of.rb
  121. +0 −7 burn/validatable_extensions/validations/validates_confirmation_of.rb
  122. +0 −7 burn/validatable_extensions/validations/validates_each.rb
  123. +0 −28 burn/validatable_extensions/validations/validates_format_of.rb
  124. +0 −15 burn/validatable_extensions/validations/validates_length_of.rb
  125. +0 −7 burn/validatable_extensions/validations/validates_numericality_of.rb
  126. +0 −7 burn/validatable_extensions/validations/validates_presence_of.rb
  127. +0 −7 burn/validatable_extensions/validations/validates_true_for.rb
  128. +0 −40 burn/validatable_extensions/validations/validates_uniqueness_of.rb
  129. +0 −20 burn/validations.rb
  130. +2 −0  environment.rb
  131. +19 −12 lib/data_mapper.rb
  132. +9 −9 lib/data_mapper/adapters/data_objects_adapter.rb
  133. +11 −13 lib/data_mapper/adapters/sqlite3_adapter.rb
  134. +76 −19 lib/data_mapper/associations.rb
  135. +3 −0  lib/data_mapper/associations/many_to_many.rb
  136. +4 −2 lib/data_mapper/associations/many_to_one.rb
  137. +3 −0  lib/data_mapper/associations/one_to_many.rb
  138. +6 −5 lib/data_mapper/associations/one_to_one.rb
  139. +12 −4 lib/data_mapper/associations/parent_to_child_association.rb
  140. +37 −27 lib/data_mapper/loaded_set.rb
  141. +9 −15 lib/data_mapper/logger.rb
  142. +1 −17 lib/data_mapper/property.rb
  143. +27 −27 lib/data_mapper/query.rb
  144. +1 −0  lib/data_mapper/repository.rb
  145. +11 −11 lib/data_mapper/resource.rb
  146. +37 −0 lib/data_mapper/types/enum.rb
  147. +48 −0 lib/data_mapper/types/flag.rb
  148. +9 −3 spec/integration/postgres_adapter_spec.rb
  149. +152 −0 spec/unit/associations_spec.rb
  150. +0 −1  spec/unit/loaded_set_spec.rb
  151. +7 −6 spec/unit/property_spec.rb
  152. +7 −12 spec/unit/resource_spec.rb
  153. +64 −0 spec/unit/types/enum_spec.rb
  154. +82 −0 spec/unit/types/flag_spec.rb
  155. +0 −49 tasks/fixtures.rb
View
21 Rakefile
@@ -13,10 +13,13 @@ require Pathname(__FILE__).dirname.expand_path + 'lib/data_mapper/support/kernel
Pathname.glob(__DIR__ + 'tasks/**/*.rb') { |t| require t }
-task :default => 'dm:spec'
-
+task :default => 'dm:spec'
+task :spec => 'dm:spec'
task :environment => 'dm:environment'
+desc 'Remove all package, rdocs and spec products'
+task :clobber_all => %w[ clobber_package clobber_rdoc dm:clobber_spec ]
+
namespace :dm do
desc "Setup Environment"
task :environment do
@@ -55,7 +58,7 @@ namespace :dm do
desc "Profile DataMapper"
task :profile do
- load __DIR__ + 'script/profile_data_mapper.rb'
+ load __DIR__ + 'script/profile.rb'
end
end
@@ -80,6 +83,7 @@ end
PROJECT = "dm-core"
+desc 'List all package files'
task :ls do
puts PACKAGE_FILES
end
@@ -132,13 +136,16 @@ task :rubyforge => [ :rdoc, :gem ] do
Rake::SshDirPublisher.new("#{ENV['RUBYFORGE_USER']}@rubyforge.org", "/var/www/gforge-projects/#{PROJECT}", 'doc').upload
end
+desc "Install #{PROJECT}"
task :install => :package do
sh %{sudo gem install pkg/#{PROJECT}-#{PACKAGE_VERSION}}
end
-namespace :dev do
- desc "Install for development (for windows)"
- task :winstall => :gem do
- system %{gem install --no-rdoc --no-ri -l pkg/#{PROJECT}-#{PACKAGE_VERSION}.gem}
+if RUBY_PLATFORM.match(/mswin32|cygwin|mingw|bccwin/)
+ namespace :dev do
+ desc 'Install for development (for windows)'
+ task :winstall => :gem do
+ system %{gem install --no-rdoc --no-ri -l pkg/#{PROJECT}-#{PACKAGE_VERSION}.gem}
+ end
end
end
View
111 burn/associations.rb
@@ -1,111 +0,0 @@
-require File.join(File.dirname(__FILE__), 'associations', 'reference')
-require File.join(File.dirname(__FILE__), 'associations', 'has_many_association')
-require File.join(File.dirname(__FILE__), 'associations', 'belongs_to_association')
-require File.join(File.dirname(__FILE__), 'associations', 'has_and_belongs_to_many_association')
-
-module DataMapper
- module Associations
-
- # Extends +base+ with methods for setting up associations between different models.
- def self.included(base)
- base.extend(ClassMethods)
- end
-
- module ClassMethods
-
- def associations
- @associations
- end
-
- # Adds the following methods for query of a single associated object:
- # * <tt>collection(</tt> - returns a set containing the associated objects. Returns
- # an empty set if no objects are found.
- # * <tt>collection << object</tt> - adds an object to the collection.
- # * <tt>collection = [objects]</tt> - replaces the collections content by deleting and
- # adding objects as appropriate.
- # * <tt>collection.empty?</tt> - returns +true+ if there is no associated objects.
- # * <tt>collection.size</tt> - returns the number of associated objects.
- #
- # Options are:
- # * <tt>:class</tt> - specify the class name of the association. So has_many :animals will by
- # default be linked to the Animal class, but if you want the association to use a
- # different class, you'll have to specify it with this option. DM also lets you specify
- # this with <tt>:class_name</tt>, for AR compability.
- # * <tt>:foreign_key</tt> - specify the foreign key used for the association. By default
- # this is guessed to be the name of this class in lower-case and _id suffixed.
- # * <tt>:dependent</tt> - if set to :destroy, the associated objects have their destroy! methods
- # called in a chain meaning all callbacks are also called for each object.
- # if set to :delete, the associated objects are deleted from the database
- # without their callbacks being triggered.
- # if set to :protect and the collection is not empty an AssociatedProtectedError will be raised.
- # if set to :nullify, the associated objects foreign key is set to NULL.
- # default is :nullify
- #
- # Option examples:
- # has_many :favourite_fruits, :class => 'Fruit', :dependent => :destroy
- def has_many(association_name, options = {})
- #self.associations << HasManyAssociation.new(self, association_name, options)
- end
-
- # Adds the following methods for query of a single associated object:
- # * <tt>association(</tt> - returns the associated object. Returns an empty set if no
- # object is found.
- # * <tt>association=(associate)</tt> - assigns the associate object, extracts the
- # primary key, and sets it as the foreign key.
- # * <tt>association.nil?</tt> - returns +true+ if there is no associated object.
- #
- # The declaration can also include an options hash to specialize the behavior of the
- # association.
- #
- # Options are:
- # * <tt>:class</tt> - specify the class name of the association. So has_one :animal will by
- # default be linked to the Animal class, but if you want the association to use a
- # different class, you'll have to specify it with this option. DM also lets you specify
- # this with <tt>:class_name</tt>, for AR compability.
- # * <tt>:foreign_key</tt> - specify the foreign key used for the association. By default
- # this is guessed to be the name of this class in lower-case and _id suffixed.
- # * <tt>:dependent</tt> - has_one is secretly a has_many so this option performs the same
- # as the has_many
- #
- # Option examples:
- # has_one :favourite_fruit, :class => 'Fruit', :foreign_key => 'devourer_id'
- def has_one(association_name, options = {})
- #self.associations << HasManyAssociation.new(self, association_name, options)
- end
-
- # Adds the following methods for query of a single associated object:
- # * <tt>association(</tt> - returns the associated object. Returns an empty set if no
- # object is found.
- # * <tt>association=(associate)</tt> - assigns the associate object, extracts the
- # primary key, and sets it as the foreign key.
- # * <tt>association.nil?</tt> - returns +true+ if there is no associated object.
- # * <tt>build_association</tt> - builds a new object of the associated type, without
- # saving it to the database.
- # * <tt>create_association</tt> - creates and saves a new object of the associated type.
- def belongs_to(association_name, options = {})
- #self.associations << BelongsToAssociation.new(self, association_name, options)
- end
-
- # Associates two classes via an intermediate join table.
- #
- # Options are:
- # * <tt>:dependent</tt> - if set to :destroy, the associated objects have their destroy! methods
- # called in a chain meaning all callbacks are also called for each object. Beware that this
- # is a cascading delete and will affect all records that have a remote relationship with the
- # record being destroyed!
- # if set to :delete, the associated objects are deleted from the database without their
- # callbacks being triggered. This does NOT cascade the deletes. All associated objects will
- # have their relationships removed from other records before being deleted. The record calling
- # destroy will only delete those records directly associated to it.
- # if set to :protect and the collection is not empty an AssociatedProtectedError will be raised.
- # if set to :nullify, the join table will have the relationship records removed which is
- # effectively nullifying the foreign key.
- # default is :nullify
- def has_and_belongs_to_many(association_name, options = {})
- #self.associations << HasAndBelongsToManyAssociation.new(self, association_name, options)
- end
-
- end
-
- end
-end
View
160 burn/associations/belongs_to_association.rb
@@ -1,160 +0,0 @@
-require File.join(File.dirname(__FILE__), 'has_n_association')
-
-module DataMapper
- module Associations
-
- class BelongsToAssociation < HasNAssociation
-
- def define_accessor(klass)
- klass.class_eval <<-EOS
-
- def create_#{@association_name}(options = {})
- #{@association_name}_association.create(options)
- end
-
- def build_#{@association_name}(options = {})
- #{@association_name}_association.build(options)
- end
-
- def #{@association_name}
- #{@association_name}_association.instance
- end
-
- def #{@association_name}=(value)
- #{@association_name}_association.set(value)
- end
-
- private
- def #{@association_name}_association
- @#{@association_name} || (@#{@association_name} = DataMapper::Associations::BelongsToAssociation::Instance.new(self, #{@association_name.inspect}))
- end
- EOS
- end
-
- # Reverse the natural order for BelongsToAssociations
- alias constant associated_constant
- def associated_constant
- @constant
- end
-
- def foreign_key_name
- @foreign_key_name || @foreign_key_name = (@options[:foreign_key] || "#{name}_#{key_table.key.name}".to_sym)
- end
-
- def complementary_association
- @complementary_association || begin
- @complementary_association = key_table.associations.find do |mapping|
- mapping.is_a?(HasManyAssociation) &&
- mapping.foreign_key_column.name == foreign_key_column.name &&
- mapping.associated_table.name == associated_table.name
- end
-
- if @complementary_association
- class << self
- attr_accessor :complementary_association
- end
- end
-
- return @complementary_association
- end
- end
-
- def to_sql # :nodoc:
- "JOIN #{key_table.to_sql} ON #{foreign_key_column.to_sql(true)} = #{primary_key_column.to_sql(true)}"
- end
-
- class Instance < Associations::Reference
-
- def dirty?(cleared = ::Set.new)
- @associated && (@new_member || @key_not_set)
- end
-
- def validate_recursively(event, cleared)
- @associated.nil? || cleared.include?(@associated) || @associated.validate_recursively(event, cleared)
- end
-
- def save_without_validation(database_context, cleared)
- @new_member = false
- unless @associated.nil?
- @instance.instance_variable_set(
- association.foreign_key_column.instance_variable_name,
- @associated.key
- )
- @instance.database_context.adapter.save_without_validation(database_context, @instance, cleared)
- end
- end
-
- def reload!
- @new_member = false
- @associated = nil
- instance
- end
-
- def instance
- @associated || @associated = begin
- if @instance.loaded_set.nil?
- nil
- else
-
- # Temp variable for the instance variable name.
- fk = association.foreign_key_column.to_sym
-
- set = @instance.loaded_set.group_by { |instance| instance.send(fk) }
-
- @instance.database_context.all(association.constant, association.associated_table.key.to_sym => set.keys).each do |assoc|
- set[assoc.key].each do |primary_instance|
- primary_instance.send("#{@association_name}_association").shallow_append(assoc)
- end
- end
-
- @associated
- end
- end
- end
-
- def create(options)
- @associated = association.associated_constant.create(options)
- end
-
- def build(options)
- @associated = association.associated_constant.new(options)
- end
-
- def setter_method
- "#{@association_name}=".to_sym
- end
-
- def set(member)
- shallow_append(member)
-
- if complement = association.complementary_association
- member.send(complement.name).shallow_append(@instance)
- end
-
- return self
- end
-
- def shallow_append(val)
- raise RecursionError.new if val == @instance
- @instance.instance_variable_set(association.foreign_key_column.instance_variable_name, val.key)
- @associated = val
- @key_not_set = true if val.key.nil?
- return self
- end
-
- def deactivate
- end
-
- private
-
- def ensure_foreign_key!
- if @associated
- @instance.instance_variable_set(association.foreign_key.instance_variable_name, @associated.key)
- end
- end
-
- end # class Instance
- end
-
- end
-end
View
437 burn/associations/has_and_belongs_to_many_association.rb
@@ -1,437 +0,0 @@
-module DataMapper
- module Associations
-
- class HasAndBelongsToManyAssociation
-
- attr_reader :adapter
-
- def initialize(klass, association_name, options)
- @adapter = repository.adapter
- @key_table = adapter.table(klass)
- @self_referential = (association_name.to_s == @key_table.name)
- @association_name = association_name.to_sym
- @options = options
-
- define_accessor(klass)
- end
-
- # def key_table
- # @key_table
- # end
-
- def name
- @association_name
- end
-
- def dependency
- @options[:dependent]
- end
-
- def foreign_name
- @foreign_name || (@foreign_name = (@options[:foreign_name] || @key_table.name).to_sym)
- end
-
- def self_referential?
- @self_referential
- end
-
- def constant
- @associated_class || @associated_class = begin
-
- if @options.has_key?(:class) || @options.has_key?(:class_name)
- associated_class_name = (@options[:class] || @options[:class_name])
- if associated_class_name.kind_of?(String)
- Kernel.const_get(Inflector.classify(associated_class_name))
- else
- associated_class_name
- end
- else
- Kernel.const_get(Inflector.classify(@association_name))
- end
-
- end
- end
-
- def activate!(force = false)
- join_columns.each {|column| column unless join_table.mapped_column_exists?(column.name)}
- join_table.create!(force)
- end
-
- def associated_columns
- associated_table.columns.reject { |column| column.lazy? } + join_columns
- end
-
- def join_columns
- [ left_foreign_key, right_foreign_key ]
- end
-
- def associated_table
- @associated_table || (@associated_table = adapter.table(constant))
- end
-
- def join_table
- @join_table || @join_table = begin
- join_table_name = @options[:join_table] ||
- [ @key_table.name.to_s, repository.schema[constant].name.to_s ].sort.join('_')
-
- adapter.table(join_table_name)
- end
- end
-
- def left_foreign_key
- @left_foreign_key || @left_foreign_key = begin
- join_table.add_column(
- (@options[:left_foreign_key] || @key_table.default_foreign_key),
- :integer, :nullable => true, :key => true)
- end
- end
-
- def right_foreign_key
- if self_referential?
- @options[:right_foreign_key] ||= ["related_", associated_table.default_foreign_key].to_s
- end
-
- @right_foreign_key || @right_foreign_key = begin
- join_table.add_column(
- (@options[:right_foreign_key] || associated_table.default_foreign_key),
- :integer, :nullable => true, :key => true)
- end
- end
-
- def to_sql
- <<-EOS.compress_lines
- JOIN #{join_table.to_sql} ON
- #{left_foreign_key.to_sql(true)} = #{@key_table.key.to_sql(true)}
- JOIN #{associated_table.to_sql} ON
- #{associated_table.key.to_sql(true)} = #{right_foreign_key.to_sql(true)}
- EOS
- end
-
- def to_shallow_sql
- if self_referential?
- <<-EOS.compress_lines
- JOIN #{join_table.to_sql} ON
- #{right_foreign_key.to_sql(true)} = #{@key_table.key.to_sql(true)}
- EOS
- else
- <<-EOS.compress_lines
- JOIN #{join_table.to_sql} ON
- #{left_foreign_key.to_sql(true)} = #{@key_table.key.to_sql(true)}
- EOS
- end
- end
-
- def to_insert_sql
- <<-EOS.compress_lines
- INSERT INTO #{join_table.to_sql}
- (#{left_foreign_key.to_sql}, #{right_foreign_key.to_sql})
- VALUES
- EOS
- end
-
- def to_delete_sql
- <<-EOS.compress_lines
- DELETE FROM #{join_table.to_sql}
- WHERE #{left_foreign_key.to_sql} = ?
- EOS
- end
-
- def to_delete_set_sql
- <<-EOS.compress_lines
- DELETE FROM #{join_table.to_sql}
- WHERE #{left_foreign_key.to_sql} IN ?
- OR #{right_foreign_key.to_sql} IN ?
- EOS
- end
-
- def to_delete_members_sql
- <<-EOS.compress_lines
- DELETE FROM #{associated_table.to_sql}
- WHERE #{associated_table.key.to_sql} IN ?
- EOS
- end
-
- def to_delete_member_sql
- <<-EOS
- DELETE FROM #{join_table.to_sql}
- WHERE #{left_foreign_key.to_sql} = ?
- AND #{right_foreign_key.to_sql} = ?
- EOS
- end
-
- def to_disassociate_sql
- <<-EOS
- UPDATE #{join_table.to_sql}
- SET #{left_foreign_key.to_sql} = NULL
- WHERE #{left_foreign_key.to_sql} = ?
- EOS
- end
-
- # Define the association instance method (i.e. Project#tasks)
- def define_accessor(klass)
- klass.class_eval <<-EOS
- def #{@association_name}
- @#{@association_name} || (@#{@association_name} = HasAndBelongsToManyAssociation::Set.new(self, #{@association_name.inspect}))
- end
-
- def #{@association_name}=(value)
- #{@association_name}.set(value)
- end
-
- private
- def #{@association_name}_keys=(value)
- #{@association_name}.clear
-
- associated_constant = #{@association_name}.association.constant
- associated_table = #{@association_name}.association.associated_table
- associated_constant.all(associated_table.key => [*value]).each do |entry|
- #{@association_name} << entry
- end
- end
- EOS
- end
-
- class Set < Associations::Reference
-
- include Enumerable
-
- def each
- entries.each { |item| yield item }
- end
-
- def size
- entries.size
- end
- alias length size
-
- def count
- entries.size
- end
-
- def [](key)
- entries[key]
- end
-
- def empty?
- entries.empty?
- end
-
- def dirty?(cleared = ::Set.new)
- return false unless @entries
- @entries.any? {|item| cleared.include?(item) || item.dirty?(cleared) } || @associated_keys != @entries.map { |entry| entry.keys }
- end
-
- def validate_recursively(event, cleared)
- @entries.blank? || @entries.all? { |item| cleared.include?(item) || item.validate_recursively(event, cleared) }
- end
-
- def save_without_validation(database_context, cleared)
- unless @entries.nil?
-
- if dirty?(cleared)
- adapter = @instance.database_context.adapter
-
- adapter.connection do |db|
- command = db.create_command(association.to_delete_sql)
- command.execute_non_query(@instance.key)
- end
-
- unless @entries.empty?
- if adapter.batch_insertable?
- sql = association.to_insert_sql
- values = []
- keys = []
-
- @entries.each do |member|
- adapter.save_without_validation(database_context, member, cleared)
- values << "(?, ?)"
- keys << @instance.key << member.key
- end
-
- adapter.connection do |db|
- command = db.create_command(sql << ' ' << values.join(', '))
- command.execute_non_query(*keys)
- end
-
- else # adapter doesn't support batch inserts...
- @entries.each do |member|
- adapter.save_without_validation(database_context, member, cleared)
- end
-
- # Just to keep the same flow as the batch-insert mode.
- @entries.each do |member|
- adapter.connection do |db|
- command = db.create_command("#{association.to_insert_sql} (?, ?)")
- command.execute_non_query(@instance.key, member.key)
- end
- end
- end # if adapter.batch_insertable?
- end # unless @entries.empty?
- end # if dirty?
- end
- end
-
- def <<(member)
- return nil unless member
-
- if member.is_a?(Enumerable)
- member.each { |entry| entries << entry }
- else
- entries << member
- end
- end
-
- def clear
- @entries = Support::TypedSet.new(association.constant)
- end
-
- def reload!
- @entries = nil
- end
-
- def delete(member)
- if found_member = entries.detect { |entry| entry == member }
- entries.delete?(found_member)
- @instance.database_context.adapter.connection do |db|
- command = db.create_command(association.to_delete_member_sql)
- command.execute_non_query(@instance.key, member.key)
- end
- member
- else
- nil
- end
- end
-
- def method_missing(symbol, *args, &block)
- if entries.respond_to?(symbol)
- entries.send(symbol, *args, &block)
- elsif association.associated_table.associations.any? { |assoc| assoc.name == symbol }
- results = []
- each do |item|
- unless (val = item.send(symbol)).blank?
- results << (val.is_a?(Enumerable) ? val.entries : val)
- end
- end
- results.flatten
- else
- super
- end
- end
-
- def entries
- @entries || @entries = begin
-
- if @instance.loaded_set.nil?
- Support::TypedSet.new(association.constant)
- else
-
- associated_items = Hash.new { |h,k| h[k] = [] }
- left_key_index = nil
- association_constant = association.constant
- left_foreign_key = association.left_foreign_key
-
- matcher = lambda do |instance,columns,row|
-
- # Locate the column for the left-key.
- unless left_key_index
- columns.each_with_index do |column, index|
- if column.name == association.left_foreign_key.name
- left_key_index = index
- break
- end
- end
- end
-
- if instance.kind_of?(association_constant)
- associated_items[left_foreign_key.type_cast_value(row[left_key_index])] << instance
- end
- end
-
- @instance.database_context.all(association.constant,
- left_foreign_key => @instance.loaded_set.map(&:key),
- :shallow_include => association.foreign_name,
- :intercept_load => matcher
- )
-
- # do stsuff with associated_items hash.
- setter_method = "#{@association_name}=".to_sym
-
- @instance.loaded_set.each do |entry|
- entry.send(setter_method, associated_items[entry.key])
- end # @instance.loaded_set.each
-
- @entries
- end
- end
- end
-
- def set(results)
- if results.is_a?(Support::TypedSet)
- @entries = results
- else
- @entries = Support::TypedSet.new(association.constant)
- [*results].each { |item| @entries << item }
- end
- @associated_keys = @entries.map { |entry| entry.key }
- return @entries
- end
-
- def inspect
- entries.inspect
- end
-
- def first
- entries.entries.first
- end
-
- def last
- entries.entries.last
- end
-
- def deactivate
- case association.dependency
- when :destroy
- entries.each do |member|
- member.destroy! unless member.new_record?
- end
- when :delete
- delete_association
- when :protect
- unless entries.empty?
- raise AssociationProtectedError.new("You cannot delete this model while it has items associated with it.")
- end
- when :nullify
- nullify_association
- else
- nullify_association
- end
- end
-
- def delete_association
- @instance.database_context.adapter.connection do |db|
- associated_keys = entries.collect do |item|
- item.key unless item.new_record?
- end.compact
- parameters = [@instance.key] + associated_keys
-
- sql = association.to_delete_set_sql
- db.create_command(sql).execute_non_query(*[parameters, parameters])
-
- sql = association.to_delete_members_sql
- db.create_command(sql).execute_non_query(associated_keys)
- end
- end
-
- def nullify_association
- @instance.database_context.adapter.connection do |db|
- sql = association.to_delete_sql
- parameters = [@instance.key]
- db.create_command(sql).execute_non_query(*parameters)
- end
- end
- end
-
- end # class HasAndBelongsToManyAssociation
-
- end # module Associations
-end # module DataMapper
View
283 burn/associations/has_many_association.rb
@@ -1,283 +0,0 @@
-require File.join(File.dirname(__FILE__), 'has_n_association')
-
-module DataMapper
- module Associations
-
- class HasManyAssociation < HasNAssociation
-
- def dependency
- @options[:dependent]
- end
-
- # Define the association instance method (i.e. Project#tasks)
- def define_accessor(klass)
- klass.class_eval <<-EOS
- def #{@association_name}
- @#{@association_name} || (@#{@association_name} = DataMapper::Associations::HasManyAssociation::Set.new(self, #{@association_name.inspect}))
- end
-
- def #{@association_name}=(value)
- #{@association_name}.set(value)
- end
-
- private
- def #{@association_name}_keys=(value)
- #{@association_name}.clear
-
- associated_constant = #{@association_name}.association.associated_constant
- associated_table = #{@association_name}.association.associated_table
- associated_constant.all(associated_table.key => [*value]).each do |entry|
- #{@association_name} << entry
- end
- end
- EOS
- end
-
- def to_disassociate_sql
- "UPDATE #{associated_table.to_sql} SET #{foreign_key_column.to_sql} = NULL WHERE #{foreign_key_column.to_sql} = ?"
- end
-
- def to_delete_sql
- "DELETE FROM #{associated_table.to_sql} WHERE #{foreign_key_column.to_sql} = ?"
- end
-
- def instance_variable_name
- class << self
- attr_reader :instance_variable_name
- end
-
- @instance_variable_name = "@#{@association_name}"
- end
-
- class Set < Associations::Reference
-
- include Enumerable
-
- # Returns true if the association has zero items
- def nil?
- loaded_members.blank?
- end
-
- def dirty?(cleared = ::Set.new)
- loaded_members.any? { |member| cleared.include?(member) || member.dirty?(cleared) }
- end
-
- def validate_recursively(event, cleared)
- loaded_members.all? { |member| cleared.include?(member) || member.validate_recursively(event, cleared) }
- end
-
- def save_without_validation(database_context, cleared)
-
- adapter = @instance.database_context.adapter
-
- members = loaded_members
-
- adapter.connection do |db|
-
- sql = association.to_disassociate_sql
- parameters = [@instance.key]
-
- member_keys = members.map { |member| member.key }.compact
-
- unless member_keys.empty?
- sql << " AND #{association.associated_table.key} NOT IN ?"
- parameters << member_keys
- end
-
- db.create_command(sql).execute_non_query(*parameters)
- end
-
- unless members.blank?
-
- setter_method = "#{@association_name}=".to_sym
- ivar_name = association.foreign_key_column.instance_variable_name
- original_value_name = association.foreign_key_column.name
-
- members.each do |member|
- member.original_values.delete(original_value_name)
- member.instance_variable_set(ivar_name, @instance.key)
- @instance.database_context.adapter.save_without_validation(database_context, member, cleared)
- end
- end
- end
-
- def each
- items.each { |item| yield item }
- end
-
- # Builds a new item and returns it.
- def build(options)
- item = association.associated_constant.new(options)
- self << item
- item
- end
-
- # Builds and saves a new item, then returns it.
- def create(options)
- item = build(options)
- item.save
- item
- end
-
- def set(value)
- values = value.is_a?(Enumerable) ? value : [value]
- @items = Support::TypedSet.new(association.associated_constant)
- values.each do |item|
- self << item
- end
- end
-
- # Adds a new item to the association. The entire item collection is then returned.
- def <<(member)
- shallow_append(member)
-
- if complement = association.complementary_association
- member.send("#{complement.name}_association").shallow_append(@instance)
- end
-
- return self
- end
-
- def clear
- @pending_members = nil
- @items = Support::TypedSet.new(association.associated_constant)
- end
-
- def shallow_append(member)
- if @items
- self.items << member
- else
- pending_members << member
- end
- return self
- end
-
- def method_missing(symbol, *args, &block)
- if items.respond_to?(symbol)
- items.send(symbol, *args, &block)
- elsif association.associated_table.associations.any? { |assoc| assoc.name == symbol }
- results = []
- each do |item|
- unless (val = item.send(symbol)).blank?
- results << (val.is_a?(Enumerable) ? val.entries : val)
- end
- end
- results.flatten
- elsif items.size == 1 && items.entries.first.respond_to?(symbol)
- items.entries.first.send(symbol, *args, &block)
- else
- super
- end
- end
-
- def respond_to?(symbol)
- items.respond_to?(symbol) || super
- end
-
- def reload!
- @items = nil
- end
-
- def items
- @items || begin
- if @instance.loaded_set.nil?
- @items = Support::TypedSet.new(association.associated_constant)
- else
- associated_items = fetch_sets
-
- # This is where @items is set, by calling association=,
- # which in turn calls HasManyAssociation::Set#set.
- association_ivar_name = association.instance_variable_name
- setter_method = "#{@association_name}=".to_sym
- @instance.loaded_set.each do |entry|
- entry.send(setter_method, associated_items[entry.key])
- end # @instance.loaded_set.each
- end # if @instance.loaded_set.nil?
-
- if @pending_members
- pending_members.each do |member|
- @items << member
- end
-
- pending_members.clear
- end
-
- return @items
- end # begin
- end # def items
-
- def inspect
- entries.inspect
- end
-
- def first
- items.entries.first
- end
-
- def last
- items.entries.last
- end
-
- def ==(other)
- (items.size == 1 ? first : items) == other
- end
-
- def deactivate
- case association.dependency
- when :destroy
- items.entries.each do |member|
- status = member.destroy! unless member.new_record?
- return false unless status
- end
- when :delete
- @instance.database_context.adapter.connection do |db|
- sql = association.to_delete_sql
- parameters = [@instance.key]
- db.create_command(sql).execute_non_query(*parameters)
- end
- when :protect
- unless items.empty?
- raise AssociationProtectedError.new("You cannot delete this model while it has items associated with it.")
- end
- when :nullify
- nullify_association
- else
- nullify_association
- end
- end
-
- def nullify_association
- @instance.database_context.adapter.connection do |db|
- sql = association.to_disassociate_sql
- parameters = [@instance.key]
- db.create_command(sql).execute_non_query(*parameters)
- end
- end
-
- private
- def loaded_members
- pending_members + @items
- end
-
- def pending_members
- @pending_members || @pending_members = Support::TypedSet.new(association.associated_constant)
- end
-
- def fetch_sets
- finder_options = { association.foreign_key_column.to_sym => @instance.loaded_set.map { |item| item.key } }
- finder_options.merge!(association.finder_options)
-
- foreign_key_ivar_name = association.foreign_key_column.instance_variable_name
-
- @instance.database_context.all(
- association.associated_constant,
- finder_options
- ).group_by { |entry| entry.instance_variable_get(foreign_key_ivar_name) }
- end
-
- end
-
- end
-
- end
-end
View
143 burn/associations/has_n_association.rb
@@ -1,143 +0,0 @@
-module DataMapper
-
- class ForeignKeyNotFoundError < StandardError; end
- class AssociationProtectedError < StandardError; end
-
- module Associations
-
- class HasNAssociation
-
- attr_reader :options
-
- OPTIONS = [
- :class,
- :class_name,
- :foreign_key,
- :dependent
- ]
-
- def initialize(klass, association_name, options)
- @constant = klass
- @adapter = repository.adapter
- @table = @adapter.table(klass)
- @association_name = association_name.to_sym
- @options = options || Hash.new
-
- define_accessor(klass)
-
- Persistable::dependencies.add(associated_constant_name) do |klass|
- @foreign_key_column = associated_table[foreign_key_name]
-
- unless @foreign_key_column
- associated_constant.property(foreign_key_name, foreign_key_type)
-
- @foreign_key_column = associated_table[foreign_key_name]
-
- if @foreign_key_column.nil?
- raise ForeignKeyNotFoundError.new(<<-EOS.compress_lines)
- key_table => #{key_table.inspect},
- association_table => #{associated_table.inspect},
- association_name => #{name},
- foreign_key_name => #{foreign_key_name.inspect},
- foreign_key_type => #{foreign_key_type.inspect},
- constant => #{constant.inspect},
- associated_constant => #{associated_constant.inspect}
- EOS
- end
- end
- end
- end
-
- def name
- @association_name
- end
-
- def constant
- @constant
- end
-
- def associated_constant
- @associated_constant || @associated_constant = Kernel.const_get(associated_constant_name)
- end
-
- def associated_constant_name
- @associated_constant_name || begin
-
- if @options.has_key?(:class) || @options.has_key?(:class_name)
- @associated_constant_name = (@options[:class] || @options[:class_name])
-
- if @associated_constant_name.kind_of?(String)
- @associated_constant_name = Inflector.classify(@associated_constant_name)
- elsif @associated_constant_name.kind_of?(Class)
- @associated_constant_name = @associated_constant_name.name
- end
- else
- @associated_constant_name = Inflector.classify(@association_name)
- end
-
- @associated_constant_name
- end
-
- end
-
- def primary_key_column
- @primary_key_column || @primary_key_column = key_table.key
- end
-
- def foreign_key_column
- @foreign_key_column
- end
-
- def foreign_key_name
- @foreign_key_name || @foreign_key_name = (@options[:foreign_key] || key_table.default_foreign_key)
- end
-
- def foreign_key_type
- @foreign_key_type || @foreign_key_type = key_table.key.type
- end
-
- def key_table
- @key_table || @key_table = @adapter.table(constant)
- end
-
- def associated_table
- @association_table || @association_table = @adapter.table(associated_constant)
- end
-
- def associated_columns
- associated_table.columns.reject { |column| column.lazy? }
- end
-
- def complementary_association
- @complementary_association || begin
- @complementary_association = associated_table.associations.find do |mapping|
- mapping.is_a?(BelongsToAssociation) &&
- mapping.foreign_key_column == foreign_key_column &&
- mapping.key_table.name == key_table.name
- end
-
- if @complementary_association
- class << self
- attr_accessor :complementary_association
- end
- end
-
- return @complementary_association
- end
- end
-
- def finder_options
- @finder_options || @finder_options = @options.reject { |k,v| self.class::OPTIONS.include?(k) }
- end
-
- def to_sql
- "JOIN #{associated_table.to_sql} ON #{foreign_key_column.to_sql(true)} = #{primary_key_column.to_sql(true)}"
- end
-
- def activate!(force = false)
- foreign_key_column
- end
- end
-
- end
-end
View
47 burn/associations/reference.rb
@@ -1,47 +0,0 @@
-module DataMapper
-
- module Associations
-
- # Reference is an abstract-class providing the boiler-plate for
- # the association proxies (ie: HasManyAssociation::Set, or
- # HasOneAssociation::Instance)
- # The proxies need to access the defining Association instances
- # to obtain mapping information. This class provides convenient
- # access to said Association.
- #
- # EXAMPLE:
- # class Zoo
- # has_many :exhibits
- # end
- # The +has_many+ declaration instantiates a
- # DataMapper::Associations::HasManyAssociation and adds it to the
- # DataMapper::Adapters::Sql::Mappings::Table#associations array for
- # the Table representing Zoo.
- #
- # Zoo.new.exhibits
- # +exhibits+ above returns an instance of
- # DataMapper::Associations::HasManyAssociation::Set. This instance
- # needs to access the actual HasManyAssociation instance in order
- # to access the mapping information within. The DataMapper::Associations::Reference
- # abstract-class for the Set provides the Reference#association method in order to
- # provide easy access to this information.
- class Reference
-
- # +instance+ is a mapped object instance. ie: #<Zoo:0x123456 ...>
- # +association_name+ is the Symbol used to look up the Association
- # instance within the DataMapper::Adapters::Sql::Mappings::Table
- def initialize(instance, association_name)
- @instance, @association_name = instance, association_name.to_sym
- @instance.loaded_associations << self
- end
-
- # #association provides lazily initialized access to the declared
- # Association.
- def association
- @association || (@association = @instance.database_context.table(@instance.class).associations[@association_name])
- end
-
- end
- end
-
-end
View
107 burn/callbacks.rb
@@ -1,107 +0,0 @@
-module DataMapper
-
- # CallbacksHelper adds a class-method ClassMethods#callbacks
- # when included in a class, and defines short-cut class-methods to
- # add delegates to callbacks for the built-in Callbacks::EVENTS.
- module CallbacksHelper
-
- # The ::included callback extends the included class with a
- # ::callbacks method, and sets up helper methods for the standard
- # events declared in Callbacks::EVENTS.
- def self.included(base)
- base.extend(ClassMethods)
-
- # Declare helpers for the standard EVENTS
- Callbacks::EVENTS.each do |name|
- base.class_eval <<-EOS
- def self.#{name}(string = nil, &block)
- if string.nil?
- callbacks.add(:#{name}, block)
- else
- callbacks.add(:#{name}, string)
- end
- end
- EOS
- end
- end
-
- # Defines class-methods for the class that CallbacksHelper is
- # included in.
- module ClassMethods
-
- # Provides lazily initialized access to a Callbacks instance.
- def callbacks
- @callbacks || ( @callbacks = DataMapper::Callbacks.new )
- end
- end
- end
-
- # Callbacks is a collection to assign and execute blocks of code when
- # hooks throughout the DataMapper call Callbacks#execute. A set of the
- # standard callbacks is declared in the Callbacks::EVENTS array.
- class Callbacks
-
- # These are a collection of default callbacks that are hooked
- # into the DataMapper. You're free to add your own events just by
- # calling #add, but you'll have add the appropriate hooks into the
- # system to actually execute them yourself.
- EVENTS = [
- :before_materialize, :after_materialize,
- :before_save, :after_save,
- :before_create, :after_create,
- :before_update, :after_update,
- :before_destroy, :after_destroy,
- :before_validation, :after_validation
- ]
-
- # Initializes an internal Hash that ensures callback names are always
- # of type Symbol, and assigns an Array to store your delegating code
- # when the callback is looked-up by name.
- def initialize
- @callbacks = Hash.new do |h,k|
- raise 'Callback names must be Symbols' unless k.kind_of?(Symbol)
- h[k] = Set.new
- end
- end
-
- # Executes a given callback and returns TRUE or FALSE depending on the
- # return value of the callbacks. All callbacks must return successfully
- # in order for the call to #execute to return TRUE. Callbacks always
- # execute against an +instance+. You may pass additional arguments
- # which will in turn be passed to any Proc objects assigned to a specific
- # callback. Strings assigned to callbacks do not accept parameters.
- # They are instance-eval'ed instead. When the callback is a Symbol,
- # it is sent to the instance under the assumption it is a method call.
- def execute(name, instance, *args)
- @callbacks[name].all? do |callback|
- case callback
- when String then instance.instance_eval(callback)
- when Proc then callback[instance, *args]
- when Symbol then instance.send(callback, *args)
- else raise ''
- end
- end
- end
-
- # Asign delegating code to a callback. The +block+ parameter
- # can be a Proc object, a String which will be eval'ed when
- # the callback is executed, or a Symbol, which will be sent to
- # the instance executed against (as a method call).
- def add(name, block)
- callback = @callbacks[name]
- raise ArgumentError.new("You didn't specify a callback in String, Symbol or Proc form.") unless [String, Proc, Symbol].detect { |type| block.is_a?(type) }
- callback << block
- end
-
- def dup
- copy = self.class.new
- @callbacks.each_pair do |name, callbacks|
- callbacks.each do |callback|
- copy.add(name, callback)
- end
- end
- return copy
- end
- end
-
-end
View
166 burn/container.rb
@@ -1,166 +0,0 @@
-require File.join(File.dirname(__FILE__), 'attributes')
-require File.join(File.dirname(__FILE__), 'validations')
-require File.join(File.dirname(__FILE__), 'associations')
-require File.join(File.dirname(__FILE__), 'callbacks')
-require File.join(File.dirname(__FILE__), 'dependency_queue')
-require File.join(File.dirname(__FILE__), 'support', 'struct')
-require File.join(File.dirname(__FILE__), 'persistable')
-require File.join(File.dirname(__FILE__), 'is', 'tree')
-require File.join(File.dirname(__FILE__), 'persistable')
-
-module DataMapper
- class Container
-
- include DataMapper::Attributes
- include DataMapper::Associations
- include DataMapper::Validations
- include DataMapper::CallbacksHelper
-
- include DataMapper::Is::Tree
-
- include DataMapper::Persistable
-
- def inherited(klass)
- klass.send(:include, InstanceMethods)
- klass.extend(ClassMethods)
-
- klass.instance_variable_set('@properties', [])
- end
-
- module InstanceMethods
- def initialize(details = nil) # :nodoc:
- check_for_properties!
- if details
- initialize_with_attributes(details)
- end
- end
-
- def initialize_with_attributes(details) # :nodoc:
- case details
- when Hash then self.attributes = details
- when details.respond_to?(:persistent?) then self.private_attributes = details.attributes
- when Struct then self.private_attributes = details.attributes
- end
- end
-
- def check_for_properties! # :nodoc:
- raise IncompleteModelDefinitionError.new("Models must have at least one property to be initialized.") if self.class.properties.blank?
- end
-
- def inspect
- inspected_attributes = attributes.map { |k,v| "@#{k}=#{v.inspect}" }
-
- instance_variables.each do |name|
- if instance_variable_get(name).kind_of?(Associations::HasManyAssociation)
- inspected_attributes << "#{name}=#{instance_variable_get(name).inspect}"
- end
- end
-
- "#<%s:0x%x @new_record=%s, %s>" % [self.class.name, (object_id * 2), new_record?, inspected_attributes.join(', ')]
- end
-
- def loaded_associations
- @loaded_associations || @loaded_associations = []
- end
-
- def <=>(other)
- keys <=> other.keys
- end
-
- # Look to ::included for __hash alias
- def hash
- @__hash || @__hash = keys.empty? ? super : keys.hash
- end
-
- def eql?(other)
- return false unless other.is_a?(self.class) || self.is_a?(other.class)
- comparator = keys.empty? ? :private_attributes : :keys
- send(comparator) == other.send(comparator)
- end
-
- def ==(other)
- eql?(other)
- end
-
- # Returns the difference between two objects, in terms of their
- # attributes.
- def ^(other)
- results = {}
-
- self_attributes, other_attributes = attributes, other.attributes
-
- self_attributes.each_pair do |k,v|
- other_value = other_attributes[k]
- unless v == other_value
- results[k] = [v, other_value]
- end
- end
-
- results
- end
- end
-
- module ClassMethods
- # Adds property accessors for a field that you'd like to be able to
- # modify. The DataMapper doesn't
- # use the table schema to infer accessors, you must explicity call
- # #property to add field accessors
- # to your model.
- #
- # Can accept an unlimited amount of property names. Optionally, you may
- # pass the property names as an
- # array.
- #
- # For more documentation, see Property.
- #
- # EXAMPLE:
- # class CellProvider
- # property :name, :string
- # property :rating_number, :rating_percent, :integer # will create two properties with same type and text
- # property [:bill_to, :ship_to, :mail_to], :text, :lazy => false # will create three properties all with same type and text
- # end
- #
- # att = CellProvider.new(:name => 'AT&T')
- # att.rating = 3
- # puts att.name, att.rating
- #
- # => AT&T
- # => 3
- #
- # OPTIONS:
- # * <tt>lazy</tt>: Lazy load the specified property (:lazy => true). False by default.
- # * <tt>accessor</tt>: Set method visibility for the property accessors. Affects both
- # reader and writer. Allowable values are :public, :protected, :private. Defaults to
- # :public
- # * <tt>reader</tt>: Like the accessor option but affects only the property reader.
- # * <tt>writer</tt>: Like the accessor option but affects only the property writer.
- # * <tt>protected</tt>: Alias for :reader => :public, :writer => :protected
- # * <tt>private</tt>: Alias for :reader => :public, :writer => :private
- def property(*columns_and_options)
- columns, options = columns_and_options.partition {|item| not item.is_a?(Hash)}
- options = (options.empty? ? {} : options[0])
- type = columns.pop
-
- @properties ||= []
- new_properties = []
-
- columns.flatten.each do |name|
- property = DataMapper::Property.new(self, name, type, options)
- new_properties << property
- @properties << property
- end
-
- return (new_properties.length == 1 ? new_properties[0] : new_properties)
- end
-
- # Returns an array of Properties for this model.
- def properties
- @properties
- end
-
- end
-
- end
-end
-
-
View
109 burn/context.rb
@@ -1,109 +0,0 @@
-require File.join(File.dirname(__FILE__), 'identity_map')
-
-module DataMapper
-
- class Context
-
- class MaterializationError < StandardError
- end
-
- attr_reader :adapter
-
- def initialize(repository)
- @repository = repository
- @adapter = repository.adapter
- end
-
- def repository
- @repository
- end
-
- def name
- @repository.name
- end
-
- def identity_map
- @identity_map || ( @identity_map = IdentityMap.new )
- end
-
- def first(klass, *args)
- id = nil
- options = nil
- table = self.table(klass)
- key = table.key
-
- if args.empty? # No id, no options
- options = { :limit => 1 }
- elsif args.size == 2 && args.last.kind_of?(Hash) # id AND options
- options = args.last.merge(key => args.first)
- elsif args.size == 1 # id OR options
- if args.first.kind_of?(Hash)
- options = args.first.merge(:limit => 1) # no id, add limit
- else
- options = { key => args.first } # no options, set id
- end
- else
- raise ArgumentError.new('Context#first takes a class, and optional type_or_id and/or options arguments')
- end
-
- # Account for undesired behaviour in MySQL that returns the
- # last inserted row when the WHERE clause contains a "#{primary_key} IS NULL".
- return nil if options.has_key?(key.name) && options[key.name] == nil
-
- @adapter.load(self, klass, options).first
- end
-
- def get(klass, keys)
- @adapter.get(self, klass, keys)
- end
-
- def all(klass, options = {})
- @adapter.load(self, klass, options)
- end
-
- def count(klass, *args)
- table(klass).count(*args)
- end
-
- def save(instance)
- @adapter.save(self, instance)
- end
-
- def destroy(instance)
- @adapter.delete(self, instance)
- end
-
- def delete_all(klass)
- @adapter.delete(self, klass)
- end
-
- def truncate(klass)
- table(klass).truncate!
- end
-
- def create_table(klass)
- table(klass).create!
- end
-
- def drop_table(klass)
- table(klass).drop!
- end
-
- def table_exists?(klass)
- table(klass).exists?
- end
-
- def execute(*args)
- @adapter.execute(*args)
- end
-
- def query(*args)
- @adapter.query(*args)
- end
-
- def logger
- @logger || @logger = @adapter.logger
- end
-
- end
-end
View
145 burn/embedded_value.rb
@@ -1,145 +0,0 @@
-module DataMapper
-
-# == EmbeddedValue
-# As an alternative to an extraneous has_one association, EmbeddedValue offers a means
-# to serialize component objects to a table without having to define an entirely new model.
-#
-# Example:
-#
-# class Person < DataMapper::Base
-#
-# property :name, :string
-# property :occupation, :string
-#
-# embed :address, :prefix => true do
-# property :street, :string
-# property :city, :string
-# property :state, :string, :size => 2
-# property :zip_code, :string, :size => 10
-#
-# def city_state_zip_code
-# "#{city}, #{state} #{zip_code}"
-# end
-#
-# end
-# end
-#
-# Columns for the Address model will appear in the Person table. Passing
-# <tt>:prefix => true</tt> will prefix the column name with the parent table's name.
-# The default behavior is to use the columns as they are defined. Using the above
-# example, the database table structure will become:
-#
-# Column Datatype, Options
-# ===============================================================
-# name :string
-# occupation :string
-# address_street :string
-# address_city :string
-# address_state :string, :size => 2
-# address_zip_code :string, :size => 10
-#
-# EmbeddedValue's become instance methods off of an instance of the parent
-# class and return a sub-class of the parent class.
-#
-# bob = Person.first(:name => 'Bob')
-# bob.address # => #<Person::Address:0x1a492b8>
-# bob.address.city # => "Pittsburgh"
-# bob.address.city_state_zip_code # => "Pitsburgh, PA 90210"
-
- class EmbeddedValue
- EMBEDDED_PROPERTIES = []
-
- def initialize(instance)
- @instance = instance
- @container_prefix = ''
- end
-
- def self.inherited(base)
- base.const_set('EMBEDDED_PROPERTIES', [])
- end
-
- # add an embedded property. For more information about how to define properties, visit Property.
- def self.property(name, type, options = {})
- # set lazy option on the mapping if defined in the embed block
- options[:lazy] ||= @container_lazy
-
- options[:reader] ||= options[:accessor] || @container_reader_visibility
- options[:writer] ||= options[:accessor] || @container_writer_visibility
-
- property_name = (@container_prefix ? @container_prefix + name.to_s : name).to_sym
-
- property = containing_class.property(property_name, type, options)
- define_property_getter(name, property)
- define_property_setter(name, property)
- end
-
- # define embedded property getters
- def self.define_property_getter(name, property) # :nodoc:
-
- # add the method on the embedded class
- class_eval <<-EOS
- #{property.reader_visibility.to_s}
- def #{name}
- #{"@instance.lazy_load!("+ property.name.inspect + ")" if property.lazy?}
- @instance.instance_variable_get(#{property.instance_variable_name.inspect})
- end
- EOS
-
- # add a shortcut boolean? method if applicable (ex: activated?)
- if property.type == :boolean
- class_eval("alias #{property.name}? #{property.name}")
- end
- end
-
- # define embedded property setters
- def self.define_property_setter(name, property) # :nodoc:
-
- # add the method on the embedded class
- class_eval <<-EOS
- #{property.writer_visibility.to_s}
- def #{name.to_s.sub(/\?$/, '')}=(value)
- @instance.instance_variable_set(#{property.instance_variable_name.inspect}, value)
- end
- EOS
- end
-
- # returns the class in which the EmbeddedValue is declared
- def self.containing_class
- @containing_class || @containing_class = begin
- tree = name.split('::')
- tree.pop
- tree.inject(Object) { |klass, current| klass.const_get(current) }
- end
- end
-
- def self.define(container, name, options, &block)
- embedded_class, embedded_class_name, accessor_name = nil
-
- accessor_name = name.to_s
- embedded_class_name = Inflector.camelize(accessor_name)
- embedded_class = Class.new(EmbeddedValue)
- container.const_set(embedded_class_name, embedded_class) unless container.const_defined?(embedded_class_name)
-
- if options[:prefix]
- container_prefix = options[:prefix].kind_of?(String) ? options[:prefix] : "#{accessor_name}_"
- embedded_class.instance_variable_set('@container_prefix', container_prefix)
- end
-
- embedded_class.instance_variable_set('@containing_class', container)
-
- embedded_class.instance_variable_set('@container_lazy', !!options[:lazy])
- embedded_class.instance_variable_set('@container_reader_visibility', options[:reader] || options[:accessor] || :public)
- embedded_class.instance_variable_set('@container_writer_visibility', options[:writer] || options[:accessor] || :public)
-
- embedded_class.class_eval(&block) if block_given?
-
- container.class_eval <<-EOS
- def #{accessor_name}
- #{embedded_class_name}.new(self)
- end
- EOS
- end
-
- end
-
-end # module DataMapper
View
7 burn/example.rb
@@ -1,7 +0,0 @@
-#!/usr/bin/env ruby
-
-require 'environment'
-require 'irb'
-
-# database { IRB::start }
-IRB::start
View
155 burn/migration.rb
@@ -1,155 +0,0 @@
-module DataMapper
- class Migration
- class Table
-
- MAPPINGS = DataMapper::Adapters::Sql::Mappings unless defined?(MAPPINGS)
-
- attr_accessor :name
-
- def initialize(table = nil, options = {})
- @name, @options = table, options
- @columns = []
- end
-
- def self.create(table)
- table.create!
- end
-
- def self.drop(table_name)
- repository.table(klass(table_name)).drop!
- end
-
- def self.add_column(table, column, type, options = {})
- column = table.add_column column, type, options
- column.create!
- end
-
- def self.remove_column(table, column)
- column = table[column]
- column.drop!
- end
-
- def add(column, type, options = {})
- column_data = [column, type, options]
- exists? ? self.class.add_column(table, *column_data) : table.add_column(*column_data)
- end
-
- def remove(column)
- self.class.remove_column table, column
- end
-
- def rename(old_column, new_column)
- column = table[old_column]
- column.rename!(new_column)
- end
-
- def alter(column, type, options = {})
- column = table[column]
- column.type = type
- column.options = options
- column.parse_options!
- column.alter!
- end
-
- def exists?
- repository.table_exists?(klass)
- end
-
- def after_create!
- unless exists?
- table.add_column(:id, :integer, { :key => true }) unless @options[:id] == false
- self.class.create(table)
- end
- end
-
- # Rails Style
-
- def column(name, type, options = {})
- add(name, type, options)
- end
-
- # klass!
-
- def table
- @table ||= repository.table(klass)
- end
-
- def klass
- @klass ||= self.class.klass(self.name)
- end
-
- def self.klass(table)
- table_name = table.to_s
- class_name = Inflector::classify(table_name)
- klass = Inflector::constantize(class_name)
- rescue NameError
- module_eval <<-classdef
- class ::#{class_name} < DataMapper::Base
- end
- classdef
- klass = eval("#{class_name}")
- ensure
- klass
- end
-
- end
-
- class << self
-
- def up; end
-
- def down; end
-
- def migrate(direction = :up)
- send(direction)
- end
-
- def table(table = nil, options = {}, &block)
- if table && block
- table = DataMapper::Migration::Table.new(table, options)
- table.instance_eval &block
- table.after_create!
- else
- return DataMapper::Migration::Table
- end
- end
-
- # Rails Style
-
- def create_table(table_name, options = {}, &block)
- new_table = table.new(table_name, options)
- yield new_table
- new_table.after_create!
- end
-
- def drop_table(table_name)
- table.drop(table_name)
- end
-
- def add_column(table_name, column, type, options = {})
- table table_name do
- add column, type, options
- end
- end
-
- def rename_column(table_name, old_column_name, new_column_name)
- table table_name do
- rename old_column_name, new_column_name
- end
- end
-
- def change_column(table_name, column_name, type, options = {})
- table table_name do
- alter column_name, type, options
- end
- end
-
- def remove_column(table_name, column)
- table table_name do
- remove column
- end
- end
- end
- end
-
-end
View
13 burn/model.rb
@@ -1,13 +0,0 @@
-require File.join(File.dirname(__FILE__), 'persistable')
-
-module DataMapper
- class Model
- def self.inherited(klass)
- klass.send(:include, DataMapper::Persistable)
- end
-
- class IncompleteModelDefinitionError < StandardError
- end
-
- end
-end
View
769 burn/persistable.rb
@@ -1,769 +0,0 @@
-require File.join(File.dirname(__FILE__), 'resource')
-require File.join(File.dirname(__FILE__), 'associations')
-require File.join(File.dirname(__FILE__), 'validations')
-require File.join(File.dirname(__FILE__), 'callbacks')
-require File.join(File.dirname(__FILE__), 'support', 'serialization')
-require File.join(File.dirname(__FILE__), 'auto_migrations')
-require File.join(File.dirname(__FILE__), 'embedded_value')
-
-# require 'data_mapper/attributes'
-# require 'data_mapper/support/serialization'
-# require 'data_mapper/validations'
-# require 'data_mapper/associations'
-# require 'data_mapper/callbacks'
-# require 'data_mapper/embedded_value'
-# require 'data_mapper/auto_migrations'
-# require 'data_mapper/dependency_queue'
-# require 'data_mapper/support/struct'
-
-class Object
-
- def self.resource_names(*args)
- self.send(:include, DataMapper::Persistable)
- resource_names(*args)
- end
-
- def self.property(*args)
- self.send(:include, DataMapper::Persistable)
- resource_names(*args)
- end
-
-end
-
-module DataMapper
- module Persistable
-
- def self.included(target)
- target.send(:include, DataMapper::Resource)
- end
-
- end
-end
-
-module DataMapper
- # See DataMapper::Persistable::ClassMethods for DataMapper's DSL documentation.
- module Persistable
-
- # This probably needs to be protected
- attr_accessor :loaded_set
-
- include Comparable
-
- class << self
- alias __included included
- end
-
- def self.included(klass) # :nodoc:
- __included(klass)
- klass.extend(ClassMethods)
- klass.extend(ConvenienceMethods::ClassMethods)
-
- klass.send(:include, ConvenienceMethods::InstanceMethods)
- klass.send(:include, Associations)
- klass.send(:include, Validations)
- klass.send(:include, CallbacksHelper)
- klass.send(:include, Support::Serialization)
-
- klass.send :extend, AutoMigrations
- klass.subclasses
- DataMapper::Persistable::subclasses << klass
- klass.send(:undef_method, :id) if method_defined?(:id)
-
- # When this class is sub-classed, copy the declared columns.
- klass.class_eval do
- def self.subclasses
- @subclasses || (@subclasses = Support::TypedSet.new(Class))
- end
-
- def self.inherited(subclass)
- subclass.send(:include, Resource)
- subclass.resource_names[:default] = self.resource_name(:default)
-
- # if super_table.type_column.nil?
- # super_table.add_column(:type, :class, {})
- # end
-
- # subclass.instance_variable_set('@properties', self.instance_variable_get("@properties").dup)
- # subclass.instance_variable_set("@callbacks", self.callbacks.dup)
-
- self::subclasses << subclass
- end
-
- def self.persistable?
- true
- end
- end
- end
-
- # Migrates the database schema based on the properties defined within
- # models. This includes removing fields no longer listed in models and
- # adding new ones.
- #
- # This is destructive. Any data stored in the database will be destroyed
- # when this method is called.
- #
- # ==== Returns
- # True:: successfully automigrated database
- # False:: an error occured when automigrating the database
- #
- # @public
- def self.auto_migrate!
- subclasses.each do |subclass|
- subclass.auto_migrate!
- end
- end
-
-
- # Drops all tables known by the schema
- #
- # ==== Returns
- # True:: successfully automigrated database
- # False:: an error occured when automigrating the database
- #
- # @public
- def self.drop_all_tables!
- repository.adapter.schema.each do |table|
- table.drop!
- end
- end
-
- # Track classes that include this module.
- # ==== Returns
- # Support::TypedSet::
- # contains classes that include or inherit from this module
- #
- # @semipublic
- def self.subclasses
- @subclasses || (@subclasses = Support::TypedSet.new(Class))
- end
-
- # Track dependencies for this model's associations.
- # ==== Returns
- # DependencyQueue::
- # a hash that contain's this model's dependencies
- #
- # @semipublic
- def self.dependencies
- @dependency_queue || (@dependency_queue = DependencyQueue.new)
- end
-
- module ConvenienceMethods
- module InstanceMethods
-
- # Save updated properties to the database.
- #
- # ==== Returns
- # True:: successfully saved the object to the database
- # False:: an error occured when saving the object to the database. Use
- # valid? to see if validation error occured
- #
- # @public
- def save
- database_context.save(self)
- end
-
- # This behaves in the same way as save, but raises a ValidationError
- # if the model is invalid. Successful saves return true.
- #
- # ==== Returns
- # True:: successfully saved the object to the database
- #
- # ==== Raises
- # ValidationError::
- # The object could not be saved to the database due to validation
- # errors.
- # @public
- def save!
- raise ValidationError.new(errors) unless save
- return true
- end
-
- # Reloads a model's properties from the database. This also includes
- # data for any associated models that have been loaded from the
- # database.
- #
- # You can limit the properties being reloaded by passing in an array
- # of symbols.
- #
- # === Returns
- # self:: The reloaded instance of this model.
- def reload!(cols = nil)
- database_context.first(self.class, key, :select => ([self.class.table.key.to_sym] + (cols || original_values.keys)).uniq, :reload => true)
- self.loaded_associations.each { |association| association.reload! }
- self
- end
- alias reload reload!
-
- # Deletes the model from the database and de-activates associations
- # === Returns
- # True:: successfully destroyed the object.
- def destroy!
- database_context.destroy(self)
- end