lib/gdpr_exporter.rb
# Declared in each model class with interest in collecting gdpr data.
# Instruments the singleton of those classes so that gdpr data can be
# collected and exported to csv.
#
# Arguments are:
# - set of simple fields: i.e. fields that will be output as is
# - a hash of params:
# {renamed_fields: {<field_from_db> => <field_name_in_output>}
#  table_name:     <the new table name in output>
#  description:    <a comment>
#  join:           <an association>}
Declared in each model class with interest in collecting gdpr data.
Instruments the singleton of those classes so that gdpr data can be
collected and exported to csv.

Arguments are:
set of simple fields: i.e.
Declared in each model class with interest in collecting gdpr data . Instruments the singleton of those classes so that gdpr data can be collected and exported to csv . Arguments are : set of simple fields : i . e .
def gdpr_collect(*args)
      if args.class == Hash 
        simple_fields, hash_params = [[], args]
      else
        simple_fields, hash_params = [args[0..-2], args.last]
      end
      unless hash_params.class == Hash
        raise ArgumentError.new("Gdpr fields collection error: last argument must be a hash!")
      end
      unless hash_params.key?(:user_id)
        raise ArgumentError.new("Gdpr fields collection error: the field aliasing user_id is not declared for '{self}'!")
      end
      GdprExporter.add_klass(self)
      @gdpr_simple_fields = simple_fields
      @gdpr_hash_params = hash_params
      self.class.send :attr_reader, :gdpr_simple_fields
      self.class.send :attr_reader, :gdpr_hash_params
      user_id_field = hash_params[:user_id]
       csv_headers = [:user_id].concat @gdpr_simple_fields  Uncomment if user_id needed
       query_fields = [user_id_field].concat @gdpr_simple_fields  Uncomment if user_id needed
      csv_headers = [].concat @gdpr_simple_fields
      query_fields = [].concat @gdpr_simple_fields
      if hash_params[:renamed_fields]
        csv_headers.concat hash_params[:renamed_fields].values
        query_fields.concat hash_params[:renamed_fields].keys
      end
       Adds the class method 'gdpr_query' to the eigenclass.
       It will execute the query.
      self.define_singleton_method(:gdpr_query) do |_user_id|
        decomposed_user_id_field = user_id_field.to_s.split(" ")
        result = case
                 when decomposed_user_id_field.size == 3
                   self
                     .includes(decomposed_user_id_field.first.to_sym => decomposed_user_id_field.second.to_sym)
                     .where(decomposed_user_id_field.second.pluralize.to_sym => { decomposed_user_id_field.last.to_sym => _user_id })
                 when decomposed_user_id_field.size == 2
                   self
                     .includes(decomposed_user_id_field.first.to_sym)
                     .where(decomposed_user_id_field.first.pluralize.to_sym => { decomposed_user_id_field.last.to_sym => _user_id })
                 else
                   self.where(user_id_field => _user_id)
                 end
         When there are multiple joins defined, just keep calling 'joins'
         for each association.
        if hash_params[:joins]
          result = hash_params[:joins].inject(result) do | query, assoc |
            query.send(:joins, assoc)
          end
        end
        result
      end
       Adds a method to export to csv to the eigenclass.
      self.define_singleton_method(:gdpr_export) do |rows, csv|
        return unless !rows.empty?
        csv << (hash_params[:table_name] ? [hash_params[:table_name]] :
                  [self.to_s])
        if hash_params[:desc]
          csv << ['Description:', hash_params[:desc]]
        end
        csv << csv_headers
        rows.each do |r|
          csv << query_fields.map do |f|
            f_splitted = f.to_s.split(' ')
            if (f_splitted.size == 2)
               field f is coming from an assoc, i.e. field has been defined
               as "<tablename> <field>" in gdpr_collect then to get its value
               do r.<tablename>.<field>
              f_splitted.inject(r) { |result, method| result.send(method) }
            elsif (f_splitted.size > 2)
              raise ArgumentError.new("Field {f} is made of more than 2 words!?")
            else
               No association involved, simply retrieve the field value.
              r.send(f)
            end
          end
        end
        csv << []
      end
    end
