# Rails 5 supports PostgreSQL >= 9.1
# This is a custom Postgres adapter for ovirt_metrics to utilize
# ActiveRecord 5.x while connecting to a PostgreSQL 8.4 database.
# It's also a necessary evil, so ye be warned.

# Make sure we're using pg high enough for type casts and Ruby 2.2+ compatibility
gem 'pg', '~> 0.18'
require 'pg'

require "active_record/connection_adapters/abstract_adapter"
require "active_record/connection_adapters/ovirt_legacy_postgresql/column"
require "active_record/connection_adapters/ovirt_legacy_postgresql/database_statements"
require "active_record/connection_adapters/ovirt_legacy_postgresql/explain_pretty_printer"
require "active_record/connection_adapters/ovirt_legacy_postgresql/oid"
require "active_record/connection_adapters/ovirt_legacy_postgresql/quoting"
require "active_record/connection_adapters/ovirt_legacy_postgresql/referential_integrity"
require "active_record/connection_adapters/ovirt_legacy_postgresql/schema_definitions"
require "active_record/connection_adapters/ovirt_legacy_postgresql/schema_dumper"
require "active_record/connection_adapters/ovirt_legacy_postgresql/schema_statements"
require "active_record/connection_adapters/ovirt_legacy_postgresql/type_metadata"
require "active_record/connection_adapters/ovirt_legacy_postgresql/utils"
require "active_record/connection_adapters/statement_pool"

