<commit_before># reopen sam/extlib/lib/extlib/object.rb
class Object

  def full_const_defined?(name)
    !!full_const_get(name)
  rescue
    false
  end
  
end

English::Inflect.rule 'ess', 'esses'

module DataMapper
  module Is
    module Remixable

      #==============================INCLUSION METHODS==============================#

      # Adds remixer methods to DataMapper::Resource
      def self.included(base)
        base.send(:include,RemixerClassMethods)
        base.send(:include,RemixerInstanceMethods)
      end

      # - is_remixable
      # ==== Description
      #   Adds RemixeeClassMethods and RemixeeInstanceMethods to any model that is: remixable
      # ==== Examples
      # class User #Remixer
      #   remixes Commentable
      #   remixes Vote
      # end
      #
      # module Commentable #Remixable
      #   include DataMapper::Resource
      #
      #   is :remixable,
      #     :suffix => "comment"
      # end
      #
      # module Vote #Remixable
      #   include DataMapper::Resource
      #
      #   is :remixable
      #
      # ==== Notes
      #   These options are just available for whatever reason your Remixable Module name
      #   might not be what you'd like to see the table name and property accessor named.
      #   These are just configurable defaults, upon remixing the class_name and accessor there
      #   take precedence over the defaults set here
      # ==== Options
      #   :suffix   <String>
      #             Table suffix, defaults to YourModule.name.downcase.singular
      #             Yields table name of remixer_suffix; ie user_comments, user_votes
      def is_remixable(options={})
        extend  DataMapper::Is::Remixable::RemixeeClassMethods
        include DataMapper::Is::Remixable::RemixeeInstanceMethods
        
        # support clean suffixes for nested modules
        default_suffix = Extlib::Inflection.demodulize(self.name).singular.snake_case
        suffix(options.delete(:suffix) || default_suffix)
      end


      #==============================CLASS METHODS==============================#

      # - RemixerClassMethods
      # ==== Description
      #   Methods available to all DataMapper::Resources
      module RemixerClassMethods
        def self.included(base);end;

        # - remixables
        # ==== Description
        #   Returns a hash of the remixables used by this class
        # ==== Returns
        #   <Hash> Remixable Class Name => Remixed Class Name
        def remixables
          @remixables
        end

        # - remix
        # ==== Description
        #   Remixes a Remixable Module
        # ==== Parameters
        #   cardinality <~Fixnum> 1, n, x ...
        #   remixable   <Symbol> plural of remixable; i.e. Comment => :comments
        #   options     <Hash>   options hash
        #                       :class_name <String> Remixed Model name (Also creates a storage_name as tableize(:class_name))
        #                                   This is the class that will be created from the Remixable Module
        #                                   The storage_name can be changed via 'enhance' in the class that is remixing
        #                                   Default: self.name.downcase + "_" + remixable.suffix.pluralize
        #                       :as         <String> Alias to access associated data
        #                                   Default: tableize(:class_name)
        #                       :for|:on    <String> Class name to join to through Remixable
        #                                   This will create a M:M relationship THROUGH the remixable, rather than
        #                                   a 1:M with the remixable
        #                       :via        <String> changes the name of the second id in a unary relationship
        #                                   see example below; only used when remixing a module between the same class twice
        #                                   ie: self.class.to_s == options[:for||:on]
        #                       :unique     <Boolean> Only works with :for|:on; creates a unique composite key
        #                                   over the two table id's
        # ==== Examples
        # Given: User (Class), Addressable (Module)
        #
        #   One-To-Many; Class-To-Remixable
        #
        #   remix n, :addressables,
        #     :class_name => "UserAddress",
        #     :as         => "addresses"
        #
        #   Tables: users, user_addresses
        #   Classes: User, UserAddress
        #     User.user_addresses << UserAddress.new
        #     User.addresses << UserAddress.new
        #   --------------------------------------------
        #   --------------------------------------------
        #
        # Given: User (Class), Video (Class), Commentable (Module)
        #
        #   Many-To-Many; Class-To-Class through RemixableIntermediate (Video allows Commentable for User)
        #
        #   Video.remix n, :commentables
        #     :for        => 'User'    #:for & :on have same effect, just a choice of wording...
        #   --------------------------------------------
        #   --------------------------------------------
        #
        # Given: User (Class), User (Class), Commentable (Module)
        #
        #   Many-To-Many Unary relationship between User & User through comments
        #   User.remix n, :commentables, :as => "comments", :for => 'User', :via => "commentor"
        #   => This would create user_id and commentor_id as the
        #
        def remix(cardinality, remixable, options={})
          #A map for remixable names to Remixed Models
          @remixables = {} if @remixables.nil?

          # Allow nested modules to be remixable to better support using dm-is-remixable in gems
          # Example (from my upcoming dm-is-rateable gem)
          # remix n, "DataMapper::Is::Rateable::Rating", :as => :ratings
          remixable_module = Object.full_const_get(Extlib::Inflection.classify(remixable))
          
          #Merge defaults/options
          options = {
            :as         => nil,
            :class_name => Extlib::Inflection.classify(self.name.snake_case + "_" + remixable_module.suffix.pluralize),
            :for        => nil,
            :on         => nil,
            :unique     => false,
            :via        => nil
          }.merge(options)

          #Make sure the class hasn't been remixed already
          unless Object.full_const_defined?(Extlib::Inflection.classify(options[:class_name]))

            #Storage name of our remixed model
            options[:table_name] = Extlib::Inflection.tableize(options[:class_name])

            #Other model to mix with in case of M:M through Remixable
            options[:other_model] = options[:for] || options[:on]

            puts " ~ Generating Remixed Model: #{options[:class_name]}"
            model = generate_remixed_model(remixable_module, options)

            # map the remixable to the remixed model            
            # since this will be used from 'enhance api' i think it makes perfect sense to
            # always refer to a remixable by its demodulized snake_cased constant name
            remixable_key = Extlib::Inflection.demodulize(remixable_module.name).snake_case.to_sym
            populate_remixables_mapping(model, options.merge(:remixable_key => remixable_key))

            #Create relationships between Remixer and remixed class
            if options[:other_model]
              # M:M Class-To-Class w/ Remixable Module as intermediate table
              # has n and belongs_to (or One-To-Many)
              remix_many_to_many cardinality, model, options
            else
              # 1:M Class-To-Remixable
              # has n and belongs_to (or One-To-Many)
              remix_one_to_many cardinality, model, options
            end

            #Add accessor alias
            attach_accessor(options) unless options[:as].nil?
          else
            puts "#{__FILE__}:#{__LINE__} warning: already remixed constant #{options[:class_name]}"
          end
        end

        # - enhance
        # ==== Description
        #   Enhance a remix; allows nesting remixables, adding columns & functions to a remixed resource
        # ==== Parameters
        #   remixable <Symbol> Name of remixable to enhance (plural or singular name of is :remixable module)
        #   block     <Proc>    Enhancements to perform
        # ==== Examples
        #   class Video
        #     include DataMapper::Resource
        #     remix Comment
        #
        #     enhance :comments do
        #       remix n, :votes        #This would result in something like YouTubes Voting comments up/down
        #
        #       property :updated_at, DateTime
        #
        #       def backwards; self.test.reverse; end;
        #     end
        def enhance(remixable,&block)
          # always use innermost singular snake_cased constant name
          model = @remixables[remixable.to_s.singular.snake_case.to_sym][:model]

          unless model.nil?
            model.class_eval &block
          else<commit_msg>Use DataMapper.logger.warn instead of puts in case a remixable has been remixed already.<commit_after># reopen sam/extlib/lib/extlib/object.rb