def gdpr_collect ( * args ) if args . class == Hash simple_fields , hash_params = [ [ ] , args ] else simple_fields , hash_params = [ args [ 0 .. - 2 ] , args . last ] end unless hash_params . class == Hash raise ArgumentError . new ( "Gdpr fields collection error: last argument must be a hash!" ) end unless hash_params . key? ( :user_id ) raise ArgumentError . new ( "Gdpr fields collection error: the field aliasing user_id is not declared for '#{self}'!" ) end GdprExporter . add_klass ( self ) @gdpr_simple_fields = simple_fields @gdpr_hash_params = hash_params self . class . send :attr_reader , :gdpr_simple_fields self . class . send :attr_reader , :gdpr_hash_params user_id_field = hash_params [ :user_id ] csv_headers = [ ] . concat @gdpr_simple_fields query_fields = [ ] . concat @gdpr_simple_fields if hash_params [ :renamed_fields ] csv_headers . concat hash_params [ :renamed_fields ] . values query_fields . concat hash_params [ :renamed_fields ] . keys end self . define_singleton_method ( :gdpr_query ) do | _user_id | decomposed_user_id_field = user_id_field . to_s . split ( " " ) result = case when decomposed_user_id_field . size == 3 self . includes ( decomposed_user_id_field . first . to_sym => decomposed_user_id_field . second . to_sym ) . where ( decomposed_user_id_field . second . pluralize . to_sym => { decomposed_user_id_field . last . to_sym => _user_id } ) when decomposed_user_id_field . size == 2 self . includes ( decomposed_user_id_field . first . to_sym ) . where ( decomposed_user_id_field . first . pluralize . to_sym => { decomposed_user_id_field . last . to_sym => _user_id } ) else self . where ( user_id_field => _user_id ) end if hash_params [ :joins ] result = hash_params [ :joins ] . inject ( result ) do | query , assoc | query . send ( :joins , assoc ) end end result end self . define_singleton_method ( :gdpr_export ) do | rows , csv | return unless ! rows . empty? csv << ( hash_params [ :table_name ] ? [ hash_params [ :table_name ] ] : [ self . to_s ] ) if hash_params [ :desc ] csv << [ 'Description:' , hash_params [ :desc ] ] end csv << csv_headers rows . each do | r | csv << query_fields . map do | f | f_splitted = f . to_s . split ( ' ' ) if ( f_splitted . size == 2 ) f_splitted . inject ( r ) { | result , method | result . send ( method ) } elsif ( f_splitted . size > 2 ) raise ArgumentError . new ( "Field #{f} is made of more than 2 words!?" ) else r . send ( f ) end end end csv << [ ] end end
Declared in each model class with interest in collecting gdpr data.
Declared in each model class with interest in collecting gdpr data .
# Params handling # when user provides the hash_params only # Adds the eigen class to the set of classes eligible for gdpr data collection. # Adds instance fields to the eigenclass. They store # all the fields and info we are interested in. # Add readers for the instance vars declared above (for testing reasons) # Build the csv header and prepare the fields used for querying # # csv_headers = [:user_id].concat @gdpr_simple_fields # Uncomment if user_id needed # query_fields = [user_id_field].concat @gdpr_simple_fields # Uncomment if user_id needed # Adds the class method 'gdpr_query' to the eigenclass. # It will execute the query. # When there are multiple joins defined, just keep calling 'joins' # for each association. # Adds a method to export to csv to the eigenclass. # field f is coming from an assoc, i.e. field has been defined # as "<tablename> <field>" in gdpr_collect then to get its value # do r.<tablename>.<field> # No association involved, simply retrieve the field value.
args