module ActiveRecord
  module ConnectionHandling # :nodoc:
    # Establishes a connection to the database that's used by all Active Record objects
    def ovirt_legacy_postgresql_connection(config)
      conn_params = config.symbolize_keys

      conn_params.delete_if { |_, v| v.nil? }

      # Map ActiveRecords param names to PGs.
      conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
      conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]

      # Forward only valid config params to PGconn.connect.
      valid_conn_param_keys = PGconn.conndefaults_hash.keys + [:requiressl]
      conn_params.slice!(*valid_conn_param_keys)

      # The postgres drivers don't allow the creation of an unconnected PGconn object,
      # so just pass a nil connection object for the time being.
      ConnectionAdapters::OvirtLegacyPostgreSQLAdapter.new(nil, logger, conn_params, config)
    end
  end

  module ConnectionAdapters
    # The OvirtLegacyPostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
    #
    # Options:
    #
    # * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
    #   the default is to connect to localhost.
    # * <tt>:port</tt> - Defaults to 5432.
    # * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
    # * <tt>:password</tt> - Password to be used if the server demands password authentication.
    # * <tt>:database</tt> - Defaults to be the same as the user name.
    # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
    #   as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
    # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
    #   <encoding></tt> call on the connection.
    # * <tt>:min_messages</tt> - An optional client min messages that is used in a
    #   <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
    # * <tt>:variables</tt> - An optional hash of additional parameters that
    #   will be used in <tt>SET SESSION key = val</tt> calls on the connection.
    # * <tt>:insert_returning</tt> - An optional boolean to control the use of <tt>RETURNING</tt> for <tt>INSERT</tt> statements
    #   defaults to true.
    #
    # Any further options are used as connection parameters to libpq. See
    # http://www.postgresql.org/docs/current/static/libpq-connect.html for the
    # list of parameters.
    #
    # In addition, default connection parameters of libpq can be set per environment variables.
    # See http://www.postgresql.org/docs/current/static/libpq-envars.html .
    class OvirtLegacyPostgreSQLAdapter < AbstractAdapter
      ADAPTER_NAME = 'OvirtLegacyPostgreSQL'.freeze

      NATIVE_DATABASE_TYPES = {
        primary_key: "serial primary key",
        string:      { name: "character varying" },
        text:        { name: "text" },
        integer:     { name: "integer" },
        float:       { name: "float" },
        decimal:     { name: "decimal" },
        datetime:    { name: "timestamp" },
        time:        { name: "time" },
        date:        { name: "date" },
        daterange:   { name: "daterange" },
        numrange:    { name: "numrange" },
        tsrange:     { name: "tsrange" },
        tstzrange:   { name: "tstzrange" },
        int4range:   { name: "int4range" },
        int8range:   { name: "int8range" },
        binary:      { name: "bytea" },
        boolean:     { name: "boolean" },
        xml:         { name: "xml" },
        tsvector:    { name: "tsvector" },
        hstore:      { name: "hstore" },
        inet:        { name: "inet" },
        cidr:        { name: "cidr" },
        macaddr:     { name: "macaddr" },
        uuid:        { name: "uuid" },
        json:        { name: "json" },
        jsonb:       { name: "jsonb" },
        ltree:       { name: "ltree" },
        citext:      { name: "citext" },
        point:       { name: "point" },
        line:        { name: "line" },
        lseg:        { name: "lseg" },
        box:         { name: "box" },
        path:        { name: "path" },
        polygon:     { name: "polygon" },
        circle:      { name: "circle" },
        bit:         { name: "bit" },
        bit_varying: { name: "bit varying" },
        money:       { name: "money" },
      }

      OID = OvirtLegacyPostgreSQL::OID #:nodoc:

      include OvirtLegacyPostgreSQL::Quoting
      include OvirtLegacyPostgreSQL::ReferentialIntegrity
      include OvirtLegacyPostgreSQL::SchemaStatements
      include OvirtLegacyPostgreSQL::DatabaseStatements
      include OvirtLegacyPostgreSQL::ColumnDumper

      def schema_creation # :nodoc:
        OvirtLegacyPostgreSQL::SchemaCreation.new self
      end

      def arel_visitor # :nodoc:
        Arel::Visitors::PostgreSQL.new(self)
      end

      # Returns true, since this connection adapter supports prepared statement
      # caching.
      def supports_statement_cache?
        true
      end

      def supports_index_sort_order?
        true
      end

      def supports_partial_index?
        true
      end

      def supports_expression_index?
        true
      end

      def supports_transaction_isolation?
        true
      end

      def supports_foreign_keys?
        true
      end

      def supports_views?
        true
      end

      def supports_datetime_with_precision?
        true
      end

      def supports_json?
        postgresql_version >= 90200
      end

      def supports_comments?
        true
      end

      def supports_comments_in_create?
        false
      end

      def supports_savepoints?
        true
      end

      def supports_collation?
        postgresql_version >= 90100
      end

      def index_algorithms
        { concurrently: 'CONCURRENTLY' }
      end

      class StatementPool < ConnectionAdapters::StatementPool
        def initialize(connection, max)
          super(max)
          @connection = connection
          @counter = 0
        end

        def next_key
          "a#{@counter + 1}"
        end

        def []=(sql, key)
          super.tap { @counter += 1 }
        end

        private

          def dealloc(key)
            @connection.query "DEALLOCATE #{key}" if connection_active?
          end

          def connection_active?
            @connection.status == PGconn::CONNECTION_OK
          rescue PGError
            false
          end
      end

      # Initializes and connects a OvirtLegacyPostgreSQL adapter.
      def initialize(connection, logger, connection_parameters, config)
        super(connection, logger, config)

        @connection_parameters = connection_parameters

        # @local_tz is initialized as nil to avoid warnings when connect tries to use it
        @local_tz = nil
        @table_alias_length = nil

        connect
        add_pg_encoders
        @statements = StatementPool.new @connection,
                                        self.class.type_cast_config_to_integer(config[:statement_limit])

        if postgresql_version < 80200
          raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
        end

        add_pg_decoders

        @type_map = Type::HashLookupTypeMap.new
        initialize_type_map(type_map)
        @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
        @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
      end

      # Clears the prepared statements cache.
      def clear_cache!
        @statements.clear
      end

      def truncate(table_name, name = nil)
        exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
      end

      # Is this connection alive and ready for queries?
      def active?
        @connection.query 'SELECT 1'
        true
      rescue PGError
        false
      end

      # Close then reopen the connection.
      def reconnect!
        super
        @connection.reset
        configure_connection
      end

      def reset!
        clear_cache!
        reset_transaction
        unless @connection.transaction_status == ::PG::PQTRANS_IDLE
          @connection.query 'ROLLBACK'
        end
        @connection.query 'DISCARD ALL'
        configure_connection
      end

      # Disconnects from the database if already connected. Otherwise, this
      # method does nothing.
      def disconnect!
        super
        @connection.close rescue nil
      end

      def native_database_types #:nodoc:
        NATIVE_DATABASE_TYPES
      end

      # Returns true, since this connection adapter supports migrations.
      def supports_migrations?
        true
      end

      # Does PostgreSQL support finding primary key on non-Active Record tables?
      def supports_primary_key? #:nodoc:
        true
      end

      def set_standard_conforming_strings
        execute('SET standard_conforming_strings = on', 'SCHEMA')
      end

      def supports_ddl_transactions?
        true
      end

      def supports_advisory_locks?
        true
      end

      def supports_explain?
        true
      end

      def supports_extensions?
        postgresql_version >= 90100
      end

      # Range datatypes weren't introduced until PostgreSQL 9.2
      def supports_ranges?
        postgresql_version >= 90200
      end

      def supports_materialized_views?
        postgresql_version >= 90300
      end

      def get_advisory_lock(lock_id) # :nodoc:
        unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
          raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
        end
        select_value("SELECT pg_try_advisory_lock(#{lock_id});")
      end

      def release_advisory_lock(lock_id) # :nodoc:
        unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
          raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
        end
        select_value("SELECT pg_advisory_unlock(#{lock_id})")
      end

      def enable_extension(name)
        exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
          reload_type_map
        }
      end

      def disable_extension(name)
        exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
          reload_type_map
        }
      end

      def extension_enabled?(name)
        if supports_extensions?
          res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled",
            'SCHEMA'
          res.cast_values.first
        end
      end

      def extensions
        if supports_extensions?
          exec_query("SELECT extname from pg_extension", "SCHEMA").cast_values
        else
          super
        end
      end

      # Returns the configured supported identifier length supported by PostgreSQL
      def table_alias_length
        @table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
      end

      # Set the authorized user for this session
      def session_auth=(user)
        clear_cache!
        exec_query "SET SESSION AUTHORIZATION #{user}"
      end

      def use_insert_returning?
        @use_insert_returning
      end

      def valid_type?(type)
        !native_database_types[type].nil?
      end

      def update_table_definition(table_name, base) #:nodoc:
        OvirtLegacyPostgreSQL::Table.new(table_name, base)
      end

      def lookup_cast_type(sql_type) # :nodoc:
        oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").first['oid'].to_i
        super(oid)
      end

      def column_name_for_operation(operation, node) # :nodoc:
        OPERATION_ALIASES.fetch(operation) { operation.downcase }
      end

      OPERATION_ALIASES = { # :nodoc:
        "maximum" => "max",
        "minimum" => "min",
        "average" => "avg",
      }

      # Returns the version of the connected PostgreSQL server.
      def postgresql_version
        @connection.server_version
      end

      protected

        # See http://www.postgresql.org/docs/current/static/errcodes-appendix.html
        VALUE_LIMIT_VIOLATION = "22001"
        FOREIGN_KEY_VIOLATION = "23503"
        UNIQUE_VIOLATION      = "23505"

        def translate_exception(exception, message)
          return exception unless exception.respond_to?(:result)

          case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
          when UNIQUE_VIOLATION
            RecordNotUnique.new(message)
          when FOREIGN_KEY_VIOLATION
            InvalidForeignKey.new(message)
          when VALUE_LIMIT_VIOLATION
            ValueTooLong.new(message)
          else
            super
          end
        end

      private

        def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
          if !type_map.key?(oid)
            load_additional_types(type_map, [oid])
          end

          type_map.fetch(oid, fmod, sql_type) {
            warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
            Type::Value.new.tap do |cast_type|
              type_map.register_type(oid, cast_type)
            end
          }
        end

        def initialize_type_map(m) # :nodoc:
          register_class_with_limit m, 'int2', Type::Integer
          register_class_with_limit m, 'int4', Type::Integer
          register_class_with_limit m, 'int8', Type::Integer
          m.alias_type 'oid', 'int2'
          m.register_type 'float4', Type::Float.new
          m.alias_type 'float8', 'float4'
          m.register_type 'text', Type::Text.new
          register_class_with_limit m, 'varchar', Type::String
          m.alias_type 'char', 'varchar'
          m.alias_type 'name', 'varchar'
          m.alias_type 'bpchar', 'varchar'
          m.register_type 'bool', Type::Boolean.new
          register_class_with_limit m, 'bit', OID::Bit
          register_class_with_limit m, 'varbit', OID::BitVarying
          m.alias_type 'timestamptz', 'timestamp'
          m.register_type 'date', Type::Date.new

          m.register_type 'money', OID::Money.new
          m.register_type 'bytea', OID::Bytea.new
          m.register_type 'point', OID::Point.new
          m.register_type 'hstore', OID::Hstore.new
          m.register_type 'json', OID::Json.new
          m.register_type 'jsonb', OID::Jsonb.new
          m.register_type 'cidr', OID::Cidr.new
          m.register_type 'inet', OID::Inet.new
          m.register_type 'uuid', OID::Uuid.new
          m.register_type 'xml', OID::Xml.new
          m.register_type 'tsvector', OID::SpecializedString.new(:tsvector)
          m.register_type 'macaddr', OID::SpecializedString.new(:macaddr)
          m.register_type 'citext', OID::SpecializedString.new(:citext)
          m.register_type 'ltree', OID::SpecializedString.new(:ltree)
          m.register_type 'line', OID::SpecializedString.new(:line)
          m.register_type 'lseg', OID::SpecializedString.new(:lseg)
          m.register_type 'box', OID::SpecializedString.new(:box)
          m.register_type 'path', OID::SpecializedString.new(:path)
          m.register_type 'polygon', OID::SpecializedString.new(:polygon)
          m.register_type 'circle', OID::SpecializedString.new(:circle)

          # FIXME: why are we keeping these types as strings?
          m.alias_type 'interval', 'varchar'

          register_class_with_precision m, 'time', Type::Time
          register_class_with_precision m, 'timestamp', OID::DateTime

          m.register_type 'numeric' do |_, fmod, sql_type|
            precision = extract_precision(sql_type)
            scale = extract_scale(sql_type)

            # The type for the numeric depends on the width of the field,
            # so we'll do something special here.
            #
            # When dealing with decimal columns:
            #
            # places after decimal  = fmod - 4 & 0xffff
            # places before decimal = (fmod - 4) >> 16 & 0xffff
            if fmod && (fmod - 4 & 0xffff).zero?
              # FIXME: Remove this class, and the second argument to
              # lookups on PG
              Type::DecimalWithoutScale.new(precision: precision)
            else
              OID::Decimal.new(precision: precision, scale: scale)
            end
          end

          load_additional_types(m)
        end

        def extract_limit(sql_type) # :nodoc:
          case sql_type
          when /^bigint/i, /^int8/i
            8
          when /^smallint/i
            2
          else
            super
          end
        end

        # Extracts the value from a PostgreSQL column default definition.
        def extract_value_from_default(default) # :nodoc:
          case default
            # Quoted types
            when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
              # The default 'now'::date is CURRENT_DATE
              if $1 == "now".freeze && $2 == "date".freeze
                nil
              else
                $1.gsub("''".freeze, "'".freeze)
              end
            # Boolean types
            when 'true'.freeze, 'false'.freeze
              default
            # Numeric types
            when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
              $1
            # Object identifier types
            when /\A-?\d+\z/
              $1
            else
              # Anything else is blank, some user type, or some function
              # and we can't know the value of that, so return nil.
              nil
          end
        end

        def extract_default_function(default_value, default) # :nodoc:
          default if has_default_function?(default_value, default)
        end

        def has_default_function?(default_value, default) # :nodoc:
          !default_value && (%r{\w+\(.*\)|\(.*\)::\w+} === default)
        end

        def load_additional_types(type_map, oids = nil) # :nodoc:
          initializer = OID::TypeMapInitializer.new(type_map)

          if supports_ranges?
            query = <<-SQL
              SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
              FROM pg_type as t
              LEFT JOIN pg_range as r ON oid = rngtypid
            SQL
          else
            query = <<-SQL
              SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
              FROM pg_type as t
            SQL
          end

          if oids
            query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
          else
            query += initializer.query_conditions_for_initial_load(type_map)
          end

          execute_and_clear(query, 'SCHEMA', []) do |records|
            initializer.run(records)
          end
        end

        FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:

        def execute_and_clear(sql, name, binds, prepare: false)
          if without_prepared_statement?(binds)
            result = exec_no_cache(sql, name, [])
          elsif !prepare
            result = exec_no_cache(sql, name, binds)
          else
            result = exec_cache(sql, name, binds)
          end
          ret = yield result
          result.clear
          ret
        end

        def exec_no_cache(sql, name, binds)
          type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
          log(sql, name, binds) { @connection.async_exec(sql, type_casted_binds) }
        end

        def exec_cache(sql, name, binds)
          stmt_key = prepare_statement(sql)
          type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }

          log(sql, name, binds, stmt_key) do
            @connection.exec_prepared(stmt_key, type_casted_binds)
          end
        rescue ActiveRecord::StatementInvalid => e
          raise unless is_cached_plan_failure?(e)

          # Nothing we can do if we are in a transaction because all commands
          # will raise InFailedSQLTransaction
          if in_transaction?
            raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
          else
            # outside of transactions we can simply flush this query and retry
            @statements.delete sql_key(sql)
            retry
          end
        end

        # Annoyingly, the code for prepared statements whose return value may
        # have changed is FEATURE_NOT_SUPPORTED.
        #
        # This covers various different error types so we need to do additional
        # work to classify the exception definitively as a
        # ActiveRecord::PreparedStatementCacheExpired
        #
        # Check here for more details:
        # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
        CACHED_PLAN_HEURISTIC = 'cached plan must not change result type'.freeze
        def is_cached_plan_failure?(e)
          pgerror = e.cause
          code = pgerror.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
          code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC)
        rescue
          false
        end

        def in_transaction?
          open_transactions > 0
        end

        # Returns the statement identifier for the client side cache
        # of statements
        def sql_key(sql)
          "#{schema_search_path}-#{sql}"
        end

        # Prepare the statement if it hasn't been prepared, return
        # the statement key.
        def prepare_statement(sql)
          sql_key = sql_key(sql)
          unless @statements.key? sql_key
            nextkey = @statements.next_key
            begin
              @connection.prepare nextkey, sql
            rescue => e
              raise translate_exception_class(e, sql)
            end
            # Clear the queue
            @connection.get_last_result
            @statements[sql_key] = nextkey
          end
          @statements[sql_key]
        end

        # Connects to a PostgreSQL server and sets up the adapter depending on the
        # connected server's characteristics.
        def connect
          @connection = PGconn.connect(@connection_parameters)
          configure_connection
        rescue ::PG::Error => error
          if error.message.include?("does not exist")
            raise ActiveRecord::NoDatabaseError
          else
            raise
          end
        end

        # Configures the encoding, verbosity, schema search path, and time zone of the connection.
        # This is called by #connect and should not be called manually.
        def configure_connection
          if @config[:encoding]
            @connection.set_client_encoding(@config[:encoding])
          end
          self.client_min_messages = @config[:min_messages] || 'warning'
          self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]

          # Use standard-conforming strings so we don't have to do the E'...' dance.
          set_standard_conforming_strings

          # If using Active Record's time zone support configure the connection to return
          # TIMESTAMP WITH ZONE types in UTC.
          # (SET TIME ZONE does not use an equals sign like other SET variables)
          if ActiveRecord::Base.default_timezone == :utc
            execute("SET time zone 'UTC'", 'SCHEMA')
          elsif @local_tz
            execute("SET time zone '#{@local_tz}'", 'SCHEMA')
          end

          # SET statements from :variables config hash
          # http://www.postgresql.org/docs/current/static/sql-set.html
          variables = @config[:variables] || {}
          variables.map do |k, v|
            if v == ':default' || v == :default
              # Sets the value to the global or compile default
              execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA')
            elsif !v.nil?
              execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA')
            end
          end
        end

        # Returns the current ID of a table's sequence.
        def last_insert_id_result(sequence_name) # :nodoc:
          exec_query("SELECT currval('#{sequence_name}')", 'SQL')
        end

        # Returns the list of a table's column names, data types, and default values.
        #
        # The underlying query is roughly:
        #  SELECT column.name, column.type, default.value, column.comment
        #    FROM column LEFT JOIN default
        #      ON column.table_id = default.table_id
        #     AND column.num = default.column_num
        #   WHERE column.table_id = get_table_id('table_name')
        #     AND column.num > 0
        #     AND NOT column.is_dropped
        #   ORDER BY column.num
        #
        # If the table name is not prefixed with a schema, the database will
        # take the first match from the schema search path.
        #
        # Query implementation notes:
        #  - format_type includes the column size constraint, e.g. varchar(50)
        #  - ::regclass is a function that gives the id for a table name
        def column_definitions(table_name) # :nodoc:
          query(<<-end_sql, 'SCHEMA')
              SELECT a.attname, format_type(a.atttypid, a.atttypmod),
                     pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
                     #{', (SELECT c.collname FROM pg_collation c, pg_type t
                         WHERE c.oid = a.attcollation AND t.oid = a.atttypid
                         AND a.attcollation <> t.typcollation)' if supports_collation?}
                     , col_description(a.attrelid, a.attnum) AS comment
                FROM pg_attribute a LEFT JOIN pg_attrdef d
                  ON a.attrelid = d.adrelid AND a.attnum = d.adnum
               WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
                 AND a.attnum > 0 AND NOT a.attisdropped
               ORDER BY a.attnum
          end_sql
        end

        def extract_table_ref_from_insert_sql(sql) # :nodoc:
          sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
          $1.strip if $1
        end

        def create_table_definition(*args) # :nodoc:
          OvirtLegacyPostgreSQL::TableDefinition.new(*args)
        end

        def can_perform_case_insensitive_comparison_for?(column)
          @case_insensitive_cache ||= {}
          @case_insensitive_cache[column.sql_type] ||= begin
            sql = <<-end_sql
              SELECT exists(
                SELECT * FROM pg_proc
                INNER JOIN pg_cast
                  ON casttarget::text::oidvector = proargtypes
                WHERE proname = 'lower'
                  AND castsource = '#{column.sql_type}'::regtype::oid
              )
            end_sql
            execute_and_clear(sql, "SCHEMA", []) do |result|
              result.getvalue(0, 0)
            end
          end
        end

        def add_pg_encoders
          map = PG::TypeMapByClass.new
          map[Integer] = PG::TextEncoder::Integer.new
          map[TrueClass] = PG::TextEncoder::Boolean.new
          map[FalseClass] = PG::TextEncoder::Boolean.new
          map[Float] = PG::TextEncoder::Float.new
          @connection.type_map_for_queries = map
        end

        def add_pg_decoders
          coders_by_name = {
            'int2' => PG::TextDecoder::Integer,
            'int4' => PG::TextDecoder::Integer,
            'int8' => PG::TextDecoder::Integer,
            'oid' => PG::TextDecoder::Integer,
            'float4' => PG::TextDecoder::Float,
            'float8' => PG::TextDecoder::Float,
            'bool' => PG::TextDecoder::Boolean,
          }
          known_coder_types = coders_by_name.keys.map { |n| quote(n) }
          query = <<-SQL % known_coder_types.join(", ")
            SELECT t.oid, t.typname
            FROM pg_type as t
            WHERE t.typname IN (%s)
          SQL
          coders = execute_and_clear(query, "SCHEMA", []) do |result|
            result
              .map { |row| construct_coder(row, coders_by_name[row['typname']]) }
              .compact
          end

          map = PG::TypeMapByOid.new
          coders.each { |coder| map.add_coder(coder) }
          @connection.type_map_for_results = map
        end

        def construct_coder(row, coder_class)
          return unless coder_class
          coder_class.new(oid: row['oid'].to_i, name: row['typname'])
        end

        ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :ovirt_legacy_postgresql)
        ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :ovirt_legacy_postgresql)
        ActiveRecord::Type.register(:bit, OID::Bit, adapter: :ovirt_legacy_postgresql)
        ActiveRecord::Type.register(:bit_varying, OID::BitVarying, adapter: :ovirt_legacy_postgresql)
        ActiveRecord::Type.register(:binary, OID::Bytea, adapter: :ovirt_legacy_postgresql)
        ActiveRecord::Type.register(:cidr, OID::Cidr, adapter: :ovirt_legacy_postgresql)
        ActiveRecord::Type.register(:datetime, OID::DateTime, adapter: :ovirt_legacy_postgresql)
        ActiveRecord::Type.register(:decimal, OID::Decimal, adapter: :ovirt_legacy_postgresql)
        ActiveRecord::Type.register(:enum, OID::Enum, adapter: :ovirt_legacy_postgresql)
        ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :ovirt_legacy_postgresql)
        ActiveRecord::Type.register(:inet, OID::Inet, adapter: :ovirt_legacy_postgresql)
        ActiveRecord::Type.register(:json, OID::Json, adapter: :ovirt_legacy_postgresql)
        ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :ovirt_legacy_postgresql)
        ActiveRecord::Type.register(:money, OID::Money, adapter: :ovirt_legacy_postgresql)
        ActiveRecord::Type.register(:point, OID::Rails51Point, adapter: :ovirt_legacy_postgresql)
        ActiveRecord::Type.register(:legacy_point, OID::Point, adapter: :ovirt_legacy_postgresql)
        ActiveRecord::Type.register(:uuid, OID::Uuid, adapter: :ovirt_legacy_postgresql)
        ActiveRecord::Type.register(:vector, OID::Vector, adapter: :ovirt_legacy_postgresql)
        ActiveRecord::Type.register(:xml, OID::Xml, adapter: :ovirt_legacy_postgresql)
    end
  end