class Object

  def full_const_defined?(name)
    !!full_const_get(name) rescue false
  end
  
end

English::Inflect.rule 'ess', 'esses'

module DataMapper
  module Is
    module Remixable

      #==============================INCLUSION METHODS==============================#

      # Adds remixer methods to DataMapper::Resource
      def self.included(base)
        base.send(:include,RemixerClassMethods)
        base.send(:include,RemixerInstanceMethods)
      end

      # - is_remixable
      # ==== Description
      #   Adds RemixeeClassMethods and RemixeeInstanceMethods to any model that is: remixable
      # ==== Examples
      # class User #Remixer
      #   remixes Commentable
      #   remixes Vote
      # end
      #
      # module Commentable #Remixable
      #   include DataMapper::Resource
      #
      #   is :remixable,
      #     :suffix => "comment"
      # end
      #
      # module Vote #Remixable
      #   include DataMapper::Resource
      #
      #   is :remixable
      #
      # ==== Notes
      #   These options are just available for whatever reason your Remixable Module name
      #   might not be what you'd like to see the table name and property accessor named.
      #   These are just configurable defaults, upon remixing the class_name and accessor there
      #   take precedence over the defaults set here
      # ==== Options
      #   :suffix   <String>
      #             Table suffix, defaults to YourModule.name.downcase.singular
      #             Yields table name of remixer_suffix; ie user_comments, user_votes
      def is_remixable(options={})
        extend  DataMapper::Is::Remixable::RemixeeClassMethods
        include DataMapper::Is::Remixable::RemixeeInstanceMethods
        
        # support clean suffixes for nested modules
        default_suffix = Extlib::Inflection.demodulize(self.name).singular.snake_case
        suffix(options.delete(:suffix) || default_suffix)
      end


      #==============================CLASS METHODS==============================#

      # - RemixerClassMethods
      # ==== Description
      #   Methods available to all DataMapper::Resources
      module RemixerClassMethods
        def self.included(base);end;

        # - remixables
        # ==== Description
        #   Returns a hash of the remixables used by this class
        # ==== Returns
        #   <Hash> Remixable Class Name => Remixed Class Name
        def remixables
          @remixables
        end

        # - remix
        # ==== Description
        #   Remixes a Remixable Module
        # ==== Parameters
        #   cardinality <~Fixnum> 1, n, x ...
        #   remixable   <Symbol> plural of remixable; i.e. Comment => :comments
        #   options     <Hash>   options hash
        #                       :class_name <String> Remixed Model name (Also creates a storage_name as tableize(:class_name))
        #                                   This is the class that will be created from the Remixable Module
        #                                   The storage_name can be changed via 'enhance' in the class that is remixing
        #                                   Default: self.name.downcase + "_" + remixable.suffix.pluralize
        #                       :as         <String> Alias to access associated data
        #                                   Default: tableize(:class_name)
        #                       :for|:on    <String> Class name to join to through Remixable
        #                                   This will create a M:M relationship THROUGH the remixable, rather than
        #                                   a 1:M with the remixable
        #                       :via        <String> changes the name of the second id in a unary relationship
        #                                   see example below; only used when remixing a module between the same class twice
        #                                   ie: self.class.to_s == options[:for||:on]
        #                       :unique     <Boolean> Only works with :for|:on; creates a unique composite key
        #                                   over the two table id's
        # ==== Examples
        # Given: User (Class), Addressable (Module)
        #
        #   One-To-Many; Class-To-Remixable
        #
        #   remix n, :addressables,
        #     :class_name => "UserAddress",
        #     :as         => "addresses"
        #
        #   Tables: users, user_addresses
        #   Classes: User, UserAddress
        #     User.user_addresses << UserAddress.new
        #     User.addresses << UserAddress.new
        #   --------------------------------------------
        #   --------------------------------------------
        #
        # Given: User (Class), Video (Class), Commentable (Module)
        #
        #   Many-To-Many; Class-To-Class through RemixableIntermediate (Video allows Commentable for User)
        #
        #   Video.remix n, :commentables
        #     :for        => 'User'    #:for & :on have same effect, just a choice of wording...
        #   --------------------------------------------
        #   --------------------------------------------
        #
        # Given: User (Class), User (Class), Commentable (Module)
        #
        #   Many-To-Many Unary relationship between User & User through comments
        #   User.remix n, :commentables, :as => "comments", :for => 'User', :via => "commentor"
        #   => This would create user_id and commentor_id as the
        #
        def remix(cardinality, remixable, options={})
          #A map for remixable names to Remixed Models
          @remixables = {} if @remixables.nil?

          # Allow nested modules to be remixable to better support using dm-is-remixable in gems
          # Example (from my upcoming dm-is-rateable gem)
          # remix n, "DataMapper::Is::Rateable::Rating", :as => :ratings
          remixable_module = Object.full_const_get(Extlib::Inflection.classify(remixable))
          
          #Merge defaults/options
          options = {
            :as         => nil,
            :class_name => Extlib::Inflection.classify(self.name.snake_case + "_" + remixable_module.suffix.pluralize),
            :for        => nil,
            :on         => nil,
            :unique     => false,
            :via        => nil
          }.merge(options)

          #Make sure the class hasn't been remixed already
          unless Object.full_const_defined?(Extlib::Inflection.classify(options[:class_name]))

            #Storage name of our remixed model
            options[:table_name] = Extlib::Inflection.tableize(options[:class_name])

            #Other model to mix with in case of M:M through Remixable
            options[:other_model] = options[:for] || options[:on]

            puts " ~ Generating Remixed Model: #{options[:class_name]}"
            model = generate_remixed_model(remixable_module, options)

            # map the remixable to the remixed model            
            # since this will be used from 'enhance api' i think it makes perfect sense to
            # always refer to a remixable by its demodulized snake_cased constant name
            remixable_key = Extlib::Inflection.demodulize(remixable_module.name).snake_case.to_sym
            populate_remixables_mapping(model, options.merge(:remixable_key => remixable_key))

            #Create relationships between Remixer and remixed class
            if options[:other_model]
              # M:M Class-To-Class w/ Remixable Module as intermediate table
              # has n and belongs_to (or One-To-Many)
              remix_many_to_many cardinality, model, options
            else
              # 1:M Class-To-Remixable
              # has n and belongs_to (or One-To-Many)
              remix_one_to_many cardinality, model, options
            end

            #Add accessor alias
            attach_accessor(options) unless options[:as].nil?
          else
            DataMapper.logger.warn "#{__FILE__}:#{__LINE__} warning: already remixed constant #{options[:class_name]}"
          end
        end

        # - enhance
        # ==== Description
        #   Enhance a remix; allows nesting remixables, adding columns & functions to a remixed resource
        # ==== Parameters
        #   remixable <Symbol> Name of remixable to enhance (plural or singular name of is :remixable module)
        #   block     <Proc>    Enhancements to perform
        # ==== Examples
        #   class Video
        #     include DataMapper::Resource
        #     remix Comment
        #
        #     enhance :comments do
        #       remix n, :votes        #This would result in something like YouTubes Voting comments up/down
        #
        #       property :updated_at, DateTime
        #
        #       def backwards; self.test.reverse; end;
        #     end
        def enhance(remixable,&block)
          # always use innermost singular snake_cased constant name
          model = @remixables[remixable.to_s.singular.snake_case.to_sym][:model]

          unless model.nil?
            model.class_eval &block
          else
{'fraction_non_alphanumeric': 0.11099085448346772, 'fraction_numerical': 0.0003246928946371557, 'mean_word_length': 1.702544603685288, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 42, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4720991', 'n_tokens_mistral': 4914, 'n_tokens_neox': 4755, 'n_words': 1401}