require 'sequel/core'

module Sequel
  # Lets you create a Model subclass with its dataset already set.
  # source can be an existing dataset or a symbol (in which case
  # it will create a dataset using the default database with 
  # the given symbol as the table name).
  #
  # The purpose of this method is to set the dataset automatically
  # for a model class, if the table name doesn't match the implicit
  # name.  This is neater than using set_dataset inside the class,
  # doesn't require a bogus query for the schema, and allows
  # it to work correctly in a system that uses code reloading.
  #
  # Example:
  #   class Comment < Sequel::Model(:something)
  #     table_name # => :something
  #   end
  def self.Model(source)
    Model::ANONYMOUS_MODEL_CLASSES[source] ||= if source.is_a?(Database)
      c = Class.new(Model)
      c.db = source
      c
    else
      Class.new(Model).set_dataset(source)
    end
  end

  # Sequel::Model is an object relational mapper built on top of Sequel core.  Each
  # model class is backed by a dataset instance, and many dataset methods can be
  # called directly on the class.  Model datasets return rows as model instances,
  # which have fairly standard ORM instance behavior.
  #
  # Sequel::Model is built completely out of plugins, the only method not part of a
  # plugin is the plugin method itself.  Plugins can override any class, instance, or
  # dataset method defined by a previous plugin and call super to get the default
  # behavior.
  #
  # You can set the SEQUEL_NO_ASSOCIATIONS constant or environment variable to
  # make Sequel not load the associations plugin by default.
  class Model
    # Map that stores model classes created with Sequel::Model(), to allow the reopening
    # of classes when dealing with code reloading.
    ANONYMOUS_MODEL_CLASSES = {}

    # Class methods added to model that call the method of the same name on the dataset
    DATASET_METHODS = %w'<< add_graph_aliases all avg count delete distinct
       each each_page eager eager_graph empty? except exclude filter first from from_self
       full_outer_join get graph grep group group_and_count group_by having import
       inner_join insert insert_multiple intersect interval invert join join_table
       last left_outer_join limit map max min multi_insert naked order order_by
       order_more paginate print qualify query range reverse reverse_order right_outer_join
       select select_all select_more server set set_defaults set_graph_aliases set_overrides
       single_value sum to_csv to_hash truncate unfiltered ungraphed ungrouped union unlimited unordered 
       update where with with_recursive with_sql'.map{|x| x.to_sym}
  
    # Class instance variables to set to nil when a subclass is created, for -w compliance
    EMPTY_INSTANCE_VARIABLES = [:@overridable_methods_module, :@db]

    # Empty instance methods to create that the user can override to get hook/callback behavior.
    # Just like any other method defined by Sequel, if you override one of these, you should
    # call super to get the default behavior (while empty by default, they can also be defined
    # by plugins).  
    HOOKS = [:after_initialize, :before_create, :after_create, :before_update,
      :after_update, :before_save, :after_save, :before_destroy, :after_destroy,
      :before_validation, :after_validation]

    # Class instance variables that are inherited in subclasses.  If the value is :dup, dup is called
    # on the superclass's instance variable when creating the instance variable in the subclass.
    # If the value is nil, the superclass's instance variable is used directly in the subclass.
    INHERITED_INSTANCE_VARIABLES = {:@allowed_columns=>:dup, :@dataset_methods=>:dup, 
      :@dataset_method_modules=>:dup, :@primary_key=>nil, :@use_transactions=>nil,
      :@raise_on_save_failure=>nil, :@restricted_columns=>:dup, :@restrict_primary_key=>nil,
      :@simple_pk=>nil, :@simple_table=>nil, :@strict_param_setting=>nil,
      :@typecast_empty_string_to_nil=>nil, :@typecast_on_assignment=>nil,
      :@raise_on_typecast_failure=>nil, :@plugins=>:dup}

    # Regexp that determines if a method name is normal in the sense that
    # it could be called directly in ruby code without using send.  Used to
    # avoid problems when using eval with a string to define methods.
    NORMAL_METHOD_NAME_REGEXP = /\A[A-Za-z_][A-Za-z0-9_]*\z/

    # The setter methods (methods ending with =) that are never allowed
    # to be called automatically via set/update/new/etc..
    RESTRICTED_SETTER_METHODS = %w"== === []= taguri= typecast_empty_string_to_nil= typecast_on_assignment= strict_param_setting= raise_on_save_failure= raise_on_typecast_failure="

    # Regular expression that determines if the method is a valid setter name
    # (i.e. it ends with =).
    SETTER_METHOD_REGEXP = /=\z/

    @allowed_columns = nil
    @db = nil
    @db_schema = nil
    @dataset_method_modules = []
    @dataset_methods = {}
    @overridable_methods_module = nil
    @plugins = []
    @primary_key = :id
    @raise_on_save_failure = true
    @raise_on_typecast_failure = true
    @restrict_primary_key = true
    @restricted_columns = nil
    @simple_pk = nil
    @simple_table = nil
    @strict_param_setting = true
    @typecast_empty_string_to_nil = true
    @typecast_on_assignment = true
    @use_transactions = true
  end

  require %w"default_inflections inflections plugins base exceptions errors", "model"
  if !defined?(::SEQUEL_NO_ASSOCIATIONS) && !ENV.has_key?('SEQUEL_NO_ASSOCIATIONS')
    require 'associations', 'model'
    Model.plugin Model::Associations
  end
end