end


require "httparty"
require "json"
require "neto_api_ruby/version"

module NetoApiRuby
  class API

  	def initialize url, api_key
  		#required args
  		@neto_url = url
  		#optional args
  		@header_args ={
  		  "Accept"      => "application/json",
  		  "Content-Type"=> "application/json",
  		  "NETOAPI_KEY" => api_key
  		}
  		@body_defaults = {
		        "IsActive"=> ["True"],
		        "OutputSelector"=> [
		            "ID",
                "SKU",
                "ParentSKU",
                "InventoryID",
                "Virtual",
                "Name",
                "Brand",
                "Description",
                "Active",
                "Visible",
                "Categories",
                "RRP",
                "PromotionPrice",
                "PriceGroups",
                "DateAdded",
                "DateAddedLocal",
                "DateAddedUTC",
                "DateUpdated",
                "DateUpdatedLocal",
                "DateUpdatedUTC",
                "Group",
                "WarehouseQuantity",
                "TaxInclusive"
		        ]
		  }
  	end
  	#public methods
  	#query    - A Hash with query params, insert into request body
  	#returns the request Array
  	def get_products query={}
  		do_request :post, 'GetItem', query
  	end

    def create_product attributes={}
      do_request :post, 'AddItem', attributes
    end

    def update_product attributes={}
      do_request :post, 'UpdateItem', attributes
    end

  	protected
  	#Internal methods: Append data as query params to an endpoint
  	def do_request method, action, query={}
  	  url = @neto_url
  	  options = { }
  	  header_action = {
  	  	"NETOAPI_ACTION"=> action
  	  }
  	  #set headers
  	  options[:headers] = @header_args.merge(header_action)
  	  #set body
      case action
      when 'GetItem'
    	  if query.empty?
    	  	options.merge!(body: {"Filter"=> @body_defaults}.to_json)
    	  else
        	body_args = @body_defaults.merge(query)
        	options.merge!(body: {"Filter"=> body_args}.to_json)
    	  end
      when 'AddItem'
        options.merge!(body: {"Item"=> [query]}.to_json)
      when 'UpdateItem'
        options.merge!(body: {"Item"=> [query]}.to_json)
      end
      HTTParty.send(method, url, options)
    end

  end
end


# frozen_string_literal: true

module Crypt
  # ISAAC is a fast, cryptographically secure pseudo-random number generator.
  # Details on the algorithm can be found here:
  # http://burtleburtle.net/bob/rand/isaac.html
  # This provides a consistent and capable algorithm for producing
  # independent streams of quality random numbers.
  class ISAAC
    attr_accessor :randrsl, :randcnt
    attr_accessor :mm, :aa, :bb, :cc
    attr_reader :seed

    # When a Crypt::ISAAC object is created, it needs to be seeded for
    # random number generation.  If the system has a /dev/urandom file,
    # that will be used to do the seeding by default.  If false is explictly
    # passed when creating the object, it will instead use /dev/random to
    # generate its seeds.  Be warned that this may make for SLOW
    # initialization - if there isn't enough entropy in the system, reads from
    # /dev/random will block while waiting for more entropy, causing
    # initialization to wait, as well.
    # If the requested source (/dev/urandom or /dev/random) do not exist,
    # the system will fall back to a simplistic initialization mechanism
    # using a pseudo-random number generator. By default, it will use an
    # xorshift* generator, but anything that responds to #rand can be passed
    # as a seed.

    def self.rand(arg = nil)
      DEFAULT.rand(arg)
    end

    def self.srand(seed = true)
      DEFAULT.srand(seed)
    end

    def self.new_seed(seed = true)
      seed_array = Array.new(256, 0)
      rnd_source = if (seed == true) || (seed == false)
                     if seed
                       '/dev/urandom'
                     else
                       '/dev/random'
                     end
                   end
      if seed.respond_to?(:each)
        (seed.length > 255 ? 256 : seed.length).times do |s|
          seed_array[s] = seed[s]
        end
      elsif rnd_source && (FileTest.exist? rnd_source)
        File.open(rnd_source, 'r') do |r|
          256.times do |t|
            z = r.read(4)
            x = z.unpack('V')[0]
            seed_array[t] = x
          end
        end
      else
        seed = nil if rnd_source

        seed_prng = if seed.respond_to?(:rand)
                      seed
                    else
                      Crypt::Xorshift64Star.new(seed)
                    end

        256.times do |t|
          seed_array[t] = seed_prng.rand(4_294_967_296)
        end
      end
      seed_array
    end

    def initialize(seed = true)
      @mm = []

      srand(seed)
    end

    def initialize_copy(from)
      @aa = from.aa
      @bb = from.bb
      @cc = from.cc
      @mm = from.mm.dup
      @randcnt = from.randcnt
      @randrsl = from.randrsl.dup

      self
    end

    # If seeded with an integer, use that to seed a standard Ruby Mersenne
    # Twister PRNG, and then use that to generate seed value for ISAAC. This is
    # mostly useful for producing repeated, deterministic results, which may be
    # needed for testing.
    def srand(seed = true)
      @randrsl = self.class.new_seed(seed)
      @seed = @randrsl.dup
      randinit(true)
      @seed
    end

    # Works just like the standard rand() function.  If called with an
    # integer argument, rand() will return positive random number in
    # the range of 0 to (argument - 1).  If called without an integer
    # argument, rand() returns a positive floating point number less than 1.
    # If called with a Range, returns a number that is in the range.

    def rand(arg = nil)
      if @randcnt == 1
        isaac
        @randcnt = 256
      end
      @randcnt -= 1
      if arg.nil?
        (@randrsl[@randcnt] / 536_870_912.0) % 1
      elsif arg.is_a?(Integer)
        @randrsl[@randcnt] % arg
      elsif arg.is_a?(Range)
        arg.min + @randrsl[@randcnt] % (arg.max - arg.min)
      else
        @randrsl[@randcnt] % arg.to_i
      end
    end

    def state
      @randrsl + [@randcnt]
    end

    def ==(other)
      state == other.state
    end

    def bytes(size)
      buffer = +''
      (size / 4).times do
        buffer << [rand(4_294_967_295)].pack('L').unpack('aaaa').join
      end

      if size % 4 != 0
        buffer << [rand(4_294_967_295)]
                  .pack('L')
                  .unpack('aaaa')[0..(size % 4 - 1)]
                  .join
      end

      buffer
    end

    # rubocop: disable Style/Semicolon
    def isaac
      i = 0

      @cc += 1
      @bb += @cc
      @bb &= 0xffffffff

      while i < 256
        x = @mm[i]
        @aa = (@mm[(i + 128) & 255] + (@aa ^ (@aa << 13))) & 0xffffffff
        @mm[i] = y = (@mm[(x >> 2) & 255] + @aa + @bb) & 0xffffffff
        @randrsl[i] = @bb = (@mm[(y >> 10) & 255] + x) & 0xffffffff
        i += 1

        x = @mm[i]
        @aa = (@mm[(i + 128) & 255] +
               (@aa ^ (0x03ffffff & (@aa >> 6)))) & 0xffffffff
        @mm[i] = y = (@mm[(x >> 2) & 255] + @aa + @bb) & 0xffffffff
        @randrsl[i] = @bb = (@mm[(y >> 10) & 255] + x) & 0xffffffff
        i += 1

        x = @mm[i]
        @aa = (@mm[(i + 128) & 255] + (@aa ^ (@aa << 2))) & 0xffffffff
        @mm[i] = y = (@mm[(x >> 2) & 255] + @aa + @bb) & 0xffffffff
        @randrsl[i] = @bb = (@mm[(y >> 10) & 255] + x) & 0xffffffff
        i += 1

        x = @mm[i]
        @aa = (@mm[(i + 128) & 255] +
               (@aa ^ (0x0000ffff & (@aa >> 16)))) & 0xffffffff
        @mm[i] = y = (@mm[(x >> 2) & 255] + @aa + @bb) & 0xffffffff
        @randrsl[i] = @bb = (@mm[(y >> 10) & 255] + x) & 0xffffffff
        i += 1
      end
    end

    def randinit(flag)
      i = 0
      @aa = @bb = @cc = 0
      a = b = c = d = e = f = g = h = 0x9e3779b9

      while i < 4
        a ^= b << 1; d += a; b += c
        b ^= 0x3fffffff & (c >> 2); e += b; c += d
        c ^= d << 8; f += c; d += e
        d ^= 0x0000ffff & (e >> 16); g += d; e += f
        e ^= f << 10; h += e; f += g
        f ^= 0x0fffffff & (g >> 4); a += f; g += h
        g ^= h << 8; b += g; h += a
        h ^= 0x007fffff & (a >> 9); c += h; a += b
        i += 1
      end

      i = 0
      while i < 256
        if flag
          a += @randrsl[i].to_i; b += @randrsl[i + 1].to_i;
          c += @randrsl[i + 2]; d += @randrsl[i + 3];
          e += @randrsl[i + 4]; f += @randrsl[i + 5];
          g += @randrsl[i + 6]; h += @randrsl[i + 7];
        end

        a ^= b << 11; d += a; b += c;
        b ^= 0x3fffffff & (c >> 2); e += b; c += d;
        c ^= d << 8;  f += c; d += e;
        d ^= 0x0000ffff & (e >> 16); g += d; e += f;
        e ^= f << 10; h += e; f += g;
        f ^= 0x0fffffff & (g >> 4); a += f; g += h;
        g ^= h << 8; b += g; h += a;
        h ^= 0x007fffff & (a >> 9); c += h; a += b;
        @mm[i] = a; @mm[i + 1] = b; @mm[i + 2] = c; @mm[i + 3] = d;
        @mm[i + 4] = e; @mm[i + 5] = f; @mm[i + 6] = g; @mm[i + 7] = h;
        i += 8
      end

      if flag
        i = 0
        while i < 256
          a += @mm[i]; b += @mm[i + 1]; c += @mm[i + 2]; d += @mm[i + 3];
          e += @mm[i + 4]; f += @mm[i + 5]; g += @mm[i + 6]; h += @mm[i + 7];
          a ^= b << 11; d += a; b += c;
          b ^= 0x3fffffff & (c >> 2); e += b; c += d;
          c ^= d << 8;  f += c; d += e;
          d ^= 0x0000ffff & (e >> 16); g += d; e += f;
          e ^= f << 10; h += e; f += g;
          f ^= 0x0fffffff & (g >> 4); a += f; g += h;
          g ^= h << 8; b += g; h += a;
          h ^= 0x007fffff & (a >> 9); c += h; a += b;
          @mm[i] = a; @mm[i + 1] = b; @mm[i + 2] = c; @mm[i + 3] = d;
          @mm[i + 4] = e; @mm[i + 5] = f; @mm[i + 6] = g; @mm[i + 7] = h;
          i += 8
        end
      end
      # rubocop: enable Style/Semicolon

      isaac
      @randcnt = 256 # /* prepare to use the first set of results */
    end

    DEFAULT = ISAAC.new
  end
