require 'flydata/heroku/instance_methods' require 'flydata/heroku/configuration_methods' module Flydata module Heroku extend ActiveSupport::Concern included do if defined? ActiveModel # Reopen ActiveModel::Serialization to add flydata support module ActiveModel::Serialization extend ActiveSupport::Concern include Flydata::Heroku::InstanceMethods include Flydata::Heroku::ConfigurationMethods end end end module ClassMethods # Puts a JSON string that represents args to $stdout # # @example # send_to('table_name', { key: 'value' }) # send_to('table_name', [{ key: 'value' }, { key: 'value' ]) # # @example # send_to(:table_name, { key: 'value' }) # send_to(:table_name', [{ key: 'value' }, { key: 'value' ]) # # @example # send_to(active_record) # send_to(active_record, active_record) # # @example # send_to([user, user]) # # @param [String, Symbol] table name # @param [Hash] values # @param [Array] array of value # @param [ActiveRecord::Base] record # @param [ActiveModel::Serialization] model # @param [Array] array of record # @param [Array] array of model def send_to(*args) if args[0].nil? raise ArgumentError, 'The argument of send_to must be non-nil' end if args[0].respond_to?(:empty?) && args[0].empty? raise ArgumentError, 'The 1st argument of send_to must not be empty' end if (args[0].kind_of?(String) || args[0].kind_of?(Symbol)) unless structured_data?(args[1]) raise ArgumentError, 'The 2nd argument of send_to must be an Array or Hash' end end obj = case args[0] when String structured_data_as_flydata(args[0], args[1]) when Symbol structured_data_as_flydata(args[0].to_s, args[1]) when Array ancestors = args[0][0].class.ancestors if defined?(ActiveModel) && ancestors.include?(ActiveModel::Serialization) active_models_as_flydata(args[0]) end else ancestors = args[0].class.ancestors if defined?(ActiveModel) && ancestors.include?(ActiveModel::Serialization) multiple_active_models_as_flydata(args) end end $stdout.puts(obj.to_json) if obj end # Formats a Time object for Redshift # # @param [#strftime] # @return [String, Objct] def format_time_for_redshift(time) time.respond_to?(:strftime) ? time.strftime('%Y-%m-%d %H:%M:%S') : time end # Formats a Time object for Redshift # # @param [#strftime] # @return [String, Objct] def format_date_for_redshift(time) time.respond_to?(:strftime) ? time.strftime('%Y-%m-%d') : time end private # @param [String] # @param [Hash, Array] # @return [Hash] def structured_data_as_flydata(table_name, values) { table_name => format_for_flydata(values) } end # @param [Object] # @return [Object] def format_for_flydata(obj) case obj when Hash obj.each { |k, v| obj[k] = format_for_flydata(v) } when Array obj.map { |item| item.kind_of?(Hash) ? format_for_flydata(item) : item } when Time format_time_for_redshift(obj) when DateTime format_time_for_redshift(obj) when Date format_date_for_redshift(obj) else obj end end # @param [Object] # @return [Boolean] def structured_data?(value) value.kind_of?(Array) || value.kind_of?(Hash) end if defined?(ActiveModel) # @param [Array] # @return [Hash] def active_models_as_flydata(models) table_name = models[0].class.flydata_table_name { table_name => models.map(&:as_flydata) } end # @param [Array] # @return [Hash] def multiple_active_models_as_flydata(models) hash = {} models.group_by { |record| record.class.flydata_table_name }.each do |table_name, models| hash[table_name] = if models.size == 1 models[0].as_flydata else models.map(&:as_flydata) end end hash end end end end end