<commit_before>require 'virtus'

module FormThis
  class Base
    include Virtus.model
    include ActiveModel::Model

    attr_accessor :record


    # Manually set the model name
    def self.model name
      @_model_name = name
    end


    # Get the model name; used by form_for
    # The convention is to use ModelNameForm, and ModelNameForm_NestedModel for
    # nested objects
    def self.model_name
      ::ActiveModel::Name.new self, nil,
        @_model_name || self.name.split('_').pop.sub(/Form$/, '')
    end


    # Use the i18n keys from activerecord
    def self.i18n_scope
      :activerecord
    end


    # Define a property
    def self.property name, opts={}
      attribute name, opts[:type] || String
      validates name, opts[:validates] if opts[:validates]

      if self.has_superclass opts[:type]
        define_method "#{name}_attributes=" do |params|
          self.send(name).validate params
        end
      end

      if opts[:type].is_a? Enumerable
        define_method "#{name}_attributes=" do |params|
          # TODO: Is using the index from params as the index a good idea? Where
          # does this index come from anyway?
          !params.map { |id, v| self.send(name)[id.to_i].validate v, self }.include? false
        end
      end
    end


    # Define multiple properties<commit_msg>Work better with one-to-many relations<commit_after>require 'virtus'

module FormThis
  class Base
    include Virtus.model
    include ActiveModel::Model

    attr_accessor :record


    # Manually set the model name
    def self.model name
      @_model_name = name
    end


    # Get the model name; used by form_for
    # The convention is to use ModelNameForm, and ModelNameForm_NestedModel for
    # nested objects
    def self.model_name
      ::ActiveModel::Name.new self, nil,
        @_model_name || self.name.split('_').pop.sub(/Form$/, '')
    end


    # Use the i18n keys from activerecord
    def self.i18n_scope
      :activerecord
    end


    # Define a property
    def self.property name, opts={}
      attribute name, opts[:type] || String
      validates name, opts[:validates] if opts[:validates]

      if self.has_superclass opts[:type]
        define_method "#{name}_attributes=" do |params|
          self.send(name).validate params
        end
      end

      if opts[:type].is_a? Enumerable
        define_method "#{name}_attributes=" do |params|
          # TODO
          # >> params[:contractees_attributes]
          # => {"0"=>{"role"=>"lessor", "organisation_person_id"=>"1"}, # "3"=>{"role"=>"signee", "organisation_person"=>"1"}}
          #
          # Where do these keys come from (specifically, '3')?
          #
          # This is (probably) an *approximation* of what AR does; we need to
          # figure out better how this works *exactly*
          #!params.map { |id, v| self.send(name)[id.to_i].validate v, self }.include? false
          !params.values.map.with_index { |v, id| self.send(name)[id].validate v, self }.include? false
        end
      end
    end


    # Define multiple properties
{'fraction_non_alphanumeric': 0.1049902786779002, 'fraction_numerical': 0.004212572909915748, 'mean_word_length': 2.5079545454545453, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '181787', 'n_tokens_mistral': 950, 'n_tokens_neox': 905, 'n_words': 278}