end


require "bottleneck/constants"

module Bottleneck
  class Core
    # Create a Core object.
    #
    # @param [String] ip A name to uniquely identify this rate limit. For example, '127.0.0.1'
    #
    # @return [Core] Core instance
    def initialize(ip)
      @ip = ip.to_s
      @storage = Bottleneck.storage
      @limits = Bottleneck.config["limits"]
    end

    # Run main logic initialization, set and read Redis keys and validation requests count and time limit.
    #
    # @return [Hash] Hash with :status and :message keys
    def run
      client_ip = @ip
      key = "request_count:#{client_ip}"
      result = { status: Constants::SUCCESS_STATUS, message: Constants::OK_MESSAGE }
      requests_count = @storage.get(key)
      unless requests_count
        @storage.set(key, 0)
        @storage.expire(key, @limits["time_period_seconds"])
      end
      if requests_count.to_i >= @limits["max_requests_count"]
        result[:status] = Constants::EXPIRED_STATUS
        result[:message] = message(period(key))
      else
        @storage.incr(key)
      end
      result
    end

    private

    # Get remaining time in seconds for key.
    #
    # @param [String]   key A unique key in Redis store
    #
    # @return [Integer] Countdown in seconds
    def period(key)
      @storage.ttl(key)
    end

    # Return string message for 429 status
    #
    # @param [Integer]   secs Remaining seconds
    #
    # @return [String] Rate limit message with seconds
    def message(secs)
      "Rate limit exceeded. Try again in #{secs} seconds"
    end
  end
end


require 'action_view'

##
# The goal of Informant is to simplify your form code by encapsulating all
# aspects of a field (label, description, etc) in a single method call. What
# used to be written as:
#
#

#     <%= f.label :name %>

#     <%= f.text_field :name %>

#
Please use proper capitalization.

#

#
# can be written like this with Informant:
#
#   <%= f.text_field :name, :description => "Please use proper capitalization." %>
#
# The label is inferred from the field name or can be specified explicitly.
# The complete list of options:
#
# * <tt>:label</tt> - add a  tag for the field
# * <tt>:colon</tt> - if true, includes a colon at the end of the label
# * <tt>:description</tt> - explanatory text displayed underneath the field
# * <tt>:required</tt> - adds an asterisk if true
# * <tt>:decoration</tt> - arbitrary string which is appended to the field (often used for AJAX "spinner")
#
# Informant contains several form builders, each with the same syntax but
# different display:
#
# * <tt>Informant::Standard</tt> - displays fields in a

# * <tt>Informant::Table</tt> - displays fields in table rows
# * <tt>Informant::Simple</tt> - adds no containers at all
#
# Please see the per-method documentation for details. It's also easy to
# customize the display of any of the included builders. Just create a
# subclass and override the +default_field_template+ and
# +check_box_field_template+ methods.
#
module Informant

  ##
  # Displays fields in a
, label on one line, field below it.
  #
  class Standard < ::ActionView::Helpers::FormBuilder

    # Declare some options as custom (don't pass to built-in form helpers).
    @@custom_field_options = [:label, :required, :description, :decoration]
    @@custom_label_options = [:required, :colon, :label_for]

    @@custom_options = @@custom_field_options + @@custom_label_options

    # Run already-defined helpers through our "shell".
    helpers = field_helpers.map(&:to_s) +
      %w(select time_zone_select date_select) -
      %w(hidden_field fields_for label)
    helpers.each do |h|
      define_method h do |field, *args|
        options = args.detect { |a| a.is_a?(Hash) } || {}
        case h
        when 'check_box'
          template = "check_box_field"
          options[:colon] = false
        when 'radio_button'
          template = "radio_button_choice"
          options[:colon] = false
        else
          template = "default_field"
        end
        build_shell(field, options, template) { super(field, *args) }
      end
    end

    ##
    # Render a set of radio buttons. Takes a method name, an array of
    # choices (just like a +select+ field), and an Informant options hash.
    #
    def radio_buttons(method, choices, options = {})
      choices.map!{ |i| i.is_a?(Array) ? i : [i] }
      build_shell(method, options, "radio_buttons_field") do
        choices.map{ |c| radio_button method, c[1], :label => c[0],
          :label_for => [object_name, method, c[1].to_s.downcase].join('_') }
      end
    end

    ##
    # Render a set of check boxes for selecting HABTM-associated objects.
    # Takes a method name (eg, category_ids), an array of
    # choices (just like a +select+ field), and an Informant options hash.
    # In the default template the check boxes are enclosed in a
 with
    # CSS class <tt>habtm_check_boxes</tt> which can be styled thusly to
    # achieve a scrolling list:
    #
    #   .habtm_check_boxes {
    #     overflow: auto;
    #     height: 150px;
    #   }
    #
    # A hidden field is included which eliminates the need to handle the
    # no-boxes-checked case in the controller, for example:
    #
    #   <input type="hidden" name="article[categories][]" value="" />
    #
    # This ensures that un-checking all boxes will work as expected.
    # Unfortunately the check_box template is not applied to each check box
    # (because the standard method of querying the @object for the field's
    # value does not work--ie, there is no "categories[]" method).
    #
    def habtm_check_boxes(method, choices, options = {})
      choices.map!{ |i| i.is_a?(Array) ? i : [i] }
      base_id   = "#{object_name}_#{method}"
      base_name = "#{object_name}[#{method}]"

      @template.hidden_field_tag(
        "#{base_name}[]", "", :id => "#{base_id}_empty") +
      build_shell(method, options, "habtm_check_boxes_field") do
        choices.map do |c|
          field_id = "#{base_id}_#{c[1].to_s.downcase}"
          habtm_single_check_box_template(
            :name => "#{base_name}[]",
            :id => field_id,
            :value => c[1],
            :checked => @object.send(method).include?(c[1]),
            :label => @template.label_tag(field_id, c[0])
          )
        end
      end
    end

    ##
    # Standard Rails date selector.
    #
    def date_select(method, options = {})
      options[:include_blank] ||= false
      options[:start_year]    ||= 1801
      options[:end_year]      ||= Time.now.year
      options[:label_for]       = "#{object_name}_#{method}_1i"
      build_shell(method, options) { super }
    end

    ##
    # This differs from the Rails-default date_select in that it
    # submits three distinct fields for storage in three separate attributes.
    # This allows for partial dates (eg, "1984" or "October 1984").
    # See {FlexDate}[http://github.com/alexreisner/flex_date] for
    # storing and manipulating partial dates.
    #
    def multipart_date_select(method, options = {})
      options[:include_blank] ||= false
      options[:start_year]    ||= 1801
      options[:end_year]      ||= Time.now.year
      options[:prefix]          = object_name # for date helpers
      options[:label_for]       = "#{object_name}_#{method}_y"
      build_shell(method, options) do
        [['y', 'year'], ['m', 'month'], ['d', 'day']].map{ |p|
          i,j = p
          value = @object.send(method.to_s + '_' + i)
          options[:field_name] = method.to_s + '_' + i
          eval("@template.select_#{j}(#{value.inspect}, options)")
        }.join(' ')
      end
    end

    ##
    # Year select field. Takes options <tt>:start_year</tt> and
    # <tt>:end_year</tt>, and <tt>:step</tt>.
    #
    def year_select(method, options = {})
      options[:first] = options[:start_year] || 1801
      options[:last]  = options[:end_year] || Date.today.year
      integer_select(method, options)
    end

    ##
    # Integer select field.
    # Takes options <tt>:first</tt>, <tt>:last</tt>, and <tt>:step</tt>.
    #
    def integer_select(method, options = {})
      options[:step] ||= 1
      choices = []; i = 0
      (options[:first]..options[:last]).each do |n|
        choices << n if i % options[:step] == 0
        i += 1
      end
      select method, choices, options
    end

    ##
    # Submit button with smart default text (if +value+ is nil uses "Create"
    # for new record or "Update" for old record).
    #
    def submit(value = nil, options = {})
      value = (@object.new_record?? "Create" : "Update") if value.nil?
      build_shell(value, options, 'submit_button') { super }
    end

    ##
    # Render a field label.
    #
    def label(method, text = nil, options = {})
      colon = false if options[:colon].nil?
      options[:for] = options[:label_for]
      required = options[:required]

      # remove special options
      options.delete :colon
      options.delete :label_for
      options.delete :required

      text = @template.send(:h, text.blank?? method.to_s.humanize : text.to_s)
      text << ':'.html_safe if colon
      text << @template.content_tag(:span, "*", :class => "required") if required
      super
    end

    ##
    # Render a field set (HTML <fieldset>). Takes the legend (optional), an
    # options hash, and a block in which fields are rendered.
    #
    def field_set(legend = nil, options = nil, &block)
      @template.content_tag(:fieldset, options) do
        (legend.blank?? "" : @template.content_tag(:legend, legend)) +
        @template.capture(&block)
      end
    end

    private # ---------------------------------------------------------------

    ##
    # Insert a field into its HTML "shell".
    #
    def build_shell(method, options, template = 'default_field') #:nodoc:

      # Build new options hash for custom label options.
      label_options = options.reject{ |i,j| !@@custom_label_options.include? i }

      # Build new options hash for custom field options.
      field_options = options.reject{ |i,j| !@@custom_field_options.include? i }

      # Remove custom options from options hash so things like
      # <tt>include_blank</tt> aren't added as HTML attributes.
      options.reject!{ |i,j| @@custom_options.include? i }

      locals = {
        :element     => yield,
        :label       => label(method, field_options[:label], label_options),
        :description => field_options[:description],
        :div_id      => "#{@object_name}_#{method}_field",
        :required    => field_options[:required],
        :decoration  => field_options[:decoration] || nil
      }
      send("#{template}_template", locals).html_safe
    end

    ##
    # Render default field template.
    #
    def default_field_template(l = {})
      <<-END

        #{l[:label]}

        #{l[:element]}#{l[:decoration]}
        #{"
#{l[:description]}
" unless l[:description].blank?}

      END
    end

    ##
    # Render check box field template.
    #
    def check_box_field_template(l = {})
      <<-END

        #{l[:element]} #{l[:label]} #{l[:decoration]}

        #{"
#{l[:description]}
" unless l[:description].blank?}

      END
    end

    ##
    # Render single radio button. Note that this is the only field
    # template without an enclosing <tt>
</tt> because it
    # is intended for use only within the radio_buttons_template (plural).
    #
    def radio_button_choice_template(l = {})
      <<-END
      #{l[:element]} #{l[:label]}
      END
    end

    ##
    # Render a group of radio buttons.
    #
    def radio_buttons_field_template(l = {})
      default_field_template(l)
    end

    ##
    # Render a group of HABTM check boxes.
    #
    def habtm_check_boxes_field_template(l = {})
      <<-END

        #{l[:label]}

#{l[:element].join}
#{l[:decoration]}
        #{"
#{l[:description]}
" unless l[:description].blank?}

      END
    end

    ##
    # Render a single check box in a HABTM set.
    #
    def habtm_single_check_box_template(l = {})
      <<-END

<input type="checkbox" name="#{l[:name]}" id="#{l[:id]}" value="#{l[:value]}" #{'checked="checked"' if l[:checked]}} /> #{l[:label]}

      END
    end

    ##
    # Render submit button template.
    #
    def submit_button_template(l = {})
      <<-END

#{l[:element]}

      END
    end
  end

  ##
  # Displays fields with no surrounding HTML containers.
  #
  class Simple < Standard

    private # ---------------------------------------------------------------

    ##
    # Render default field template.
    #
    def default_field_template(l = {})
      "#{l[:element]}#{l[:decoration]}"
    end

    ##
    # Render check box field template.
    #
    def check_box_field_template(l = {})
      "#{l[:element]} #{l[:label]} #{l[:decoration]}"
    end

    ##
    # Render submit button template.
    #
    def submit_button_template(l = {})
      l[:element]
    end
  end

  ##
  # Displays fields in table rows: label in first column, field (with
  # description and decoration) in second.
  #
  class Table < Standard

    private # ---------------------------------------------------------------

    ##
    # Render default field template.
    #
    def default_field_template(l = {})
      <<-END

        #{l[:label]}
        #{l[:element]}#{l[:decoration]}
        #{"
#{l[:description]}
" unless l[:description].blank?}

      END
    end

    ##
    # Render check box field template.
    #
    def check_box_field_template(l = {})
      default_field_template(l)
    end

    ##
    # Render submit button template.
    #
    def submit_button_template(l = {})
      <<-END

        #{l[:element]}

      END
    end
  end
end


require 'rstruct/base_types/type'

module Rstruct
  class PackError < StandardError
  end

  class ReadError < StandardError
  end

  module Packable
    private
      # sets up a callback for packing a value to raw data
      # for this type
      def on_pack(&block)
        @pack_cb = block
      end

      # sets up a callback for unpacking data from a string for
      # this type
      def on_unpack(&block)
        @unpack_cb = block
      end

    public
      # Called when parsing. While you can override this in subclasses,
      # in general it is probably better to use the 'on_unpack' method
      # to define a proc to handle unpacking for special cases.
      def read(raw, predecessors=nil)
        if raw.respond_to?(:read)
          raw = raw.read(self.sizeof())
        end
        if raw.size < self.sizeof()
          raise(ReadError, "Expected #{self.sizeof} bytes, but only got #{raw.size} bytes")
        end

        vals =
          if @unpack_cb
            @unpack_cb.call(raw, predecessors)
          else
            raw.unpack(self.format)
          end
        return(self.claim_value(vals, predecessors))
      end

      # Called when composing raw data. While you can override this in
      # subclasses, in general it is probably better to use the 'on_pack'
      # method to define a proc to handle packing for special cases.
      def pack_value(val, obj=nil)
        begin
          if @pack_cb
            @pack_cb.call(val, obj)
          else
            varray = val.is_a?(Array) ? val : [val]
            varray.pack(self.format)
          end
        rescue => e
          raise(PackError, "Error packing #{val.inspect} as type #{self.name.inspect} -- #{e.class} -> #{e}")
        end
      end
  end

  class PackedType < Type
    include Packable
    attr_reader :sizeof, :format

    def initialize(name, size, format, opts={}, &block)
      @sizeof = size
      @format = format
      @groupable = true
      super(name, opts, &block)
    end

    def instance(val=nil)
      val
    end
  end
end


require 'stringio'
require 'rstruct/registry'
require 'rstruct/types'
require 'rstruct/structure'
require 'rstruct/struct_builder'

module Rstruct
  module ClassMethods

    # Take the following C struct for example:
    #
    #   struct mach_header {
    #     uint32_t      magic;      /* mach magic number identifier */
    #     cpu_type_t    cputype;    /* cpu specifier */
    #     cpu_subtype_t cpusubtype; /* machine specifier */
    #     uint32_t      filetype;   /* type of file */
    #     uint32_t      ncmds;      /* number of load commands */
    #     uint32_t      sizeofcmds; /* the size of all the load commands */
    #     uint32_t      flags;      /* flags */
    #   }
    #
    # Which might be defined with rstruct as follows:
    #
    #   extend(Rstruct::ClassMethods)
    #
    #   typedef :uint32_t, :cpu_type_t
    #   typedef :uint32_t, :cpu_subtype_t
    #
    #   struct(:mach_header) {
    #     uint32_t      :magic      # mach magic number identifier
    #     cpu_type_t    :cputype    # cpu specifier
    #     cpu_subtype_t :cpusubtype # machine specifier
    #     uint32_t      :filetype   # type of file
    #     uint32_t      :ncmds      # number of load commands
    #     uint32_t      :sizeofcmds # the size of all the load commands
    #     uint32_t      :flags      # flags
    #   }
    #
    def struct(name, opts={},&block)
      Rstruct::Structure.new(name, opts, &block)
    end

    def typedef(p, t, opts={})
      reg = opts[:register] || default_registry
      reg.typedef(p,t,opts)
    end

    def sizeof(typ, reg=nil)
      reg ||= default_registry
      if t=reg[typ]
        t.sizeof
      else
        raise(InvalidTypeError, "unknown type: #{typ}")
      end
    end

    # Returns the default Rstruct registry
    def default_registry
      Registry::DEFAULT_REGISTRY
    end

    def get_type(typ, reg=Registry::DEFAULT_REGISTRY)
      reg[typ]
    end

  end

  extend(ClassMethods)
end


require 'ires/core'
require 'ires/service'
require 'ires/mode'
require 'ires/type'
require 'action_view/helpers'

module Ires
  module ViewHelper
    # Image resize
    # @return [image_tag]
    def ires_tag(path, width: nil, height: nil, type: Type::ALL, mode: Mode::RESIZE, expire: 30.days, **option)
      image_path = Ires::Service.path(
        path,
        width: width || 0,
        height: height || 0,
        mode: mode,
        type: type,
        expire: expire
      )

      # Set image_tag
      image_tag(image_path, option)
    end
  end
end


# ==========================================================================
# Project:   Lebowski Framework - The SproutCore Test Automation Framework
# License:   Licensed under MIT license (see License.txt)
# ==========================================================================

module RSpec
  module Matchers

    #
    # Replacing RSpec's default method_missing implementation so that we can
    # include our own special default hooks that allows spec tests to look
    # more readable.
    #
    # Ideally it would have been better if RSpec provided some nice hooks to
    # try other default pattern matchers
    #
    def method_missing(sym, *args, &block)
      #
      # Note: Be sure that the symbol does not contain the word "test". test
      # is a private method on Ruby objects and will cause the Be and Has
      # matches to fail.
      #
      return Lebowski::RSpec::Matchers::Be.new(sym, *args) if sym.to_s =~ /^be_/
      return Lebowski::RSpec::Matchers::Has.new(sym, *args) if sym.to_s =~ /^have_/
      return Lebowski::RSpec::Operators::That.new(sym, *args) if sym.to_s =~ /^that_/
      super
    end
  end
end


# Required libs
require "jekyll"
require "yaml"
require "benchmark"
require "find"
require "fileutils"
require "open-uri"

module Jektify

  # Instance class
  ENGINE = Engine.new
  RENDER = Render.new

  # Variables global
  APP_NAME = "jektify"
  YML_LOAD = ENGINE.yml_config('_config.yml')
  APP_ROOT_CONFIG = YML_LOAD["#{APP_NAME}"]

  class Generator < Jekyll::Generator

    def generate(site)
      # Before, verify structure Jektify in _config.yml
      ENGINE.yml_verify(APP_ROOT_CONFIG)

      @site = site

      # It will only generate the Javascript file for the user, if the 'Toggle' feature is enabled in the file "_config.yml".
      if APP_ROOT_CONFIG["enable"] == true
        @site.static_files.concat static_files if APP_ROOT_CONFIG["toggle"].nil? || APP_ROOT_CONFIG["toggle"]["enable"] == true
      end
      ENGINE.copy_sass_manual(APP_ROOT_CONFIG)
    end

    # This method searches for folders and files in the assets root directory.
    # After searching all files stores in an array and then copying to
    # the folder "_site".
    def static_files
      source = File.dirname(ENGINE.assets_path)
      asset_files.map do |file|
        dir = File.dirname(file)
        file_name = File.basename(file)
        Jekyll::StaticFile.new @site, source, dir, file_name
      end
    end

    # Get paths and fils directory 'assets'
    def asset_files
      asset_files = []
      Find.find(ENGINE.assets_path).each do |path|
        next if File.directory?(path)
        next if path.include?(ENGINE.stylesheets_sass_path)
        asset_files << path.sub(ENGINE.assets_path, 'assets')
      end
      asset_files
    end

  end # Generator

  # class load SASS
  class << self

    def loadSass
      ENGINE.configure_sass(ENGINE)
    end

  end # self

  # Implement Jektify in Page
  class Main < Liquid::Tag

    def initialize(tag_name, input, tokens)
      super
      # Name tag in liquid
      # @tag_name = tag_name # DEPRECATED
      @input = input
    end

    def render(context)
      # Sets the data splitter
      input_split = ENGINE.split_params(@input)

      # Determines the positions of each data with the divisor
      spotify_user = input_split[0].strip
      spotify_embed_category = input_split[1].strip
      spotify_id = input_split[2].strip
      spotify_embed_theme = input_split[3].strip

      # Transpiler variable "theme"
      spotify_embed_theme == "light" ? spotify_embed_theme = "white" : spotify_embed_theme = input_split[3].strip

      # Checks a series of error values in the settings of Jektify in sctructure {% spotify [user]/[type]/[id]/[theme] %}
      ENGINE.error_different_string(spotify_embed_theme, "dark", "white", "[x] Error: Parameter incorrect in {% spotify [user]/[type]/[id]/[theme] %}.")

      # Customizing Url Spotify
      # E.g: { % spotify [user]/[type]/[id]/[theme] % }
      spotify_embed_url = "https://embed.spotify.com/?uri=spotify:#{spotify_embed_category}:#{spotify_id}&theme=#{spotify_embed_theme}"
      spotify_embed_url = "https://embed.spotify.com/?uri=spotify:user:#{spotify_user}:#{spotify_embed_category}:#{spotify_id}&theme=#{spotify_embed_theme}" unless spotify_embed_category != "playlist"

      # Checks a series of error values in the settings of Jektify in _config.yml
      ENGINE.error_different_true_false(APP_ROOT_CONFIG["enable"], "[x] Error: The property 'spotify => enable' in file '_config.yml' does not exist or its value is incorrect. Use: [ true | false ]") unless APP_ROOT_CONFIG["enable"].nil?

      ENGINE.error_different_true_false(APP_ROOT_CONFIG["open"], "[x] Error: The property 'spotify => open' in file '_config.yml' does not exist or its value is incorrect. Use: [ true | false ]") unless APP_ROOT_CONFIG["open"].nil?

      ENGINE.error_different_true_false(APP_ROOT_CONFIG["title"]["enable"], "[x] Error: The property 'spotify => title => enable' in file '_config.yml' does not exist or its value is incorrect. Use: [ true | false ]") unless APP_ROOT_CONFIG["title"].nil?

      ENGINE.error_different_true_false(APP_ROOT_CONFIG["description"]["enable"], "[x] Error: The property 'spotify => description => enable' in file '_config.yml' does not exist or its value is incorrect. Use: [ true | false ]") unless APP_ROOT_CONFIG["description"].nil?

      ENGINE.error_different_true_false(APP_ROOT_CONFIG["toggle"]["enable"], "[x] Error: The property 'spotify => toggle => enable' in file '_config.yml' does not exist or its value is incorrect. Use: [ true | false ]") unless APP_ROOT_CONFIG["toggle"].nil?

      # If there are no errors starting the plugin
      RENDER.rendering(APP_NAME, APP_ROOT_CONFIG, spotify_embed_category, spotify_embed_url, spotify_embed_theme)

    end # def render

  end # class Main

  # Load files: (SASS)
  Jektify.loadSass

  # Register plugin (tag)
  Liquid::Template.register_tag(APP_NAME, Jektify::Main)

end # module Jektify


require 'net/https'
require 'uri'
require 'builder'
require 'rexml/document'
require 'date'

module Paypal
  class Report
    API_URL = 'https://payments-reports.paypal.com/reportingengine'

    def initialize(user, password, vendor, partner = 'PayPalUK')
      @user, @password, @vendor, @partner = user, password, vendor, partner
    end

    #high level functions
    def daily(time = Date.today, page_size = 50)
      time      = time.strftime("%Y-%m-%d") unless time.is_a?(String)
      report_id = run_report_request('DailyActivityReport', {'report_date' => time}, page_size)

      meta_data = get_meta_data_request(report_id)

      data = []
      meta_data["numberOfPages"].to_i.times do |page_num|
        data += get_data_request(report_id, page_num + 1) #it's zero indexed
      end
      data
    end

    def monthly(start_date, end_date = Date.today, page_size = 50)
      start_date = Date.parse(start_date) if start_date.is_a?(String)
      start_date = start_date.to_date if start_date.is_a?(Time)

      end_date = Date.parse(end_date) if end_date.is_a?(String)
      end_date = end_date.to_date if end_date.is_a?(Time)

      data = []
      while(start_date <= end_date)
        data += daily(start_date, page_size)
        start_date = start_date.next_day
      end
      data
    end

    def transaction_summary(start_date, end_date = Time.now, page_size = 50)
      start_date = start_date.strftime("%Y-%m-%d 00:00:00") unless start_date.is_a?(String)
      end_date   = end_date.strftime("%Y-%m-%d 23:59:59") unless end_date.is_a?(String)
      report_id  = run_report_request('TransactionSummaryReport', {'start_date' => start_date, 'end_date' => end_date}, page_size)

      meta_data = get_meta_data_request(report_id)

      data = []
      meta_data["numberOfPages"].to_i.times do |page_num|
        data += get_data_request(report_id, page_num + 1) #it's zero indexed
      end
      data
    end

    #low level functions
    def run_report_request(report_name, report_params = {}, page_size = 50)
      response = request 'runReportRequest' do |xml|
        xml.reportName report_name
        report_params.each do |name, value|
          xml.reportParam do
            xml.paramName name
            xml.paramValue value
          end
        end
        xml.pageSize page_size
      end

      response.elements["runReportResponse/reportId"].get_text.value
    end

    def get_meta_data_request(report_id)
      response = request 'getMetaDataRequest' do |xml|
        xml.reportId report_id
      end

      response.elements["getMetaDataResponse"].inject({}) do |h,e|
        if text = e.get_text
          h[e.name] = text.value
        else
          h[e.name] ||= []
          h[e.name] << e.elements["dataName"].get_text.value
        end
        h
      end
    end

    def get_data_request(report_id, page_num)
      response = request 'getDataRequest' do |xml|
        xml.reportId report_id
        xml.pageNum page_num
      end

      [].tap do |result|
        response.elements.each("getDataResponse/reportDataRow") do |data_row|
          result << [].tap do |row|
            data_row.elements.each("columnData/data") do |data|
              row << if text = data.get_text
                text.value
              else
                nil
              end
            end
          end
        end
      end
    end

    private
    def uri
      @uri ||= URI.parse(API_URL)
    end

    def http
      @http ||= Net::HTTP.new(uri.host, uri.port).tap do |http|
        http.use_ssl     = true
        http.verify_mode = OpenSSL::SSL::VERIFY_NONE
      end
    end

    def request(type)
      xml = Builder::XmlMarkup.new
      xml.tag! 'reportingEngineRequest' do
        xml.tag! 'authRequest' do
          xml.user @user
          xml.vendor @vendor
          xml.partner @partner
          xml.password @password
        end
        xml.tag! type do
          yield xml
        end
      end

      body = http.post(uri.request_uri, xml.target!).body
      REXML::Document.new(body).elements['reportingEngineResponse'].tap do |response|
        code = response.elements['baseResponse/responseCode'].get_text.value.to_i
        raise response.elements['baseResponse/responseMsg'].get_text.value unless code == 100
      end
    end

  end
end


require "unique_generator/version"
require "active_support/concern"

module UniqueGenerator
  extend ActiveSupport::Concern

  def self.setup!
    ActiveRecord::Base.send(:include, self) if defined?(ActiveRecord::Base)
    MongoMapper::Document.plugin(self)      if defined?(MongoMapper::Document)
  end

  CHOICES = (Array('A'..'Z') + Array('a'..'z') + Array('0'..'9'))

  included do
    if defined?(ActiveRecord::Base) && self <= ActiveRecord::Base
      include UniqueChecks::AR
    elsif defined?(MongoMapper::Document) && self <= MongoMapper::Plugins::Document
      include UniqueChecks::MM
    end
  end

  module UniqueChecks

    module MM
      # Returns whether or not the given field is unique to every item.
      def unique_field?(field_name)
        scope = self.class.where(field_name => send(field_name))
        scope = scope.where(:_id.ne => id) if id.present?
        scope.empty?
      end
    end

    module AR
      # Returns whether or not the given field is unique to every item.
      def unique_field?(field_name)
        scope = self.class.where(field_name => send(field_name))
        # When we have an id, check for those with a different id.
        if id.present?
          conditions = self.class.arel_table[:id].eq(id).not
          scope = scope.where(conditions)
        end
        scope.empty?
      end
    end

  end

  module ClassMethods

    # Generate a unique field
    def generate_unique(length = 32, &blk)
      unique = generate_random(length)
      unique = generate_random(length) until blk.call(unique)
      unique
    end

    # Generate a random string with the given number of characters.
    def generate_random(length = 32)
      (1..length).inject("") { |token, _| token << CHOICES.sample }
    end

  end

  # Generate unique field.
  def generate_unique_field!(name, size = 32)
    return if send(name).present?
    self.class.generate_unique(size) do |token|
      send "#{name}=", token
      unique_field? name
    end
  end

  setup!

  if defined?(Rails::Railtie)
    class Railtie < Rails::Railtie
      initializer "unique_generator.initialize" do
        UniqueGenerator.setup!
      end
    end
  end

end


# frozen_string_literal: true

require 'prawn'

# Main class for creating and rendering PDFs
module Paperback; class Document
  attr_reader :pdf, :debug

  def initialize(debug: false)
    log.debug('Document#initialize')
    @debug = debug
    @pdf = Prawn::Document.new
  end

  def log
    @log ||= Paperback.class_log(self.class)
  end

  def render(output_file:, draw_opts:)
    log.info('Rendering PDF')

    # Create all the PDF content
    draw_paperback(**draw_opts)

    # Render to output file
    log.info("Writing PDF to #{output_file.inspect}")
    pdf.render_file(output_file)
  end

  # High level method to draw the paperback content on the pdf document
  #
  # @param qr_code
  # @param sixword_lines
  # @param sixword_bytes
  # @param labels
  # @param passphrase_sha
  # @param [Integer, nil] passphrase_len Length of the passphrase used to
  #   encrypt the original content. If this is not provided, then assume the
  #   original content was not encrypted and skip adding gpg -d instructions.
  # @param [Integer] sixword_font_size The font size to use for Sixword text
  # @param [String,nil] base64_content If provided, then append the original
  #   content (possibly encrypted) encoded using Base64.
  # @param [Integer, nil] base64_bytes The length of the original content
  #   before encoding to base64. This is used for the informational header.
  def draw_paperback(qr_code:, sixword_lines:, sixword_bytes:, labels:,
                     passphrase_sha: nil, passphrase_len: nil,
                     sixword_font_size: nil, base64_content: nil,
                     base64_bytes: nil)
    unless qr_code.is_a?(RQRCode::QRCode)
      raise ArgumentError.new('qr_code must be RQRCode::QRCode')
    end

    # Header & QR code page
    pdf.font('Times-Roman')

    debug_draw_axes

    draw_header(labels: labels, passphrase_sha: passphrase_sha,
                passphrase_len: passphrase_len)

    add_newline

    draw_qr_code(qr_modules: qr_code.modules)

    pdf.stroke_color '000000'
    pdf.fill_color '000000'

    # Sixword page

    pdf.start_new_page

    draw_sixword(lines: sixword_lines, sixword_bytes: sixword_bytes,
                 font_size: sixword_font_size,
                 is_encrypted: passphrase_len)

    if base64_content
      draw_base64(b64_content: base64_content, b64_bytes: base64_bytes,
                  is_encrypted: passphrase_len)
    end

    pdf.number_pages('<page> of <total>', align: :right,
                     at: [pdf.bounds.right - 100, -2])
  end

  # If in debug mode, draw axes on the page to assist with layout
  def debug_draw_axes
    return unless debug
    pdf.float { pdf.stroke_axis }
  end

  # Move cursor down by one line
  def add_newline
    pdf.move_down(pdf.font_size)
  end

  def draw_header(labels:, passphrase_sha:, passphrase_len:,
                  repo_url: 'https://github.com/ab/paperback')

    intro = [
      "This is a paper backup produced by `paperback`. ",
      "<u><link href='#{repo_url}'>#{repo_url}</link></u>",
    ].join
    pdf.text(intro, inline_format: true)
    add_newline

    label_pad = labels.keys.map(&:length).max + 1

    unless passphrase_sha && passphrase_len
      labels['Encrypted'] = 'no'
    end

    pdf.font('Courier') do
      labels.each_pair do |k, v|
        pdf.text("#{(k + ':').ljust(label_pad)} #{v}")
      end

      if passphrase_sha
        pdf.text("SHA256(passphrase)[0...16]: #{passphrase_sha}")
      end
    end

    add_newline

    if passphrase_len
      pdf.font('Helvetica') do
        pdf.font_size(12.8) do
          pdf.text('Passphrase: ' + '_ ' * passphrase_len)
        end
      end

      pdf.move_down(8)
      pdf.indent(72) do
        pdf.text('Be sure to cover the passphrase when scanning the QR code!' +
                 ' Decrypt with `gpg -d`.')
      end
    end
  end

  # @param [Array<String>] lines An array of sixword sentences to print
  # @param [Integer] columns The number of text columns on the page
  # @param [Integer] hunks_per_row The number of 6-word sentences per line
  # @param [Integer] sixword_bytes Bytesize of the sixword encoded data
  def draw_sixword(lines:, sixword_bytes:, columns: 3, hunks_per_row: 1,
                   font_size: nil, is_encrypted: true)
    font_size ||= 11

    debug_draw_axes

    numbered = lines.each_slice(hunks_per_row).each_with_index.map { |row, i|
      "#{i * hunks_per_row + 1}: #{row.map(&:strip).join('. ')}"
    }

    header = [
      "This sixword text encodes #{sixword_bytes} bytes in #{lines.length}",
      ' six-word sentences.',
      ' Decode with `sixword -d`',
      (is_encrypted ? ', then `gpg -d`.' : '.')
    ].join

    pdf.font('Times-Roman') do
      pdf.text(header)
      add_newline
    end

    pdf.column_box([0, pdf.cursor], columns: columns, width: pdf.bounds.width) do
      pdf.font('Times-Roman') do
        pdf.font_size(font_size) do
          pdf.text(numbered.join("\n"))
        end
      end
    end
  end

  def draw_qr_code(qr_modules:)
    qr_height = pdf.cursor # entire rest of page
    qr_width = pdf.bounds.width # entire page width

    # number of modules plus 2 for quiet area
    qr_code_size = qr_modules.length + 2

    pixel_height = qr_height / qr_code_size
    pixel_width = qr_width / qr_code_size

    pdf.bounding_box([0, pdf.cursor], width: qr_width, height: qr_height) do
      if debug
        pdf.stroke_color('888888')
        pdf.stroke_bounds
      end

      qr_modules.each do |row|
        pdf.move_down(pixel_height)

        row.each_with_index do |pixel_val, col_i|
          pdf.stroke do
            pdf.stroke_color(pixel_val ? '000000' : 'ffffff')
            pdf.fill_color(pixel_val ? '000000' : 'ffffff')
            xy = [(col_i + 1) * pixel_width, pdf.cursor]
            pdf.fill_rectangle(xy, pixel_width, pixel_height)
          end
        end
      end
    end
  end

  # @param [String] b64_content
  def draw_base64(b64_content:, b64_bytes:, font_size: nil, is_encrypted: true)
    font_size ||= 11

    debug_draw_axes

    if is_encrypted
      header = [
        "This PGP text encodes #{b64_bytes} bytes in #{b64_content.length}",
        " characters. Decode with `gpg -d`."
      ].join
    else
      header = [
        "This base64 text encodes #{b64_bytes} bytes in #{b64_content.length}",
        " characters. Decode with `base64 --decode`."
      ].join
    end

    add_newline
    add_newline
    pdf.text(header)
    add_newline

    pdf.font('Courier') do
      pdf.text(b64_content)
    end

  end
end; end


module I18nCountryTranslations

  # Generates patterns for locale files, bases on a list of supported locales
  class LocaleFilesPatternGenerator

    attr_reader :base_pattern, :extension

    def initialize(base_pattern, extension = '.yml')
      @base_pattern = base_pattern
      @extension = extension
    end

    # Generates a glob file pattern for the specified list of locales (i.e. IETF language tags)
    def pattern_from(locales)
      locales = Array(locales || [])
      locales = locales.map { |locale| subpatterns_from locale }.flatten
      pattern = locales.blank? ? '*' : "{#{locales.join ','}}"
      "#{base_pattern}#{pattern}#{extension}"
    end

    protected

    # Generates subpatterns for the specified locale (i.e. IETF language tag).
    # Subpatterns are all more generic variations of a locale.
    # E.g. subpatterns for en-US are en-US and en. Subpatterns for az-Latn-IR are az-Latn-IR, az-Latn and az
    def subpatterns_from(locale)
      parts = locale.to_s.split('-')
      parts.map.with_index { |part,index| parts[0..index].join('-') }
    end

  end
end


module Danger
  # Validates the code coverage of the files changed within a Pull Request and
  # generates a brief coverage report.
  #
  # @example Validating code coverage for EasyPeasy (easy-peasy.io)
  #
  #    # Checks the coverage for the EasyPeasy scheme within the specified
  #    # workspace, ignoring the target 'Demo.app' and setting a minimum
  #    # coverage percentage of 90%.
  #
  #    # The result is sent to the pull request with a markdown format and
  #    # notifies failure if the minimum coverage threshold is not reached.
  #
  #    xcov.report(
  #      scheme: 'EasyPeasy',
  #      workspace: 'Example/EasyPeasy.xcworkspace',
  #      exclude_targets: 'Demo.app',
  #      minimum_coverage_percentage: 90
  #    )
  #
  # @tags xcode, coverage, xccoverage, tests, ios, xcov
  # @see nakiostudio/danger-xcov
  #
  class DangerXcov < Plugin
    # Validates the code coverage of the files changed within a Pull Request.
    # This method accepts the same arguments allowed by the xcov gem.
    #
    # @param   args Hash{Symbol => String}
    #          This method accepts the same arguments accepted by the xcov gem.
    #          A complete list of parameters allowed is available here:
    #          https://github.com/nakiostudio/xcov
    # @return  [void]
    #
    def report(*args)
      # Run xcov to produce a processed report
      report = produce_report(*args)
      # Output the processed report
      output_report(report)
    end

    # Produces and processes a report for use in the report method
    # It takes the same arguments as report, and returns the same
    # object as process_report
    def produce_report(*args)
      # Check xcov availability, install it if needed
      `gem install xcov` unless xcov_available?
      unless xcov_available?
        puts "xcov is not available on this machine"
        return
      end

      require "xcov"
      require "fastlane_core"

      # Init Xcov
      config = FastlaneCore::Configuration.create(Xcov::Options.available_options, convert_options(args.first))
      Xcov.config = config
      Xcov.ignore_handler = Xcov::IgnoreHandler.new

      # Init project
      manager =  Xcov::Manager.new(config)

      # Parse .xccoverage
      report_json = manager.parse_xccoverage

      # Map and process report
      process_report(Xcov::Report.map(report_json))
    end

    # Outputs a processed report with Danger
    def output_report(report)
      # Create markdown
      report_markdown = report.markdown_value

      # Send markdown
      markdown(report_markdown)

      # Notify failure if minimum coverage hasn't been reached
      threshold = Xcov.config[:minimum_coverage_percentage].to_i
      if !threshold.nil? && (report.coverage * 100) < threshold
        fail("Code coverage under minimum of #{threshold}%")
      end
    end

    # Aux methods

    # Checks whether xcov is available
    def xcov_available?
      `which xcov`.split("/").count > 1
    end

    # Filters the files that haven't been modified in the current PR
    def process_report(report)
      file_names = @dangerfile.git.modified_files.map { |file| File.basename(file) }
      file_names += @dangerfile.git.added_files.map { |file| File.basename(file) }
      report.targets.each do |target|
        target.files = target.files.select { |file| file_names.include?(file.name) }
      end

      report
    end

    # Processes the parameters passed to the plugin
    def convert_options(options)
      converted_options = options.dup
      converted_options.delete(:verbose)
      converted_options
    end

    private :xcov_available?, :process_report

  end
end


require 'digest/crc16_ccitt'

module Digest
  #
  # Implements the CRC16_CCITT algorithm used in QT algorithms.
  #
  # @author Matthew Bednarski
  #
  class CRC16QT < CRC16CCITT

    FINAL_XOR = 0xffff

    REVERSE_CRC_RESULT = true

    REVERSE_DATA = true

    # Updates the CRC16 checksum.
    #
    # @param [String] data
    #   The data to update the checksum with.
    #
    def update(data)
      data.each_byte do |b|
        b = revert_byte(b) if REVERSE_DATA
        @crc = ((@table[((@crc >> 8) ^ b) & 0xff] ^ (@crc << 8)) & 0xffff)
      end

      return self
    end

    def checksum
      crc = @crc + 0
      crc ^= FINAL_XOR       if FINAL_XOR
      crc = revert_bits(crc) if REVERSE_CRC_RESULT
      return crc
    end

    protected

    def revert_bits(cc)
      ob = 0
      b  = (1 << 15)

      16.times do |t|
        ob |= (1 << t) if (cc & b) != 0
        b >>= 1
      end

      return ob
    end

    def revert_byte(cc)
      ob = 0
      b  = (1 << 7)

      8.times do |t|
        ob |= (1 << t) if (cc & b) != 0
        b >>= 1
      end

      return ob
    end

  end
end


module XingApi
  class Client
    include XingApi::ResponseHandler

    OAUTH_ATTRIBUTES = [:consumer_key, :consumer_secret, :oauth_token, :oauth_token_secret].freeze
    attr_writer(*OAUTH_ATTRIBUTES)
    attr_accessor :request_token_hash

    class << self
      attr_accessor :default_options

      def configure
        instance = new
        yield instance
        self.default_options = instance.send(:to_hash)
      end
    end # class << self

    def initialize(options = {})
      options = (self.class.default_options ||= {}).merge(options)
      OAUTH_ATTRIBUTES.each do |attribute|
        send "#{attribute}=", options[attribute]
      end
    end

    def request(http_verb, url, options = {})
      full_url = url + hash_to_params(options)
      handle(access_token.request(http_verb, full_url))
    end

    def request_with_body(http_verb, url, body_hash = {})
      handle(access_token.request(http_verb, url, body_hash.to_json, 'Content-Type' => 'application/json'))
    end

    def get_request_token(oauth_callback = 'oob')
      ensure_attributes_are_set! %w(consumer_key consumer_secret)

      request_token = request_token(oauth_callback)
      self.request_token_hash = {
        request_token: request_token.token,
        request_token_secret: request_token.secret,
        authorize_url: request_token.authorize_url
      }
    end

    def get_access_token(verifier, options = {})
      ensure_attributes_are_set! %w(consumer_key consumer_secret)
      options = request_token_hash.merge(options) if request_token_hash

      access_token = rebuild_request_token(options).get_access_token(oauth_verifier: verifier)
      self.oauth_token = access_token.token
      self.oauth_token_secret = access_token.secret
      {
        access_token: access_token.token,
        access_token_secret: access_token.secret
      }
    end

    OAUTH_ATTRIBUTES.each do |attribute|
      define_method(attribute) do
        instance_variable_get("@#{attribute}") || self.class.default_options[attribute]
      end
    end

    private

    def rebuild_request_token(options)
      request_token = options[:request_token] || raise('request_token missing')
      request_token_secret = options[:request_token_secret] || raise('request_token_secret missing')
      OAuth::RequestToken.new(consumer, request_token, request_token_secret)
    end

    def to_hash
      {
        consumer_key:       consumer_key,
        consumer_secret:    consumer_secret,
        oauth_token:        oauth_token,
        oauth_token_secret: oauth_token_secret
      }
    end

    def request_token(oauth_callback)
      @request_token ||= consumer.get_request_token(oauth_callback: oauth_callback)
    end

    def consumer
      OAuth::Consumer.new(consumer_key, consumer_secret, xing_oauth_options)
    end

    def access_token
      OAuth::AccessToken.new(consumer, oauth_token, oauth_token_secret)
    end

    def xing_oauth_options
      {
        site:               ENV['XING_API_SITE'] || 'https://api.xing.com',
        request_token_path: '/v1/request_token',
        authorize_path:     '/v1/authorize',
        access_token_path:  '/v1/access_token',
        signature_method:   'PLAINTEXT',
        oauth_version:      '1.0',
        scheme:             'query_string'
      }
    end

    def hash_to_params(hash)
      return '' if hash.empty?
      '?' + hash.map { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join('&')
    end

    def ensure_attributes_are_set!(attribute_names)
      Array(attribute_names).each do |attribute_name|
        raise "#{attribute_name} is missing" unless send(attribute_name)
      end
    end
  end
end


require 'active_support/core_ext/string/multibyte'
require 'delegate'
module Fuzzily
  class String < SimpleDelegator

    def trigrams
      return [] if __getobj__.nil?
      normalized = self.normalize
      number_of_trigrams = normalized.length - 3
      trigrams = (0..number_of_trigrams).map { |index| normalized[index,3] }.uniq
    end

    def scored_trigrams
      trigrams.map { |t| [t, self.length] }
    end

    protected

    # Remove accents, downcase, replace spaces and word start with '*',
    # return list of normalized words
    def normalize
      ActiveSupport::Multibyte::Chars.new(self).
        mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/,'').downcase.to_s.
        gsub(/[^a-z]/,' ').
        gsub(/\s+/,'*').
        gsub(/^/,'**').
        gsub(/$/,'*')
    end
  end
end


module ClassyEnum
  class InvalidDefault < StandardError; end

  def self._normalize_value(value, default=nil, allow_blank=false) # :nodoc:
    if value.class == Class && value < ClassyEnum::Base
      value = value.new.to_s
    elsif value.present?
      value = value.to_s
    elsif value.blank? && allow_blank
      value
    else
      default
    end
  end

  def self._normalize_default(value, enum_class) # :nodoc:
    if value.present?
      if value.is_a? Proc
        value = value.call(enum_class)
      end

      unless enum_class.include? value
        raise InvalidDefault, "must be one of [#{enum_class.to_a.join(',')}]"
      end
    end

    value
  end

  module ActiveRecord
    def self.included(klass)
      klass.extend self
    end

    # Class macro used to associate an enum with an attribute on an ActiveRecord model.
    # This method is added to an ActiveRecord model when ClassEnum::ActiveRecord
    # is included. Accepts an argument for the enum class to be associated with
    # the model. ActiveRecord validation is automatically added to ensure
    # that a value is one of its pre-defined enum members.
    #
    # ==== Example
    #  # Associate an enum Priority with Alarm model's priority attribute
    #  class Alarm < ActiveRecord::Base
    #    include ClassyEnum::ActiveRecord
    #
    #    classy_enum_attr :priority
    #  end

    #  # Associate an enum Priority with Alarm model's alarm_priority attribute
    #  classy_enum_attr :alarm_priority, class_name: 'Priority'
    #
    #  # Allow enum value to be nil
    #  classy_enum_attr :priority, allow_nil: true
    #
    #  # Allow enum value to be blank
    #  classy_enum_attr :priority, allow_blank: true
    #
    #  # Specifying a default enum value
    #  classy_enum_attr :priority, default: 'low'
    def classy_enum_attr(attribute, options={})
      enum              = (options[:class_name] || options[:enum] || attribute).to_s.camelize.constantize
      allow_blank       = options[:allow_blank] || false
      allow_nil         = options[:allow_nil] || false
      default           = ClassyEnum._normalize_default(options[:default], enum)

      # Add ActiveRecord validation to ensure it won't be saved unless it's an option
      validates_inclusion_of attribute,
        in:          enum,
        allow_blank: allow_blank,
        allow_nil:   allow_nil

      # Use a module so that the reader methods can be overridden in classes and
      # use super to get the enum value.
      mod = Module.new do

        # Define getter method that returns a ClassyEnum instance
        define_method attribute do
          enum.build(read_attribute(attribute) || super(), owner: self)
        end

        # Define setter method that accepts string, symbol, instance or class for member
        define_method "#{attribute}=" do |value|
          value = ClassyEnum._normalize_value(value, default, (allow_nil || allow_blank))
          super(value)
        end

        define_method :save_changed_attribute do |attr_name, arg|
          if attribute.to_s == attr_name.to_s && !attribute_changed?(attr_name)
            arg = enum.build(arg)
            current_value = clone_attribute_value(:read_attribute, attr_name)

            if arg != current_value
              if respond_to?(:set_attribute_was, true)
                set_attribute_was(attr_name, enum.build(arg, owner: self))
              else
                changed_attributes[attr_name] = enum.build(current_value, owner: self)
              end
            end
          else
            super(attr_name, arg)
          end
        end
      end

      include mod

      # Initialize the object with the default value if it is present
      # because this will let you store the default value in the
      # database and make it searchable.
      if default.present?
        after_initialize do
          value = read_attribute(attribute) || send(attribute)

          if (value.blank? && !(allow_blank || allow_nil)) || (value.nil? && !allow_nil)
            send("#{attribute}=", default)
          end
        end
      end

    end

  end
end


module Dummer
  class Random
    def initialize
      @rand = ::Random.new(0)
      @chars = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a # no symbols and multi-bytes for now
    end

    # belows are data types
    # @return Proc object which returns a random generated value, or [formatted_value, raw_value]

    def string(opts = {})
      length, any, value = (opts[:length] || 8), opts[:any], opts[:value]
      if value
        string = value.to_s
        Proc.new { string }
      elsif any
        Proc.new { self.any(any) }
      else
        Proc.new { Array.new(length){@chars[rand(@chars.size-1)]}.join }
      end
    end

    def integer(opts = {})
      format, range, countup, value = opts[:format], opts[:range], opts[:countup], opts[:value]
      if format
        if value
          integer = sprintf(format, value.to_i)
          Proc.new { integer }
        elsif range
          Proc.new { sprintf(format, self.range(range)) }
        elsif countup
          Proc.new {|prev| v = prev + 1; [sprintf(format, v), v] }
        else
          Proc.new { sprintf(format, rand(0..2,147,483,647)) }
        end
      else
        if value
          integer = value.to_i
          Proc.new { integer }
        elsif range
          Proc.new { self.range(range) }
        elsif countup
          Proc.new {|prev| prev + 1 }
        else
          Proc.new { rand(0..2,147,483,647) }
        end
      end
    end

    def float(opts = {})
      format, range, value = opts[:format], opts[:range], opts[:value]
      if format
        if value
          float = value.to_f
          Proc.new { sprintf(format, float) }
        elsif range
          Proc.new { sprintf(format, self.range(range)) }
        else
          Proc.new { r = rand(1..358); sprintf(format, r * Math.cos(r)) }
        end
      else
        if value
          float = value.to_f
          Proc.new { float }
        elsif range
          Proc.new { self.range(range) }
        else
          Proc.new { r = rand(1..358); r * Math.cos(r) }
        end
      end
    end

    def datetime(opts = {})
      format, random, value = (opts[:format] || "%Y-%m-%d %H:%M:%S.%3N"), (opts[:random] || false), opts[:value]
      if value
        Proc.new { value.strftime(format) }
      elsif random
        Proc.new {
          y = rand(1970..2037);
          m = rand(1..12);
          d = rand(1..27);
          h = rand(0..23);
          min = rand(0..59);
          s = rand(0..59);
          usec = rand(0..999999);
          Time.local(y, m, d, h, min, s, usec).strftime(format)
        }
      else
        Proc.new { Time.now.strftime(format) }
      end
    end

    # private

    def range(range)
      rand(range)
    end

    def any(any)
      any[rand(any.size)]
    end

    def rand(arg = nil)
      @rand.rand(arg)
    end
  end
end