Full Code of thoughtbot/paperclip for AI

main c769382c9b70 cached
178 files
647.5 KB
167.0k tokens
805 symbols
1 requests
Download .txt
Showing preview only (695K chars total). Download the full file or copy to clipboard to get everything.
Repository: thoughtbot/paperclip
Branch: main
Commit: c769382c9b70
Files: 178
Total size: 647.5 KB

Directory structure:
gitextract_33jzlcve/

├── .codeclimate.yml
├── .github/
│   └── issue_template.md
├── .gitignore
├── .hound.yml
├── .rubocop.yml
├── .travis.yml
├── Appraisals
├── CONTRIBUTING.md
├── Gemfile
├── LICENSE
├── MIGRATING-ES.md
├── MIGRATING.md
├── NEWS
├── README.md
├── RELEASING.md
├── Rakefile
├── UPGRADING
├── features/
│   ├── basic_integration.feature
│   ├── migration.feature
│   ├── rake_tasks.feature
│   ├── step_definitions/
│   │   ├── attachment_steps.rb
│   │   ├── html_steps.rb
│   │   ├── rails_steps.rb
│   │   ├── s3_steps.rb
│   │   └── web_steps.rb
│   └── support/
│       ├── env.rb
│       ├── fakeweb.rb
│       ├── file_helpers.rb
│       ├── fixtures/
│       │   ├── boot_config.txt
│       │   ├── gemfile.txt
│       │   └── preinitializer.txt
│       ├── paths.rb
│       ├── rails.rb
│       └── selectors.rb
├── gemfiles/
│   ├── 4.2.gemfile
│   └── 5.0.gemfile
├── lib/
│   ├── generators/
│   │   └── paperclip/
│   │       ├── USAGE
│   │       ├── paperclip_generator.rb
│   │       └── templates/
│   │           └── paperclip_migration.rb.erb
│   ├── paperclip/
│   │   ├── attachment.rb
│   │   ├── attachment_registry.rb
│   │   ├── callbacks.rb
│   │   ├── content_type_detector.rb
│   │   ├── errors.rb
│   │   ├── file_command_content_type_detector.rb
│   │   ├── filename_cleaner.rb
│   │   ├── geometry.rb
│   │   ├── geometry_detector_factory.rb
│   │   ├── geometry_parser_factory.rb
│   │   ├── glue.rb
│   │   ├── has_attached_file.rb
│   │   ├── helpers.rb
│   │   ├── interpolations/
│   │   │   └── plural_cache.rb
│   │   ├── interpolations.rb
│   │   ├── io_adapters/
│   │   │   ├── abstract_adapter.rb
│   │   │   ├── attachment_adapter.rb
│   │   │   ├── data_uri_adapter.rb
│   │   │   ├── empty_string_adapter.rb
│   │   │   ├── file_adapter.rb
│   │   │   ├── http_url_proxy_adapter.rb
│   │   │   ├── identity_adapter.rb
│   │   │   ├── nil_adapter.rb
│   │   │   ├── registry.rb
│   │   │   ├── stringio_adapter.rb
│   │   │   ├── uploaded_file_adapter.rb
│   │   │   └── uri_adapter.rb
│   │   ├── locales/
│   │   │   └── en.yml
│   │   ├── logger.rb
│   │   ├── matchers/
│   │   │   ├── have_attached_file_matcher.rb
│   │   │   ├── validate_attachment_content_type_matcher.rb
│   │   │   ├── validate_attachment_presence_matcher.rb
│   │   │   └── validate_attachment_size_matcher.rb
│   │   ├── matchers.rb
│   │   ├── media_type_spoof_detector.rb
│   │   ├── missing_attachment_styles.rb
│   │   ├── processor.rb
│   │   ├── processor_helpers.rb
│   │   ├── rails_environment.rb
│   │   ├── railtie.rb
│   │   ├── schema.rb
│   │   ├── storage/
│   │   │   ├── filesystem.rb
│   │   │   ├── fog.rb
│   │   │   └── s3.rb
│   │   ├── storage.rb
│   │   ├── style.rb
│   │   ├── tempfile.rb
│   │   ├── tempfile_factory.rb
│   │   ├── thumbnail.rb
│   │   ├── url_generator.rb
│   │   ├── validators/
│   │   │   ├── attachment_content_type_validator.rb
│   │   │   ├── attachment_file_name_validator.rb
│   │   │   ├── attachment_file_type_ignorance_validator.rb
│   │   │   ├── attachment_presence_validator.rb
│   │   │   ├── attachment_size_validator.rb
│   │   │   └── media_type_spoof_detection_validator.rb
│   │   ├── validators.rb
│   │   └── version.rb
│   ├── paperclip.rb
│   └── tasks/
│       └── paperclip.rake
├── paperclip.gemspec
├── shoulda_macros/
│   └── paperclip.rb
└── spec/
    ├── database.yml
    ├── paperclip/
    │   ├── attachment_definitions_spec.rb
    │   ├── attachment_processing_spec.rb
    │   ├── attachment_registry_spec.rb
    │   ├── attachment_spec.rb
    │   ├── content_type_detector_spec.rb
    │   ├── file_command_content_type_detector_spec.rb
    │   ├── filename_cleaner_spec.rb
    │   ├── geometry_detector_spec.rb
    │   ├── geometry_parser_spec.rb
    │   ├── geometry_spec.rb
    │   ├── glue_spec.rb
    │   ├── has_attached_file_spec.rb
    │   ├── integration_spec.rb
    │   ├── interpolations_spec.rb
    │   ├── io_adapters/
    │   │   ├── abstract_adapter_spec.rb
    │   │   ├── attachment_adapter_spec.rb
    │   │   ├── data_uri_adapter_spec.rb
    │   │   ├── empty_string_adapter_spec.rb
    │   │   ├── file_adapter_spec.rb
    │   │   ├── http_url_proxy_adapter_spec.rb
    │   │   ├── identity_adapter_spec.rb
    │   │   ├── nil_adapter_spec.rb
    │   │   ├── registry_spec.rb
    │   │   ├── stringio_adapter_spec.rb
    │   │   ├── uploaded_file_adapter_spec.rb
    │   │   └── uri_adapter_spec.rb
    │   ├── matchers/
    │   │   ├── have_attached_file_matcher_spec.rb
    │   │   ├── validate_attachment_content_type_matcher_spec.rb
    │   │   ├── validate_attachment_presence_matcher_spec.rb
    │   │   └── validate_attachment_size_matcher_spec.rb
    │   ├── media_type_spoof_detector_spec.rb
    │   ├── meta_class_spec.rb
    │   ├── paperclip_missing_attachment_styles_spec.rb
    │   ├── paperclip_spec.rb
    │   ├── plural_cache_spec.rb
    │   ├── processor_helpers_spec.rb
    │   ├── processor_spec.rb
    │   ├── rails_environment_spec.rb
    │   ├── rake_spec.rb
    │   ├── schema_spec.rb
    │   ├── storage/
    │   │   ├── filesystem_spec.rb
    │   │   ├── fog_spec.rb
    │   │   ├── s3_live_spec.rb
    │   │   └── s3_spec.rb
    │   ├── style_spec.rb
    │   ├── tempfile_factory_spec.rb
    │   ├── tempfile_spec.rb
    │   ├── thumbnail_spec.rb
    │   ├── url_generator_spec.rb
    │   ├── validators/
    │   │   ├── attachment_content_type_validator_spec.rb
    │   │   ├── attachment_file_name_validator_spec.rb
    │   │   ├── attachment_presence_validator_spec.rb
    │   │   ├── attachment_size_validator_spec.rb
    │   │   └── media_type_spoof_detection_validator_spec.rb
    │   └── validators_spec.rb
    ├── spec_helper.rb
    └── support/
        ├── assertions.rb
        ├── fake_model.rb
        ├── fake_rails.rb
        ├── fixtures/
        │   ├── animated
        │   ├── animated.unknown
        │   ├── empty.html
        │   ├── empty.xlsx
        │   ├── fog.yml
        │   ├── s3.yml
        │   └── text.txt
        ├── matchers/
        │   ├── accept.rb
        │   ├── exist.rb
        │   └── have_column.rb
        ├── mock_attachment.rb
        ├── mock_interpolator.rb
        ├── mock_url_generator_builder.rb
        ├── model_reconstruction.rb
        ├── reporting.rb
        ├── test_data.rb
        └── version_helper.rb

================================================
FILE CONTENTS
================================================

================================================
FILE: .codeclimate.yml
================================================
---
engines:
  duplication:
    enabled: true
    config:
      languages:
      - ruby
  fixme:
    enabled: true
  rubocop:
    enabled: true
ratings:
  paths:
  - "**.rb"
exclude_paths:
- features/
- spec/


================================================
FILE: .github/issue_template.md
================================================
## Deprecation notice

Paperclip is currently undergoing [deprecation in favor of ActiveStorage](https://github.com/thoughtbot/paperclip/blob/master/MIGRATING.md). Maintainers of this repository will no longer be tending to new issues. We're leaving the issues page open so Paperclip users can still see & search through old issues, and continue existing discussions if they wish.


================================================
FILE: .gitignore
================================================
*~
*.swp
.rvmrc
.bundle
tmp
.DS_Store

*.log

public
paperclip*.gem
capybara*.html

*.rbc
.rbx

*SPIKE*
*emfile.lock
tags


================================================
FILE: .hound.yml
================================================
AllCops:
  Include:
    - "**/*.gemspec"
    - "**/*.podspec"
    - "**/*.jbuilder"
    - "**/*.rake"
    - "**/*.opal"
    - "**/Gemfile"
    - "**/Rakefile"
    - "**/Capfile"
    - "**/Guardfile"
    - "**/Podfile"
    - "**/Thorfile"
    - "**/Vagrantfile"
    - "**/Berksfile"
    - "**/Cheffile"
    - "**/Vagabondfile"
  Exclude:
    - "vendor/**/*"
    - "db/schema.rb"
    - 'vendor/**/*'
    - 'gemfiles/vendor/**/*'
  Rails:
    Enabled: false
  DisplayCopNames: false
  StyleGuideCopsOnly: false
Style/AccessModifierIndentation:
  Description: Check indentation of private/protected visibility modifiers.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#indent-public-private-protected
  Enabled: true
  EnforcedStyle: indent
  SupportedStyles:
  - outdent
  - indent
Style/AlignHash:
  Description: Align the elements of a hash literal if they span more than one line.
  Enabled: true
  EnforcedHashRocketStyle: key
  EnforcedColonStyle: key
  EnforcedLastArgumentHashStyle: always_inspect
  SupportedLastArgumentHashStyles:
  - always_inspect
  - always_ignore
  - ignore_implicit
  - ignore_explicit
Style/AlignParameters:
  Description: Align the parameters of a method call if they span more than one line.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-double-indent
  Enabled: true
  EnforcedStyle: with_first_parameter
  SupportedStyles:
  - with_first_parameter
  - with_fixed_indentation
Style/AndOr:
  Description: Use &&/|| instead of and/or.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-and-or-or
  Enabled: true
  EnforcedStyle: always
  SupportedStyles:
  - always
  - conditionals
Style/BarePercentLiterals:
  Description: Checks if usage of %() or %Q() matches configuration.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-q-shorthand
  Enabled: true
  EnforcedStyle: bare_percent
  SupportedStyles:
  - percent_q
  - bare_percent
Style/BracesAroundHashParameters:
  Description: Enforce braces style around hash parameters.
  Enabled: true
  EnforcedStyle: no_braces
  SupportedStyles:
  - braces
  - no_braces
  - context_dependent
Style/CaseIndentation:
  Description: Indentation of when in a case/when/[else/]end.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#indent-when-to-case
  Enabled: true
  IndentWhenRelativeTo: case
  SupportedStyles:
  - case
  - end
  IndentOneStep: false
Style/ClassAndModuleChildren:
  Description: Checks style of children classes and modules.
  Enabled: false
  EnforcedStyle: nested
  SupportedStyles:
  - nested
  - compact
Style/ClassCheck:
  Description: Enforces consistent use of `Object#is_a?` or `Object#kind_of?`.
  Enabled: true
  EnforcedStyle: is_a?
  SupportedStyles:
  - is_a?
  - kind_of?
Style/CollectionMethods:
  Description: Preferred collection methods.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#map-find-select-reduce-size
  Enabled: true
  PreferredMethods:
    collect: map
    collect!: map!
    find: detect
    find_all: select
    reduce: inject
Style/CommentAnnotation:
  Description: Checks formatting of special comments (TODO, FIXME, OPTIMIZE, HACK,
    REVIEW).
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#annotate-keywords
  Enabled: false
  Keywords:
  - TODO
  - FIXME
  - OPTIMIZE
  - HACK
  - REVIEW
Style/DotPosition:
  Description: Checks the position of the dot in multi-line method calls.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains
  Enabled: true
  EnforcedStyle: trailing
  SupportedStyles:
  - leading
  - trailing
Style/EmptyLineBetweenDefs:
  Description: Use empty lines between defs.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#empty-lines-between-methods
  Enabled: true
  AllowAdjacentOneLineDefs: false
Style/EmptyLinesAroundBlockBody:
  Description: Keeps track of empty lines around block bodies.
  Enabled: true
  EnforcedStyle: no_empty_lines
  SupportedStyles:
  - empty_lines
  - no_empty_lines
Style/EmptyLinesAroundClassBody:
  Description: Keeps track of empty lines around class bodies.
  Enabled: true
  EnforcedStyle: no_empty_lines
  SupportedStyles:
  - empty_lines
  - no_empty_lines
Style/EmptyLinesAroundModuleBody:
  Description: Keeps track of empty lines around module bodies.
  Enabled: true
  EnforcedStyle: no_empty_lines
  SupportedStyles:
  - empty_lines
  - no_empty_lines
Style/Encoding:
  Description: Use UTF-8 as the source file encoding.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#utf-8
  Enabled: false
  EnforcedStyle: always
  SupportedStyles:
  - when_needed
  - always
Style/FileName:
  Description: Use snake_case for source file names.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-files
  Enabled: false
  Exclude: []
Style/FirstParameterIndentation:
  Description: Checks the indentation of the first parameter in a method call.
  Enabled: true
  EnforcedStyle: special_for_inner_method_call_in_parentheses
  SupportedStyles:
  - consistent
  - special_for_inner_method_call
  - special_for_inner_method_call_in_parentheses
Style/For:
  Description: Checks use of for or each in multiline loops.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-for-loops
  Enabled: true
  EnforcedStyle: each
  SupportedStyles:
  - for
  - each
Style/FormatString:
  Description: Enforce the use of Kernel#sprintf, Kernel#format or String#%.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#sprintf
  Enabled: false
  EnforcedStyle: format
  SupportedStyles:
  - format
  - sprintf
  - percent
Style/GlobalVars:
  Description: Do not introduce global variables.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#instance-vars
  Enabled: false
  AllowedVariables: []
Style/GuardClause:
  Description: Check for conditionals that can be replaced with guard clauses
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals
  Enabled: false
  MinBodyLength: 1
Style/HashSyntax:
  Description: 'Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax { :a =>
    1, :b => 2 }.'
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#hash-literals
  Enabled: true
  EnforcedStyle: ruby19
  SupportedStyles:
  - ruby19
  - hash_rockets
Style/IfUnlessModifier:
  Description: Favor modifier if/unless usage when you have a single-line body.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier
  Enabled: false
  MaxLineLength: 80
Style/IndentationWidth:
  Description: Use 2 spaces for indentation.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-indentation
  Enabled: true
  Width: 2
Style/IndentHash:
  Description: Checks the indentation of the first key in a hash literal.
  Enabled: true
  EnforcedStyle: special_inside_parentheses
  SupportedStyles:
  - special_inside_parentheses
  - consistent
Style/LambdaCall:
  Description: Use lambda.call(...) instead of lambda.(...).
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#proc-call
  Enabled: false
  EnforcedStyle: call
  SupportedStyles:
  - call
  - braces
Style/Next:
  Description: Use `next` to skip iteration instead of a condition at the end.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals
  Enabled: false
  EnforcedStyle: skip_modifier_ifs
  MinBodyLength: 3
  SupportedStyles:
  - skip_modifier_ifs
  - always
Style/NonNilCheck:
  Description: Checks for redundant nil checks.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-non-nil-checks
  Enabled: true
  IncludeSemanticChanges: false
Style/MethodDefParentheses:
  Description: Checks if the method definitions have or don't have parentheses.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#method-parens
  Enabled: true
  EnforcedStyle: require_parentheses
  SupportedStyles:
  - require_parentheses
  - require_no_parentheses
Style/MethodName:
  Description: Use the configured style when naming methods.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars
  Enabled: true
  EnforcedStyle: snake_case
  SupportedStyles:
  - snake_case
  - camelCase
Style/MultilineOperationIndentation:
  Description: Checks indentation of binary operations that span more than one line.
  Enabled: true
  EnforcedStyle: aligned
  SupportedStyles:
  - aligned
  - indented
Style/NumericLiterals:
  Description: Add underscores to large numeric literals to improve their readability.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#underscores-in-numerics
  Enabled: false
  MinDigits: 5
Style/ParenthesesAroundCondition:
  Description: Don't use parentheses around the condition of an if/unless/while.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-parens-if
  Enabled: true
  AllowSafeAssignment: true
Style/PercentLiteralDelimiters:
  Description: Use `%`-literal delimiters consistently
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-literal-braces
  Enabled: false
  PreferredDelimiters:
    "%": "()"
    "%i": "()"
    "%q": "()"
    "%Q": "()"
    "%r": "{}"
    "%s": "()"
    "%w": "()"
    "%W": "()"
    "%x": "()"
Style/PercentQLiterals:
  Description: Checks if uses of %Q/%q match the configured preference.
  Enabled: true
  EnforcedStyle: lower_case_q
  SupportedStyles:
  - lower_case_q
  - upper_case_q
Style/PredicateName:
  Description: Check the names of predicate methods.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark
  Enabled: true
  NamePrefix:
  - is_
  - has_
  - have_
  NamePrefixBlacklist:
  - is_
Style/RaiseArgs:
  Description: Checks the arguments passed to raise/fail.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#exception-class-messages
  Enabled: false
  EnforcedStyle: exploded
  SupportedStyles:
  - compact
  - exploded
Style/RedundantReturn:
  Description: Don't use return where it's not required.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-explicit-return
  Enabled: true
  AllowMultipleReturnValues: false
Style/RegexpLiteral:
  Description: Use %r for regular expressions matching more than `MaxSlashes` '/'
    characters. Use %r only for regular expressions matching more than `MaxSlashes`
    '/' character.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-r
  Enabled: false
  MaxSlashes: 1
Style/Semicolon:
  Description: Don't use semicolons to terminate expressions.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-semicolon
  Enabled: true
  AllowAsExpressionSeparator: false
Style/SignalException:
  Description: Checks for proper usage of fail and raise.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#fail-method
  Enabled: false
  EnforcedStyle: semantic
  SupportedStyles:
  - only_raise
  - only_fail
  - semantic
Style/SingleLineBlockParams:
  Description: Enforces the names of some block params.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#reduce-blocks
  Enabled: false
  Methods:
  - reduce:
    - a
    - e
  - inject:
    - a
    - e
Style/SingleLineMethods:
  Description: Avoid single-line methods.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-single-line-methods
  Enabled: false
  AllowIfMethodIsEmpty: true
Style/StringLiterals:
  Description: Checks if uses of quotes match the configured preference.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-string-literals
  Enabled: true
  EnforcedStyle: double_quotes
  SupportedStyles:
  - single_quotes
  - double_quotes
Style/StringLiteralsInInterpolation:
  Description: Checks if uses of quotes inside expressions in interpolated strings
    match the configured preference.
  Enabled: true
  EnforcedStyle: single_quotes
  SupportedStyles:
  - single_quotes
  - double_quotes
Style/SpaceAroundBlockParameters:
  Description: Checks the spacing inside and after block parameters pipes.
  Enabled: true
  EnforcedStyleInsidePipes: no_space
  SupportedStyles:
  - space
  - no_space
Style/SpaceAroundEqualsInParameterDefault:
  Description: Checks that the equals signs in parameter default assignments have
    or don't have surrounding space depending on configuration.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-around-equals
  Enabled: true
  EnforcedStyle: space
  SupportedStyles:
  - space
  - no_space
Style/SpaceBeforeBlockBraces:
  Description: Checks that the left block brace has or doesn't have space before it.
  Enabled: true
  EnforcedStyle: space
  SupportedStyles:
  - space
  - no_space
Style/SpaceInsideBlockBraces:
  Description: Checks that block braces have or don't have surrounding space. For
    blocks taking parameters, checks that the left brace has or doesn't have trailing
    space.
  Enabled: true
  EnforcedStyle: space
  SupportedStyles:
  - space
  - no_space
  EnforcedStyleForEmptyBraces: no_space
  SpaceBeforeBlockParameters: true
Style/SpaceInsideHashLiteralBraces:
  Description: Use spaces inside hash literal braces - or don't.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators
  Enabled: true
  EnforcedStyle: space
  EnforcedStyleForEmptyBraces: no_space
  SupportedStyles:
  - space
  - no_space
Style/SymbolProc:
  Description: Use symbols as procs instead of blocks when possible.
  Enabled: true
  IgnoredMethods:
  - respond_to
Style/TrailingBlankLines:
  Description: Checks trailing blank lines and final newline.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#newline-eof
  Enabled: true
  EnforcedStyle: final_newline
  SupportedStyles:
  - final_newline
  - final_blank_line
Style/TrailingCommaInLiteral:
  Description: Checks for trailing comma in parameter lists and literals.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas
  Enabled: false
  EnforcedStyleForMultiline: no_comma
  SupportedStyles:
  - comma
  - no_comma
Style/TrivialAccessors:
  Description: Prefer attr_* methods to trivial readers/writers.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#attr_family
  Enabled: false
  ExactNameMatch: false
  AllowPredicates: false
  AllowDSLWriters: false
  Whitelist:
  - to_ary
  - to_a
  - to_c
  - to_enum
  - to_h
  - to_hash
  - to_i
  - to_int
  - to_io
  - to_open
  - to_path
  - to_proc
  - to_r
  - to_regexp
  - to_str
  - to_s
  - to_sym
Style/VariableName:
  Description: Use the configured style when naming variables.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars
  Enabled: true
  EnforcedStyle: snake_case
  SupportedStyles:
  - snake_case
  - camelCase
Style/WhileUntilModifier:
  Description: Favor modifier while/until usage when you have a single-line body.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#while-as-a-modifier
  Enabled: false
  MaxLineLength: 80
Style/WordArray:
  Description: Use %w or %W for arrays of words.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-w
  Enabled: false
  MinSize: 0
  WordRegex: !ruby/regexp /\A[\p{Word}]+\z/
Metrics/AbcSize:
  Description: A calculated magnitude based on number of assignments, branches, and
    conditions.
  Enabled: true
  Max: 15
Metrics/BlockNesting:
  Description: Avoid excessive block nesting
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count
  Enabled: false
  Max: 3
Metrics/ClassLength:
  Description: Avoid classes longer than 100 lines of code.
  Enabled: false
  CountComments: false
  Max: 100
Metrics/CyclomaticComplexity:
  Description: A complexity metric that is strongly correlated to the number of test
    cases needed to validate a method.
  Enabled: false
  Max: 6
Metrics/LineLength:
  Description: Limit lines to 80 characters.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#80-character-limits
  Enabled: true
  Max: 80
  AllowURI: true
  URISchemes:
  - http
  - https
Metrics/MethodLength:
  Description: Avoid methods longer than 10 lines of code.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#short-methods
  Enabled: false
  CountComments: false
  Max: 10
Metrics/ParameterLists:
  Description: Avoid parameter lists longer than three or four parameters.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#too-many-params
  Enabled: false
  Max: 5
  CountKeywordArgs: true
Metrics/PerceivedComplexity:
  Description: A complexity metric geared towards measuring complexity for a human
    reader.
  Enabled: false
  Max: 7
Lint/AssignmentInCondition:
  Description: Don't use assignment in conditions.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition
  Enabled: false
  AllowSafeAssignment: true
Lint/EndAlignment:
  Description: Align ends correctly.
  Enabled: true
  AlignWith: keyword
  SupportedStyles:
  - keyword
  - variable
Lint/DefEndAlignment:
  Description: Align ends corresponding to defs correctly.
  Enabled: true
  AlignWith: start_of_line
  SupportedStyles:
  - start_of_line
  - def
Rails/ActionFilter:
  Description: Enforces consistent use of action filter methods.
  Enabled: false
  EnforcedStyle: action
  SupportedStyles:
  - action
  - filter
  Include:
  - app/controllers/**/*.rb
Rails/HasAndBelongsToMany:
  Description: Prefer has_many :through to has_and_belongs_to_many.
  Enabled: true
  Include:
  - app/models/**/*.rb
Rails/Output:
  Description: Checks for calls to puts, print, etc.
  Enabled: true
  Include:
  - app/**/*.rb
  - config/**/*.rb
  - db/**/*.rb
  - lib/**/*.rb
Rails/ReadWriteAttribute:
  Description: Checks for read_attribute(:attr) and write_attribute(:attr, val).
  Enabled: true
  Include:
  - app/models/**/*.rb
Rails/ScopeArgs:
  Description: Checks the arguments of ActiveRecord scopes.
  Enabled: true
  Include:
  - app/models/**/*.rb
Rails/Validation:
  Description: Use validates :attribute, hash of validations.
  Enabled: true
  Include:
  - app/models/**/*.rb
Style/InlineComment:
  Description: Avoid inline comments.
  Enabled: false
Style/MethodCalledOnDoEndBlock:
  Description: Avoid chaining a method call on a do...end block.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#single-line-blocks
  Enabled: false
Style/SymbolArray:
  Description: Use %i or %I for arrays of symbols.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-i
  Enabled: false
Style/ExtraSpacing:
  Description: Do not use unnecessary spacing.
  Enabled: true
Style/AccessorMethodName:
  Description: Check the naming of accessor methods for get_/set_.
  Enabled: false
Style/Alias:
  Description: Use alias_method instead of alias.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#alias-method
  Enabled: false
Style/AlignArray:
  Description: Align the elements of an array literal if they span more than one line.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#align-multiline-arrays
  Enabled: true
Style/ArrayJoin:
  Description: Use Array#join instead of Array#*.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#array-join
  Enabled: false
Style/AsciiComments:
  Description: Use only ascii symbols in comments.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#english-comments
  Enabled: false
Style/AsciiIdentifiers:
  Description: Use only ascii symbols in identifiers.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#english-identifiers
  Enabled: false
Style/Attr:
  Description: Checks for uses of Module#attr.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#attr
  Enabled: false
Style/BeginBlock:
  Description: Avoid the use of BEGIN blocks.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-BEGIN-blocks
  Enabled: true
Style/BlockComments:
  Description: Do not use block comments.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-block-comments
  Enabled: true
Style/BlockEndNewline:
  Description: Put end statement of multiline block on its own line.
  Enabled: true
Style/Blocks:
  Description: Avoid using {...} for multi-line blocks (multiline chaining is always
    ugly). Prefer {...} over do...end for single-line blocks.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#single-line-blocks
  Enabled: true
Style/CaseEquality:
  Description: Avoid explicit use of the case equality operator(===).
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-case-equality
  Enabled: false
Style/CharacterLiteral:
  Description: Checks for uses of character literals.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-character-literals
  Enabled: false
Style/ClassAndModuleCamelCase:
  Description: Use CamelCase for classes and modules.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#camelcase-classes
  Enabled: true
Style/ClassMethods:
  Description: Use self when defining module/class methods.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#def-self-singletons
  Enabled: true
Style/ClassVars:
  Description: Avoid the use of class variables.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-class-vars
  Enabled: false
Style/ColonMethodCall:
  Description: 'Do not use :: for method call.'
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#double-colons
  Enabled: false
Style/CommentIndentation:
  Description: Indentation of comments.
  Enabled: true
Style/ConstantName:
  Description: Constants should use SCREAMING_SNAKE_CASE.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#screaming-snake-case
  Enabled: true
Style/DefWithParentheses:
  Description: Use def with parentheses when there are arguments.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#method-parens
  Enabled: true
Style/Documentation:
  Description: Document classes and non-namespace modules.
  Enabled: false
Style/DoubleNegation:
  Description: Checks for uses of double negation (!!).
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-bang-bang
  Enabled: false
Style/EachWithObject:
  Description: Prefer `each_with_object` over `inject` or `reduce`.
  Enabled: false
Style/ElseAlignment:
  Description: Align elses and elsifs correctly.
  Enabled: true
Style/EmptyElse:
  Description: Avoid empty else-clauses.
  Enabled: true
Style/EmptyLines:
  Description: Don't use several empty lines in a row.
  Enabled: true
Style/EmptyLinesAroundAccessModifier:
  Description: Keep blank lines around access modifiers.
  Enabled: true
Style/EmptyLinesAroundMethodBody:
  Description: Keeps track of empty lines around method bodies.
  Enabled: true
Style/EmptyLiteral:
  Description: Prefer literals to Array.new/Hash.new/String.new.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#literal-array-hash
  Enabled: false
Style/EndBlock:
  Description: Avoid the use of END blocks.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-END-blocks
  Enabled: true
Style/EndOfLine:
  Description: Use Unix-style line endings.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#crlf
  Enabled: true
Style/EvenOdd:
  Description: Favor the use of Fixnum#even? && Fixnum#odd?
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#predicate-methods
  Enabled: false
Style/FlipFlop:
  Description: Checks for flip flops
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-flip-flops
  Enabled: false
Style/IfWithSemicolon:
  Description: Do not use if x; .... Use the ternary operator instead.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-semicolon-ifs
  Enabled: false
Style/IndentationConsistency:
  Description: Keep indentation straight.
  Enabled: true
Style/IndentArray:
  Description: Checks the indentation of the first element in an array literal.
  Enabled: true
Style/InfiniteLoop:
  Description: Use Kernel#loop for infinite loops.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#infinite-loop
  Enabled: true
Style/Lambda:
  Description: Use the new lambda literal syntax for single-line blocks.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#lambda-multi-line
  Enabled: false
Style/LeadingCommentSpace:
  Description: Comments should start with a space.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#hash-space
  Enabled: true
Style/LineEndConcatenation:
  Description: Use \ instead of + or << to concatenate two string literals at line
    end.
  Enabled: false
Style/MethodCallParentheses:
  Description: Do not use parentheses for method calls with no arguments.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-args-no-parens
  Enabled: true
Style/ModuleFunction:
  Description: Checks for usage of `extend self` in modules.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#module-function
  Enabled: false
Style/MultilineBlockChain:
  Description: Avoid multi-line chains of blocks.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#single-line-blocks
  Enabled: true
Style/MultilineBlockLayout:
  Description: Ensures newlines after multiline block do statements.
  Enabled: true
Style/MultilineIfThen:
  Description: Do not use then for multi-line if/unless.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-then
  Enabled: true
Style/MultilineTernaryOperator:
  Description: 'Avoid multi-line ?: (the ternary operator); use if/unless instead.'
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-multiline-ternary
  Enabled: true
Style/NegatedIf:
  Description: Favor unless over if for negative conditions (or control flow or).
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#unless-for-negatives
  Enabled: false
Style/NegatedWhile:
  Description: Favor until over while for negative conditions.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#until-for-negatives
  Enabled: false
Style/NestedTernaryOperator:
  Description: Use one expression per branch in a ternary operator.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-ternary
  Enabled: true
Style/NilComparison:
  Description: Prefer x.nil? to x == nil.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#predicate-methods
  Enabled: false
Style/Not:
  Description: Use ! instead of not.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#bang-not-not
  Enabled: false
Style/OneLineConditional:
  Description: Favor the ternary operator(?:) over if/then/else/end constructs.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#ternary-operator
  Enabled: false
Style/OpMethod:
  Description: When defining binary operators, name the argument other.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#other-arg
  Enabled: false
Style/PerlBackrefs:
  Description: Avoid Perl-style regex back references.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers
  Enabled: false
Style/Proc:
  Description: Use proc instead of Proc.new.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#proc
  Enabled: false
Style/RedundantBegin:
  Description: Don't use begin blocks when they are not needed.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#begin-implicit
  Enabled: true
Style/RedundantException:
  Description: Checks for an obsolete RuntimeException argument in raise/fail.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-explicit-runtimeerror
  Enabled: true
Style/RedundantSelf:
  Description: Don't use self where it's not needed.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-self-unless-required
  Enabled: true
Style/RescueModifier:
  Description: Avoid using rescue in its modifier form.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-rescue-modifiers
  Enabled: true
Style/SelfAssignment:
  Description: Checks for places where self-assignment shorthand should have been
    used.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#self-assignment
  Enabled: false
Style/SpaceBeforeFirstArg:
  Description: Checks that exactly one space is used between a method name and the
    first argument for method calls without parentheses.
  Enabled: true
Style/SpaceAfterColon:
  Description: Use spaces after colons.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators
  Enabled: true
Style/SpaceAfterComma:
  Description: Use spaces after commas.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators
  Enabled: true
Style/SpaceAroundKeyword:
  Description: Use spaces after if/elsif/unless/while/until/case/when.
  Enabled: true
Style/SpaceAfterMethodName:
  Description: Do not put a space between a method name and the opening parenthesis
    in a method definition.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#parens-no-spaces
  Enabled: true
Style/SpaceAfterNot:
  Description: Tracks redundant space after the ! operator.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-space-bang
  Enabled: true
Style/SpaceAfterSemicolon:
  Description: Use spaces after semicolons.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators
  Enabled: true
Style/SpaceBeforeComma:
  Description: No spaces before commas.
  Enabled: true
Style/SpaceBeforeComment:
  Description: Checks for missing space between code and a comment on the same line.
  Enabled: true
Style/SpaceBeforeSemicolon:
  Description: No spaces before semicolons.
  Enabled: true
Style/SpaceAroundOperators:
  Description: Use spaces around operators.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-operators
  Enabled: true
Style/SpaceInsideBrackets:
  Description: No spaces after [ or before ].
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-spaces-braces
  Enabled: true
Style/SpaceInsideParens:
  Description: No spaces after ( or before ).
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-spaces-braces
  Enabled: true
Style/SpaceInsideRangeLiteral:
  Description: No spaces inside range literals.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-space-inside-range-literals
  Enabled: true
Style/SpecialGlobalVars:
  Description: Avoid Perl-style global variables.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms
  Enabled: false
Style/StructInheritance:
  Description: Checks for inheritance from Struct.new.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-extend-struct-new
  Enabled: true
Style/Tab:
  Description: No hard tabs.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#spaces-indentation
  Enabled: true
Style/TrailingWhitespace:
  Description: Avoid trailing whitespace.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace
  Enabled: true
Style/UnlessElse:
  Description: Do not use unless with else. Rewrite these with the positive case first.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-else-with-unless
  Enabled: true
Style/UnneededCapitalW:
  Description: Checks for %W when interpolation is not needed.
  Enabled: true
Style/UnneededPercentQ:
  Description: Checks for %q/%Q when single quotes or double quotes would do.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-q
  Enabled: true
Style/UnneededPercentX:
  Description: Checks for %x when `` would do.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-x
  Enabled: true
Style/VariableInterpolation:
  Description: Don't interpolate global, instance and class variables directly in
    strings.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#curlies-interpolate
  Enabled: false
Style/WhenThen:
  Description: Use when x then ... for one-line cases.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#one-line-cases
  Enabled: false
Style/WhileUntilDo:
  Description: Checks for redundant do after while or until.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-multiline-while-do
  Enabled: true
Lint/AmbiguousOperator:
  Description: Checks for ambiguous operators in the first argument of a method invocation
    without parentheses.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#parens-as-args
  Enabled: false
Lint/AmbiguousRegexpLiteral:
  Description: Checks for ambiguous regexp literals in the first argument of a method
    invocation without parenthesis.
  Enabled: false
Lint/BlockAlignment:
  Description: Align block ends correctly.
  Enabled: true
Lint/ConditionPosition:
  Description: Checks for condition placed in a confusing position relative to the
    keyword.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#same-line-condition
  Enabled: false
Lint/Debugger:
  Description: Check for debugger calls.
  Enabled: true
Lint/DeprecatedClassMethods:
  Description: Check for deprecated class method calls.
  Enabled: false
Lint/DuplicateMethods:
  Description: Check for duplicate methods calls.
  Enabled: true
Lint/ElseLayout:
  Description: Check for odd code arrangement in an else block.
  Enabled: false
Lint/EmptyEnsure:
  Description: Checks for empty ensure block.
  Enabled: true
Lint/EmptyInterpolation:
  Description: Checks for empty string interpolation.
  Enabled: true
Lint/EndInMethod:
  Description: END blocks should not be placed inside method definitions.
  Enabled: true
Lint/EnsureReturn:
  Description: Do not use return in an ensure block.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-return-ensure
  Enabled: true
Lint/Eval:
  Description: The use of eval represents a serious security risk.
  Enabled: true
Lint/HandleExceptions:
  Description: Don't suppress exception.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions
  Enabled: false
Lint/InvalidCharacterLiteral:
  Description: Checks for invalid character literals with a non-escaped whitespace
    character.
  Enabled: false
Lint/LiteralInCondition:
  Description: Checks of literals used in conditions.
  Enabled: false
Lint/LiteralInInterpolation:
  Description: Checks for literals used in interpolation.
  Enabled: false
Lint/Loop:
  Description: Use Kernel#loop with break rather than begin/end/until or begin/end/while
    for post-loop tests.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#loop-with-break
  Enabled: false
Lint/ParenthesesAsGroupedExpression:
  Description: Checks for method calls with a space before the opening parenthesis.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#parens-no-spaces
  Enabled: false
Lint/RequireParentheses:
  Description: Use parentheses in the method call to avoid confusion about precedence.
  Enabled: false
Lint/RescueException:
  Description: Avoid rescuing the Exception class.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-blind-rescues
  Enabled: true
Lint/ShadowingOuterLocalVariable:
  Description: Do not use the same name as outer local variable for block arguments
    or block local variables.
  Enabled: true
Lint/SpaceBeforeFirstArg:
  Description: Put a space between a method name and the first argument in a method
    call without parentheses.
  Enabled: true
Lint/StringConversionInInterpolation:
  Description: Checks for Object#to_s usage in string interpolation.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-to-s
  Enabled: true
Lint/UnderscorePrefixedVariableName:
  Description: Do not use prefix `_` for a variable that is used.
  Enabled: false
Lint/UnusedBlockArgument:
  Description: Checks for unused block arguments.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars
  Enabled: true
Lint/UnusedMethodArgument:
  Description: Checks for unused method arguments.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars
  Enabled: true
Lint/UnreachableCode:
  Description: Unreachable code.
  Enabled: true
Lint/UselessAccessModifier:
  Description: Checks for useless access modifiers.
  Enabled: true
Lint/UselessAssignment:
  Description: Checks for useless assignment to a local variable.
  StyleGuide: https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars
  Enabled: true
Lint/UselessComparison:
  Description: Checks for comparison of something with itself.
  Enabled: true
Lint/UselessElseWithoutRescue:
  Description: Checks for useless `else` in `begin..end` without `rescue`.
  Enabled: true
Lint/UselessSetterCall:
  Description: Checks for useless setter call to a local variable.
  Enabled: true
Lint/Void:
  Description: Possible use of operator/literal/variable in void context.
  Enabled: false
Rails/Delegate:
  Description: Prefer delegate method for delegations.
  Enabled: false



================================================
FILE: .rubocop.yml
================================================
inherit_from: .hound.yml


================================================
FILE: .travis.yml
================================================
language: ruby
sudo: false

rvm:
  - 2.1
  - 2.2
  - 2.3
  - 2.4

script: "bundle exec rake clean spec cucumber"

addons:
  apt:
    packages:
    - ghostscript

gemfile:
  - gemfiles/4.2.gemfile
  - gemfiles/5.0.gemfile

matrix:
  fast_finish: true
  exclude:
    - gemfile: gemfiles/5.0.gemfile
      rvm: 2.1


================================================
FILE: Appraisals
================================================
appraise "4.2" do
  gem "rails", "~> 4.2.0"
end

appraise "5.0" do
  gem "rails", "~> 5.0.0"
end


================================================
FILE: CONTRIBUTING.md
================================================
Contributing
============

We love pull requests from everyone. By participating in this project, you agree
to abide by the thoughtbot [code of conduct].

[code of conduct]: https://thoughtbot.com/open-source-code-of-conduct

Here's a quick guide for contributing:

1. Fork the repo.

1. Make sure you have ImageMagick and Ghostscript installed. See [this section]
(./README.md#image-processor) of the README.

1. Run the tests. We only take pull requests with passing tests, and it's great
to know that you have a clean slate: `bundle && bundle exec rake`

1. Add a test for your change. Only refactoring and documentation changes
require no new tests. If you are adding functionality or fixing a bug, we need
a test!

1. Make the test pass.

1. Mention how your changes affect the project to other developers and users in
   the `NEWS.md` file.

1. Push to your fork and submit a pull request.

At this point you're waiting on us. We like to at least comment on, if not
accept, pull requests within seven business days (most of the work on Paperclip
gets done on Fridays). We may suggest some changes or improvements or
alternatives.

Some things that will increase the chance that your pull request is accepted,
taken straight from the Ruby on Rails guide:

* Use Rails idioms and helpers
* Include tests that fail without your code, and pass with it
* Update the documentation, the surrounding one, examples elsewhere, guides,
  whatever is affected by your contribution

Running Tests
-------------

Paperclip uses [Appraisal](https://github.com/thoughtbot/appraisal) to aid
testing against multiple version of Ruby on Rails. This helps us to make sure
that Paperclip performs correctly with them.

Paperclip also uses [RSpec](http://rspec.info) for its unit tests. If you submit
tests that are not written for Cucumber or RSpec without a very good reason, you
will be asked to rewrite them before we'll accept.

### Bootstrapping your test suite:

    bundle install
    bundle exec appraisal install

This will install all the required gems that requires to test against each
version of Rails, which defined in `gemfiles/*.gemfile`.

### To run a full test suite:

    bundle exec appraisal rake

This will run RSpec and Cucumber against all version of Rails

### To run single Test::Unit or Cucumber test

You need to specify a `BUNDLE_GEMFILE` pointing to the gemfile before running
the normal test command:

    BUNDLE_GEMFILE=gemfiles/4.1.gemfile rspec spec/paperclip/attachment_spec.rb
    BUNDLE_GEMFILE=gemfiles/4.1.gemfile cucumber features/basic_integration.feature

Syntax
------

* Two spaces, no tabs.
* No trailing whitespace. Blank lines should not have any space.
* Prefer &&/|| over and/or.
* MyClass.my_method(my_arg) not my_method( my_arg ) or my_method my_arg.
* a = b and not a=b.
* Follow the conventions you see used in the source already.

And in case we didn't emphasize it enough: we love tests!


================================================
FILE: Gemfile
================================================
source "https://rubygems.org"

gemspec

gem 'sqlite3', '~> 1.3.8', :platforms => :ruby
gem 'pry'

# Hinting at development dependencies
# Prevents bundler from taking a long-time to resolve
group :development, :test do
  gem 'activerecord-import'
  gem 'mime-types'
  gem 'builder'
  gem 'rubocop', require: false
  gem 'rspec'
end


================================================
FILE: LICENSE
================================================

LICENSE

The MIT License

Copyright (c) 2008-2016 Jon Yurek and thoughtbot, inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: MIGRATING-ES.md
================================================
# Migrando de Paperclip a ActiveStorage

Paperclip y ActiveStorage resuelven problemas similares con soluciones
similares, por lo que pasar de uno a otro es simple.

El proceso de ir desde Paperclip hacia ActiveStorage es como sigue:

1. Implementa las migraciones a la base de datos de ActiveStorage.
2. Configura el almacenamiento.
3. Copia la base de datos.
4. Copia los archivos.
5. Actualiza tus pruebas.
6. Actualiza tus vistas.
7. Actualiza tus controladores.
8. Actualiza tus modelos.

## Implementa las migraciones a la base de datos de ActiveStorage

Sigue [las instrucciones para instalar ActiveStorage]. Muy probablemente vas a
querer agregar la gema `mini_magick` a tu Gemfile.


```sh
rails active_storage:install
```

[las instrucciones para instalar ActiveStorage]: https://github.com/rails/rails/blob/master/activestorage/README.md#installation

## Configura el almacenamiento

De nuevo, sigue [las instrucciones para configurar ActiveStorage].

[las instrucciones para configurar ActiveStorage]: http://edgeguides.rubyonrails.org/active_storage_overview.html#setup

## Copia la base de datos.

Las tablas `active_storage_blobs` y`active_storage_attachments` son en donde
ActiveStorage espera encontrar los metadatos del archivo. Paperclip almacena los
metadatos del archivo directamente en en la tabla del objeto asociado.

Vas a necesitar escribir una migración para esta conversión. Proveer un script
simple, es complicado porque están involucrados tus modelos. ¡Pero lo
intentaremos!

Así sería para un `User` con un `avatar` en Paperclip:

```ruby
class User < ApplicationRecord
  has_attached_file :avatar
end
```

Tus migraciones de Paperclip producirán una tabla como la siguiente:

```ruby
create_table "users", force: :cascade do |t|
  t.string "avatar_file_name"
  t.string "avatar_content_type"
  t.integer "avatar_file_size"
  t.datetime "avatar_updated_at"
end
```

Y tu la convertirás en estas tablas:

```ruby
create_table "active_storage_attachments", force: :cascade do |t|
  t.string "name", null: false
  t.string "record_type", null: false
  t.integer "record_id", null: false
  t.integer "blob_id", null: false
  t.datetime "created_at", null: false
  t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
  t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
end
```

```ruby
create_table "active_storage_blobs", force: :cascade do |t|
  t.string "key", null: false
  t.string "filename", null: false
  t.string "content_type"
  t.text "metadata"
  t.bigint "byte_size", null: false
  t.string "checksum", null: false
  t.datetime "created_at", null: false
  t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
end
```

Así que asumiendo que quieres dejar los archivos en el mismo lugar, _esta es tu
migración_. De otra forma, ve la siguiente sección primero y modifica la
migración como corresponda.

```ruby
Dir[Rails.root.join("app/models/**/*.rb")].sort.each { |file| require file }

class ConvertToActiveStorage < ActiveRecord::Migration[5.2]
  require 'open-uri'

  def up
    # postgres
    get_blob_id = 'LASTVAL()'
    # mariadb
    # get_blob_id = 'LAST_INSERT_ID()'
    # sqlite
    # get_blob_id = 'LAST_INSERT_ROWID()'

    active_storage_blob_statement = ActiveRecord::Base.connection.raw_connection.prepare(<<-SQL)
      INSERT INTO active_storage_blobs (
        key, filename, content_type, metadata, byte_size,
        checksum, created_at
      ) VALUES (?, ?, ?, '{}', ?, ?, ?)
    SQL

    active_storage_attachment_statement = ActiveRecord::Base.connection.raw_connection.prepare(<<-SQL)
      INSERT INTO active_storage_attachments (
        name, record_type, record_id, blob_id, created_at
      ) VALUES (?, ?, ?, #{get_blob_id}, ?)
    SQL

    models = ActiveRecord::Base.descendants.reject(&:abstract_class?)

    transaction do
      models.each do |model|
        attachments = model.column_names.map do |c|
          if c =~ /(.+)_file_name$/
            $1
          end
        end.compact

        model.find_each.each do |instance|
          attachments.each do |attachment|
            active_storage_blob_statement.execute(
              key(instance, attachment),
              instance.send("#{attachment}_file_name"),
              instance.send("#{attachment}_content_type"),
              instance.send("#{attachment}_file_size"),
              checksum(instance.send(attachment)),
              instance.updated_at.iso8601
            )

            active_storage_attachment_statement.
              execute(attachment, model.name, instance.id, instance.updated_at.iso8601)
          end
        end
      end
    end

    active_storage_attachment_statement.close
    active_storage_blob_statement.close
  end

  def down
    raise ActiveRecord::IrreversibleMigration
  end

  private

  def key(instance, attachment)
    SecureRandom.uuid
    # Alternativamente:
    # instance.send("#{attachment}_file_name")
  end

  def checksum(attachment)
    # archivos locales almacenados en disco:
    url = attachment.path
    Digest::MD5.base64digest(File.read(url))

    # archivos remotos almacenados en la computadora de alguién más:
    # url = attachment.url
    # Digest::MD5.base64digest(Net::HTTP.get(URI(url)))
  end
end
```

## Copia los archivos

La migración de arriba deja los archivos como estaban. Sin embargo,
los servicios de Paperclip y ActiveStorage utilizan diferentes ubicaciones.

Por defecto, Paperclip se ve así:

```
public/system/users/avatars/000/000/004/original/the-mystery-of-life.png
```

Y ActiveStorage se ve así:

```
storage/xM/RX/xMRXuT6nqpoiConJFQJFt6c9
```

Ese `xMRXuT6nqpoiConJFQJFt6c9` es el valor de `active_storage_blobs.key`. En la
migración de arriba usamos simplemente el nombre del archivo, pero tal vez
quieras usar un UUID.

Migrando los archivos en un hospedaje externo (S3, Azure Storage, GCS, etc.)
está fuera del alcance de este documento inicial. Así es como se vería para un
almacenamiento local:

```ruby
#!bin/rails runner

class ActiveStorageBlob < ActiveRecord::Base
end

class ActiveStorageAttachment < ActiveRecord::Base
  belongs_to :blob, class_name: 'ActiveStorageBlob'
  belongs_to :record, polymorphic: true
end

ActiveStorageAttachment.find_each do |attachment|
  name = attachment.name

  source = attachment.record.send(name).path
  dest_dir = File.join(
    "storage",
    attachment.blob.key.first(2),
    attachment.blob.key.first(4).last(2))
  dest = File.join(dest_dir, attachment.blob.key)

  FileUtils.mkdir_p(dest_dir)
  puts "Moving #{source} to #{dest}"
  FileUtils.cp(source, dest)
end
```

## Actualiza tus pruebas

En lugar de utilizar `have_attached_file`, será necesario que escribas tu propio
matcher. Aquí hay un matcher similar _en espíritu_ al que Paperclip provee:


```ruby
RSpec::Matchers.define :have_attached_file do |name|
  matches do |record|
    file = record.send(name)
    file.respond_to?(:variant) && file.respond_to?(:attach)
  end
end
```

## Actualiza tus vistas

En Paperclip se ven así:

```ruby
image_tag @user.avatar.url(:medium)
```

En ActiveStorage se ven así:

```ruby
image_tag @user.avatar.variant(resize: "250x250")
```

## Actualiza tus controladores

Esto no debería _requerir_ ningúna actualización. Sin embargo, si te fijas en
el schema de tu base de datos, notaras un join.

Por ejemplo si tu controlador tiene:

```ruby
def index
  @users = User.all.order(:name)
end
```

Y tu vista tiene:

```
<ul>
  <% @users.each do |user| %>
    <li><%= image_tag user.avatar.variant(resize: "10x10"), alt: user.name %></li>
  <% end %>
</ul>
```

Vas a terminar con un n+1, ya que descargas cada archivo adjunto dentro del
bucle.

Así que mientras que el controlador y el modelo funcionarán sin ningún cambio,
tal vez quieras revisar dos veces tus bucles y agregar `includes` en dónde haga
falta.

ActiveStorage agrega `avatar_attachment` y `avatar_blob` a las relaciones del
tipo `has-one`, así como `avatar_attachments` y `avatar_blobs` a las relaciones
de tipo `has-many`:

```ruby
def index
  @users = User.all.order(:name).includes(:avatar_attachment)
end
```

## Actualiza tus modelos

Sigue [la guía sobre cómo adjuntar archivos a los registros]. Por ejemplo, un
`User` con un `avatar` se representa como:

```ruby
class User < ApplicationRecord
  has_one_attached :avatar
end
```

Cualquier cambio de tamaño se hace en la vista como un `variant`.

[la guía sobre cómo adjuntar archivos a los registros]: http://edgeguides.rubyonrails.org/active_storage_overview.html#attaching-files-to-records

## Quita Paperclip

Quita la gema de tu `Gemfile` y corre `bundle`. Corre tus pruebas porque ya
terminaste!


================================================
FILE: MIGRATING.md
================================================
# Migrating from Paperclip to ActiveStorage

* [Video presentation](https://www.youtube.com/watch?v=tZ_WNUytO9o).
* [En español](https://github.com/thoughtbot/paperclip/blob/master/MIGRATING-ES.md).

Paperclip and ActiveStorage solve similar problems with similar solutions, so
transitioning from one to the other is straightforward data re-writing.

The process of going from Paperclip to ActiveStorage is as follows:

1. Apply the ActiveStorage database migrations.
2. Configure storage.
3. Copy the database data over.
4. Copy the files over.
5. Update your tests.
6. Update your views.
7. Update your controllers.
8. Update your models.

## Apply the ActiveStorage database migrations

You'll very likely want to add the `mini_magick` gem to your Gemfile.

Make sure your `config/application.rb` requires the ActiveStorage engine:

```rb
# config/application.rb
require "active_storage/engine"
```

Then, follow [the instructions for installing ActiveStorage].


```sh
rails active_storage:install
```

[the instructions for installing ActiveStorage]: https://github.com/rails/rails/tree/5-2-stable/activestorage#installation

## Configure storage

Again, follow [the instructions for configuring ActiveStorage].

It's worth highlighting that, by default, ActiveStorage's
[`DiskService`][active-storage-service] will store files locally in
`Rails.root.join("storage")`. When storing files locally, Paperclip, by default,
writes to `Rails.root.join("public", "system")`.

Make sure to exclude your locally stored files from version control.

For instance, if you're using Git, add `storage/` to your `.gitignore`.

```diff
  !.keep
  /.bundle
  /.byebug_history
  /.tmp/*
  /log/*
  /public/system/
+ storage/
```

[the instructions for configuring ActiveStorage]: https://guides.rubyonrails.org/v5.2/active_storage_overview.html#setup
[active-storage-service]: https://api.rubyonrails.org/v5.2/classes/ActiveStorage/Service.html

## Copy the database data over

The `active_storage_blobs` and `active_storage_attachments` tables are where
ActiveStorage expects to find file metadata. Paperclip stores the file metadata
directly on the associated object's table.

You'll need to write a migration for this conversion. Because the models for
your domain are involved, it's tricky to supply a simple script. But we'll try!

Here's how it would go for a `User` with an `avatar`, that is this in
Paperclip:

```ruby
class User < ApplicationRecord
  has_attached_file :avatar
end
```

Your Paperclip migrations will produce a table like so:

```ruby
create_table "users", force: :cascade do |t|
  t.string "avatar_file_name"
  t.string "avatar_content_type"
  t.integer "avatar_file_size"
  t.datetime "avatar_updated_at"
end
```

And you'll be converting into these tables:

```ruby
create_table "active_storage_attachments", force: :cascade do |t|
  t.string "name", null: false
  t.string "record_type", null: false
  t.integer "record_id", null: false
  t.integer "blob_id", null: false
  t.datetime "created_at", null: false
  t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
  t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
end
```

```ruby
create_table "active_storage_blobs", force: :cascade do |t|
  t.string "key", null: false
  t.string "filename", null: false
  t.string "content_type"
  t.text "metadata"
  t.bigint "byte_size", null: false
  t.string "checksum", null: false
  t.datetime "created_at", null: false
  t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
end
```

So, assuming you want to leave the files in the exact same place, _this is
your migration_. Otherwise, see the next section first and modify the migration
to taste.

```ruby
class ConvertToActiveStorage < ActiveRecord::Migration[5.2]
  require 'open-uri'

  def up
    # postgres
    get_blob_id = 'LASTVAL()'
    # mariadb
    # get_blob_id = 'LAST_INSERT_ID()'
    # sqlite
    # get_blob_id = 'LAST_INSERT_ROWID()'

    active_storage_blob_statement = ActiveRecord::Base.connection.raw_connection.prepare('active_storage_blob_statement', <<-SQL)
      INSERT INTO active_storage_blobs (
        `key`, filename, content_type, metadata, byte_size, checksum, created_at
      ) VALUES ($1, $2, $3, '{}', $4, $5, $6)
    SQL

    active_storage_attachment_statement = ActiveRecord::Base.connection.raw_connection.prepare('active_storage_attachment_statement', <<-SQL)
      INSERT INTO active_storage_attachments (
        name, record_type, record_id, blob_id, created_at
      ) VALUES ($1, $2, $3, #{get_blob_id}, $4)
    SQL

    Rails.application.eager_load!
    models = ActiveRecord::Base.descendants.reject(&:abstract_class?)

    transaction do
      models.each do |model|
        attachments = model.column_names.map do |c|
          if c =~ /(.+)_file_name$/
            $1
          end
        end.compact

        if attachments.blank?
          next
        end

        model.find_each.each do |instance|
          attachments.each do |attachment|
            if instance.send(attachment).path.blank?
              next
            end

            ActiveRecord::Base.connection.execute_prepared(
              'active_storage_blob_statement', [
                key(instance, attachment),
                instance.send("#{attachment}_file_name"),
                instance.send("#{attachment}_content_type"),
                instance.send("#{attachment}_file_size"),
                checksum(instance.send(attachment)),
                instance.updated_at.iso8601
              ])

            ActiveRecord::Base.connection.execute_prepared(
              'active_storage_attachment_statement', [
                attachment,
                model.name,
                instance.id,
                instance.updated_at.iso8601,
              ])
          end
        end
      end
    end
  end

  def down
    raise ActiveRecord::IrreversibleMigration
  end

  private

  def key(instance, attachment)
    SecureRandom.uuid
    # Alternatively:
    # instance.send("#{attachment}_file_name")
  end

  def checksum(attachment)
    # local files stored on disk:
    url = attachment.path
    Digest::MD5.base64digest(File.read(url))

    # remote files stored on another person's computer:
    # url = attachment.url
    # Digest::MD5.base64digest(Net::HTTP.get(URI(url)))
  end
end
```

## Copy the files over

The above migration leaves the files as they are. However, the default
Paperclip and ActiveStorage storage services use different locations.

By default, Paperclip looks like this:

```
public/system/users/avatars/000/000/004/original/the-mystery-of-life.png
```

And ActiveStorage looks like this:

```
storage/xM/RX/xMRXuT6nqpoiConJFQJFt6c9
```

That `xMRXuT6nqpoiConJFQJFt6c9` is the `active_storage_blobs.key` value. In the
migration above we simply used the filename but you may wish to use a UUID
instead.


### Moving local storage files

```ruby
#!bin/rails runner

ActiveStorage::Attachment.find_each do |attachment|
  name = attachment.name

  source = attachment.record.send(name).path
  dest_dir = File.join(
    "storage",
    attachment.blob.key.first(2),
    attachment.blob.key.first(4).last(2))
  dest = File.join(dest_dir, attachment.blob.key)

  FileUtils.mkdir_p(dest_dir)
  puts "Moving #{source} to #{dest}"
  FileUtils.cp(source, dest)
end
```

### Moving files on a remote host (S3, Azure Storage, GCS, etc.)

One of the most straightforward ways to move assets stored on a remote host is
to use a rake task that regenerates the file names and places them in the
proper file structure/hierarchy.

Assuming you have a model configured similarly to the example below:

```ruby
class Organization < ApplicationRecord
  # New ActiveStorage declaration
  has_one_attached :logo

  # Old Paperclip config
  # must be removed BEFORE to running the rake task so that
  # all of the new ActiveStorage goodness can be used when
  # calling organization.logo
  has_attached_file :logo,
                    path: "/organizations/:id/:basename_:style.:extension",
                    default_url: "https://s3.amazonaws.com/xxxxx/organizations/missing_:style.jpg",
                    default_style: :normal,
                    styles: { thumb: "64x64#", normal: "400x400>" },
                    convert_options: { thumb: "-quality 100 -strip", normal: "-quality 75 -strip" }
end
```

The following rake task would migrate all of your assets:

```ruby
namespace :organizations do
  task migrate_to_active_storage: :environment do
    Organization.where.not(logo_file_name: nil).find_each do |organization|
      # This step helps us catch any attachments we might have uploaded that
      # don't have an explicit file extension in the filename
      image = organization.logo_file_name
      ext = File.extname(image)
      image_original = CGI.unescape(image.gsub(ext, "_original#{ext}"))

      # this url pattern can be changed to reflect whatever service you use
      logo_url = "https://s3.amazonaws.com/xxxxx/organizations/#{organization.id}/#{image_original}"
      organization.logo.attach(io: open(logo_url),
                                   filename: organization.logo_file_name,
                                   content_type: organization.logo_content_type)
    end
  end
end
```

An added advantage of this method is that you're creating a copy of all assets,
which is handy in the event you need to rollback your deploy.

This also means that you can run the rake task from your development machine
and completely migrate the assets before your deploy, minimizing the chances
that you'll have a timed-out deployment.

The main drawback of this method is the same as its benefit - you are
essentially duplicating all of your assets. These days storage and bandwidth
are relatively cheap, but in some instances where you have a huge volume of
files, or very large file sizes, this might get a little less feasible.

In my experience I was able to move tens of thousands of images in a matter of
a couple of hours, just by running the migration overnight on my MacBook Pro.

Once you've confirmed that the migration and deploy have gone successfully you
can safely delete the old assets from your remote host.

## Update your tests

Instead of the `have_attached_file` matcher, you'll need to write your own.
Here's one that is similar in spirit to the Paperclip-supplied matcher:

```ruby
RSpec::Matchers.define :have_attached_file do |name|
  match do |record|
    file = record.send(name)
    file.respond_to?(:variant) && file.respond_to?(:attach)
  end
end
```

If you were using a Factory or a Fixture that set the Paperclip-generated
columns' values directly, you'll likely need to attach the Files instead.

For example, you could replace a `FactoryBot` factory definition's Paperclip
attributes with File I/O using
[`ActiveSupport::Testing::FixtureFiles#file_fixture`][file-fixture]:

```diff
factory :user do
  trait :with_avatar do
-    avatar_file_name { "avatar.jpg" }
-    avatar_file_type { "image/jpg" }
-    avatar_file_size { 1024 }
+   transient do
+     avatar_file { file_fixture("avatar.jpg") }
+
+     after :build do |user, evaluator|
+       user.avatar.attach(
+         io: evaluator.avatar_file.open,
+         filename: evaluator.avatar_file.basename.to_s,
+       )
+     end
+   end
  end
end
```

[file-fixture]: https://api.rubyonrails.org/v5.2/classes/ActiveSupport/Testing/FileFixtures.html

## Update your views

In Paperclip it looks like this:

```ruby
image_tag @user.avatar.url(:medium)
```

In ActiveStorage it looks like this:

```ruby
image_tag @user.avatar.variant(resize: "250x250")
```

## Update your controllers

This should _require_ no update. However, if you glance back at the database
schema above, you may notice a join.

For example, if your controller has

```ruby
def index
  @users = User.all.order(:name)
end
```

And your view has

```
<ul>
  <% @users.each do |user| %>
    <li><%= image_tag user.avatar.variant(resize: "10x10"), alt: user.name %></li>
  <% end %>
</ul>
```

Then you'll end up with an n+1 as you load each attachment in the loop.

So while the controller and model will work without change, you will want to
double-check your loops and add `includes` as needed.

ActiveStorage automatically declares `ActiveStorage::Attachment` and
`ActiveStorage::Blob` relationships to your models, along with eager-loading
scopes.

For example, a `has_one_attached :avatar` declaration will generate a `has_one
:avatar_attachment` relationship along with a
[`.with_attached_avatar`][has-one-eager-loading-scope] scope for eager loading
attachments and blobs.

A `has_many_attached :avatars` declaration will generate a `has_many
:avatar_attachments` relationship along with a
[`.with_attached_avatars`][has-many-eager-loading-scope] scope for eager loading
attachments and blobs.

When eager-loading transitive relationships, you'll need to specify the
relationship names directly, like `includes(avatar_attachment: :blob)` or
`includes(avatar_attachments: :blob)`:

```ruby
def index
  @users = User.all.order(:name).includes(avatar_attachment: :blob)
end
```

[has-one-eager-loading-scope]: https://api.rubyonrails.org/v5.2/classes/ActiveStorage/Attached/Macros.html#method-i-has_one_attached
[has-many-eager-loading-scope]: https://api.rubyonrails.org/v5.2/classes/ActiveStorage/Attached/Macros.html#method-i-has_many_attached

## Update your models

Follow [the guide on attaching files to records]. For example, a `User` with an
`avatar` is represented as:

```ruby
class User < ApplicationRecord
  has_one_attached :avatar
end
```

Any resizing is done in the view as a variant.

[the guide on attaching files to records]: https://guides.rubyonrails.org/v5.2/active_storage_overview.html#attaching-files-to-records

### Validations

Unlike Paperclip, [which shipped with built-in attachment
validations][paperclip-validations], ActiveStorage does not have built-in support
for validating an attachment's content type or file size (which can be useful for
[preventing content type spoofing][security-validations]).

There are alternatives that support some of Paperclip's file validations.

For instance, here are some changes you could make to migrate a
Paperclip-enabled model to use validations provided by the [`file_validators`
gem][file-validators]:


```diff
class User < ApplicationRecord
  # ...

-  validates_attachment_content_type :avatar, content_type: /\Aimage/
-  validates_attachment_file_name :avatar, matches: /jpe?g\z/
+  validates :avatar, file_content_type: {
+    allow: ["image/jpeg", "image/png"],
+    if: -> { avatar.attached? },
+  }
```

[paperclip-validations]: https://github.com/thoughtbot/paperclip/tree/v6.1.0#validations
[security-validations]: https://github.com/thoughtbot/paperclip/tree/v6.1.0#security-validations
[file-validators]: https://github.com/musaffa/file_validators/tree/v2.3.0#examples

## Remove Paperclip

Make sure to delete any files Paperclip was storing locally. You can also update
your version control to no longer ignore the directory.

For instance, if you're using Git, remove `public/system/` from your
`.gitignore`.

```diff
  !.keep
  /.bundle
  /.byebug_history
  /.tmp/*
  /log/*
- /public/system/
  storage/
```

Remove the Gem from your `Gemfile` and run `bundle`. Run your tests because
you're done!


================================================
FILE: NEWS
================================================
6.1.0 (2018-07-27):

* BUGFIX: Don't double-encode URLs (Roderick Monje).
* BUGFIX: Only use the content_type when it exists (Jean-Philippe Doyle).
* STABILITY: Better handling of the content-disposition header. Now supports
  file name that is either enclosed or not in double quotes and is case
  insensitive as per RC6266 grammar (Hasan Kumar, Yves Riel).
* STABILITY: Change database column type of attachment file size from unsigned 4-byte
  `integer` to unsigned 8-byte `bigint`. The former type limits attachment size
  to just over 2GB, which can easily be exceeded by a large video file (Laurent
  Arnoud, Alen Zamanyan).
* STABILITY: Better error message when thumbnail processing errors (Hayden Ball).
* STABILITY: Fix file linking issues around Windows (Akihiko Odaki).
* STABILITY: Files without an extension will now be checked for spoofing attempts
  (George Walters II).
* STABILITY: Manually close Tempfiles when we are done with them (Erkki Eilonen).

6.0.0 (2018-03-09):

* Improvement: Depend only on `aws-sdk-s3` instead of `aws-sdk` (https://github.com/thoughtbot/paperclip/pull/2481)

5.3.0 (2018-03-09):

* Improvement: Use `FactoryBot` instead of `FactoryGirl` (https://github.com/thoughtbot/paperclip/pull/2501)
* Improvement: README updates (https://github.com/thoughtbot/paperclip/pull/2411, https://github.com/thoughtbot/paperclip/pull/2433, https://github.com/thoughtbot/paperclip/pull/2374, https://github.com/thoughtbot/paperclip/pull/2417, https://github.com/thoughtbot/paperclip/pull/2536)
* Improvement: Remove Ruby 2.4 deprecation warning (https://github.com/thoughtbot/paperclip/pull/2401)
* Improvement: Rails 5 migration compatibility (https://github.com/thoughtbot/paperclip/pull/2470)
* Improvement: Documentation around post processing (https://github.com/thoughtbot/paperclip/pull/2381)
* Improvement: S3 hostname example documentation (https://github.com/thoughtbot/paperclip/pull/2379)
* Bugfix: Allow paperclip to load in IRB (https://github.com/thoughtbot/paperclip/pull/2369)
* Bugfix: MIME type detection (https://github.com/thoughtbot/paperclip/issues/2527)
* Bugfix: Bad tempfile state after symlink failure (https://github.com/thoughtbot/paperclip/pull/2540)
* Bugfix: Rewind file after Fog bucket creation (https://github.com/thoughtbot/paperclip/pull/2572)
* Improvement: Use `Terrapin` instead of `Cocaine` (https://github.com/thoughtbot/paperclip/pull/2553)

5.2.1 (2018-01-25):

* Bugfix: Fix copying files on Windows. (#2532)

5.2.0 (2018-01-23):

* Security: Remove the automatic loading of URI adapters. Some of these
  adapters can be specially crafted to expose your network topology. (#2435)
* Bugfix: The rake task no longer rescues `Exception`. (#2476)
* Bugfix: Handle malformed `Content-Disposition` headers (#2283)
* Bugfix: The `:only_process` option works when passed a lambda again. (#2289)
* Improvement: Added `:use_accelerate_endpoint` option when using S3 to enable
  [Amazon S3 Transfer Acceleration](http://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html)
  (#2291)
* Improvement: Make the fingerprint digest configurable per attachment. The
  default remains MD5. Making this configurable means it can change in a future
  version because it is not considered secure anymore against intentional file
  corruption. For more info, see https://en.wikipedia.org/wiki/MD5#Security

  You can change the digest used for an attachment by adding the
  `:adapter_options` parameter to the `has_attached_file` options like this:
  `has_attached_file :avatar, adapter_options: { hash_digest: Digest::SHA256 }`

  Use the rake task to regenerate fingerprints with the new digest for a given
  class. Note that this does **not** check the file integrity using the old
  fingerprint. Run the following command to regenerate fingerprints for all
  User attachments:
  `CLASS=User rake paperclip:refresh:fingerprints`
  You can optionally limit the attachment that will be processed, e.g:
  `CLASS=User ATTACHMENT=avatar rake paperclip:refresh:fingerprints` (#2229)
* Improvement: The new `frame_index` option on the thumbnail processor allows
  you to select a specific frame from an animated upload to use as a thumbnail.
  Initial support is for mkv, avi, MP4, mov, MPEG, and GIF. (#2155)
* Improvement: Instead of copying files, use hard links. This is an
  optimization. (#2120)
* Improvement: S3 storage option `:s3_prefixes_in_alias`. (#2287)
* Improvement: Fog option `:fog_public` can be a lambda. (#2302)
* Improvement: One fewer warning on JRuby. (#2352)
* Ruby 2.4.0 compatibility (doesn't use Fixnum anymore)

5.1.0 (2016-08-19):

* Add default `content_type_detector` to `UploadedFileAdapter` (#2270)
* Default S3 protocol to empty string (#2038)
* Don't write original file if it wasn't reprocessed (#1993)
* Disallow trailing newlines in regular expressions (#2266)
* Support for readbyte in Paperclip attachments (#2034)
* (port from 4.3) Uri io adapter uses the content-disposition filename (#2250)
* General refactors and documentation improvements

5.0.0 (2016-07-01):

* Improvement: Add `read_timeout` configuration for URI Adapter download_content method.
* README adjustments for Ruby beginners (add links, elucidate model in Quick Start)
* Bugfix: Now it's possible to save images from URLs with special characters [#1932]
* Bugfix: Return false when file to copy is not present in cloud storage [#2173]
* Automatically close file while checking mime type [#2016]
* Add `read_timeout` option to `UriAdapter#download_content` method [#2232]
* Fix a nil error in content type validation matcher [#1910]
* Documentation improvements

5.0.0.beta2 (2016-04-01):

* Bugfix: Dynamic fog directory option is now respected
* Bugfix: Fixes cocaine duplicated paths [#2169]
* Removal of dead code (older versions of Rails and AWS SDK)
* README adjustments

5.0.0.beta1 (2016-03-13):

* Bug Fix: megabytes of mime-types info in logs when a spoofed media type is detected.
* Drop support to end-of-life'd ruby 2.0.
* Drop support for end-of-life'd Rails 3.2 and 4.1
* Drop support for AWS v1
* Remove tests for JRuby and Rubinius from Travis CI (they were failing)
* Improvement: Add `fog_options` configuration to send options to fog when
  storing files.
* Extracted repository for locales only:  https://github.com/thoughtbot/paperclip-i18n
* Bugfix: Original file could be unlinked during `post_process_style`, producing failures
* Bugfix for image magick scaling images up
* Memory consumption improvements
* `url` on a unpersisted record returns `default_url` rather than `nil`
* Improvement: aws-sdk v2 support
  https://github.com/thoughtbot/paperclip/pull/1903

  If your Gemfile contains aws-sdk (>= 2.0.0) and aws-sdk-v1, paperclip will use
  aws-sdk v2. With aws-sdk v2, S3 storage requires you to set the s3_region.
  s3_region may be nested in s3_credentials, and (if not nested in
  s3_credentials) it may be a Proc.

4.3

See patch versions in v4.3 NEWS:
https://github.com/thoughtbot/paperclip/blob/v4.3/NEWS

4.3.0 (2015-06-18):

* Improvement: Update aws-sdk and cucumber gem versions.
* Improvement: Add `length` alias for `size` method in AbstractAdapter.
* Improvement: Removed some cruft
* Improvement: deep_merge! Attachment definitions
* Improvement: Switch to mimemagic gem for content-type detection
* Improvement: Allows multiple content types for spoof detector
* Bug Fix: Don't assume we have Rails.env if we have Rails
* Performance: Decrease Memory footprint
* Ruby Versioning: Drop support for 1.9.3 (EOL'ed)
* Rails Versioning: Drop support for 4.0.0 (EOL'ed)

4.2.4 (2015-06-05):

* Rollback backwards incompatible change, allowing paperclip to run on
  Ruby >= 1.9.2.

4.2.3:

* Fix dependency specifications (didn't work with Rails 4.1)
* Fix paperclip tests in CI

4.2.2:

* Security fix: Fix a potential security issue with spoofing

4.2.1:

* Improvement: Added `validate_media_type` options to allow/bypass spoof check
* Improvement: Added incremental backoff when AWS gives us a SlowDown error.
* Improvement: Stream downloads when usign aws-sdk.
* Improvement: Documentation fixes, includes Windows instructions.
* Improvement: Added pt-BR, zh-HK, zh-CN, zh-TW, and ja-JP locales.
* Improvement: Better escaping for characters in URLs
* Improvement: Honor `fog_credentials[:scheme]`
* Improvement: Also look for custom processors in lib/paperclip
* Improvement: id partitioning for string IDs works like integer id
* Improvement: Can pass options to DB adapters in migrations
* Improvement: Update expiring_url creation for later versions of fog
* Improvement: `path` can be a Proc in S3 attachments
* Test Fix: Improves speed and reliability of the specs
* Bug Fix: #original_filename= does not error when passed `nil`

4.2.0:

* Improvement: Converted test suite from test/unit to RSpec
* Improvement: Refactored Paperclip::Attachment#assign
* Improvement: Added Spanish and German locales
* Improvement: Required Validators accept validator subclasses
* Improvement: EXIF orientation checking can be turned off for performance
* Improvement: Documentation updates
* Improvement: Better #human_size method for AttachmentSizeValidators
* Bug Fix: Allow MIME-types with dots in them
* Improvement: Travis CI updates
* Improvement: Validators can take multiple messages
* Improvement: Per-style options for S3 storage
* Improvement: Allow `nil` geometry strings
* Improvement: Use `eager_load!`

4.1.1:

* Improvement: Add default translations for spoof validation
* Bug Fix: Don't check for spoofs if the file hasn't changed
* Bug Fix: Callback chain terminator is different in Rails 4.1, remove warnings
* Improvement: Fixed various Ruby warnings
* Bug Fix: Give bundler a hint, so it doesn't run forever on a fresh bundle
* Improvement: Documentation fixes
* Improvement: Allow travis-ci to finish-fast


4.1.0:

* Improvement: Add :content_type_mappings to correct for missing spoof types
* Improvement: Credit Egor Homakov with discovering the content_type spoof bug
* Improvement: Memoize calls to identify in the thumbnail processor
* Improvement: Make MIME type optional for Data URIs.
* Improvement: Add default format for styles

4.0.0:

* Security: Attachments are checked to make sure they're not pulling a fast one.
* Security: It is now *enforced* that every attachment has a file/mime validation.
* Bug Fix: Removed a call to IOAdapter#close that was causing issues.
* Improvement: Added bullets to the 3.5.3 list of changes. Very important.
* Improvement: Updated the copyright to 2014

3.5.3:

* Improvement: After three long, hard years... we know how to upgrade
* Bug Fix: #expiring_url returns 'missing' urls if nothing is attached
* Improvement: Lots of documentation fixes
* Improvement: Lots of fixes for Ruby warnings
* Improvement: Test the most appropriate Ruby/Rails comobinations on Travis
* Improvement: Delegate more IO methods through IOAdapters
* Improvement: Remove Rails 4 deprecations
* Improvement: Both S3's and Fog's #expiring_url can take a Time or Int
* Bug Fix: Both S3's and Fog's expiring_url respect style when missing the file
* Bug Fix: Timefiles will have a reasonable-length name. They're all MD5 hashes now
* Bug Fix: Don't delete files off S3 when reprocessing due to AWS inconsistencies
* Bug Fix: "swallow_stream" isn't thread dafe. Use :swallow_stderr
* Improvement: Regexps use \A and \Z instead of ^ and $
* Improvement: :s3_credentials can take a lambda as an argument
* Improvement: Search up the class heirarchy for attachments
* Improvement: deep_merge options instead of regular merge
* Bug Fix: Prevent file deletion on transaction rollback
* Test Improvement: Ensure more files are properly closed during tests
* Test Bug Fix: Return the gemfile's syntax to normal

3.5.2:

* Security: Force cocaine to at least 0.5.3 to include a security fix
* Improvement: Fixed some README exmaples
* Feature: Added HTTP URL Proxy Adapter, can assign string URLs as attachments
* Improvement: Put validation errors on the base attribute and the sub-attribute

3.5.1:

* Bug Fix: Returned the class-level `attachment_definitions` method for compatability.
* Improvement: Ensured compatability with Rails 4
* Improvement: Added Rails 4 to the Appraisals
* Bug Fix: #1296, where validations were generating errors
* Improvement: Specify MIT license in the gemspec

3.5.0:

* Feature: Handle Base64-encoded data URIs as uploads
* Feature: Add a FilenameCleaner class to allow custom filename sanitation
* Improvement: Satisfied Mocha deprecation warnings
* Bug Fix: Allow empty string to be submitted and ignored, as some forms do this
* Improvement: Make #expiring_url behavior consistent with #url
* Bug Fix: "Validate" attachments without invoking AR's validations
* Improvement: Various refactorings for a cleaner codebase
* Improvement: Be agnostic, use ActiveModel when appropriate
* Improvement: Add validation errors to the base attachment attribute
* Improvement: Handle errors in rake tasks
* Improvement: Largely refactor has_attached_file into a new class
* Improvement: Added Ruby 2.0.0 as a supported platform and removed 1.8.7
* Improvement: Fixed some incompatabilities in the test suite

3.4.2:

* Improvement: Use https for Gemfile urls
* Improvement: Updated and more correct documentation
* Improvement: Use the -optimize flag on animated GIFs
* Improvement: Remove the Gemfile.lock
* Improvement: Add #expiring_url as an alias for #url until the storage defines it
* Improvement: Remove path clash checking, as it's unnecessary
* Bug Fix: Do not rely on checking version numbers for aws-sdk

3.4.1:

* Improvement: Various documentation fixes and improvements
* Bug Fix: Clearing an attachment with `preserve_files` on should still clear the attachment
* Bug Fix: Instances are #changed? when a new file is assigned
* Bug Fix: Correctly deal with S3 styles when using a lambda
* Improvement: Accept and pass :credential_provider option to AWS-SDK
* Bug Fix: Sanitize original_filename more correctly in IO Adapters
* Improvement: s3_host_name can be a lambda
* Improvement: Cache some interpolations for speed
* Improvement: Update to latest cocaine
* Improvement: Update copyrights, various typos

3.4.0:

* Bug Fix: Allow UploadedFileAdapter to force the use of `file`
* Bug Fix: Close the file handle when dealing with URIs
* Bug Fix: Ensure files are closed for writing when we're done.
* Bug Fix: Fixed 'type' being nil on Windows 7 error.
* Bug Fix: Fixed nil access when no s3 headers are defined
* Bug Fix: Fixes auto_orientation
* Bug Fix: Prevent a missing method error when switching from aws_sdk to fog
* Bug Fix: Properly fail to process invalid attachments
* Bug Fix: Server-side encryption is specified correctly
* Bug Fix: fog_public returned to true by default
* Bug Fix: Check attachment paths for duplicates, not URLs
* Feature: Add Attachment#blank?
* Feature: Add support for blacklisting certain content_types
* Feature: Add support for style-specific s3 headers and meta data
* Feature: Allow only_process to be a lambda
* Feature: Allow setting of escape url as a default option
* Feature: Create :override_file_permissions option for filesystem attachments
* Improvement: Add Attachment#as_json
* Improvement: Evaluate lambdas for fog_file properties
* Improvement: Extract geometry parsing into factories
* Improvement: Fixed various typos
* Improvement: Refactored some tests
* Improvement: Reuse S3 connections

New In 3.3.1:

* Bug Fix: Moved Filesystem's copy_to_local_file to the right place.

3.3.0:

* Improvement: Upgrade cocaine to 0.4

3.2.0:

* Bug Fix: Use the new correct Amazon S3 encryption header.
* Bug Fix: The rake task respects the updated_at column.
* Bug Fix: Strip newline from content type.
* Feature: Fog file visibility can be specified per style.
* Feature: Automatically rotate images.
* Feature: Reduce class-oriented programming of the attachment definitions.

3.1.4:

* Bug Fix: Allow user to be able to set path without `:style` attribute and not raising an error.
  This is a regression introduced in 3.1.3, and that feature will be postponed to another minor
  release instead.
* Feature: Allow for URI Adapter as an optional paperclip io adapter.

3.1.3:

* Bug Fix: Copy empty attachment between instances is now working.
* Bug Fix: Correctly rescue Fog error.
* Bug Fix: Using default path and url options in Fog storage now work as expected.
* Bug Fix: `Attachment#s3_protocol` now returns a protocol without colon suffix.
* Feature: Paperclip will now raise an error if multiple styles are defined but no `:style`
  interpolation exists in `:path`.
* Feature: Add support for `#{attachment}_created_at` field
* Bug Fix: Paperclip now gracefully handles msising file command.
* Bug Fix: `StringIOAdapter` now accepts content type.

3.1.2:

* Bug Fix: #remove_attachment on 3.1.0 and 3.1.1 mistakenly trying to remove the column that has
  the same name as data type (such as :string, :datetime, :interger.) You're advised to update to
  Paperclip 3.1.2 as soon as possible.

3.1.1:

* Bug Fix: Paperclip will only load Paperclip::Schema only when Active Record is available.

3.1.0:

* Feature: Paperclip now support new migration syntax (sexy migration) that reads better:

      class AddAttachmentToUsers < ActiveRecord::Migration
        def self.up
          create_table :users do |t|
            t.attachment :avatar
          end
        end
      end

  Also, schema-definition level syntax has been added:

      add_attachment :users, :avatar
      remove_attachment :users, :avatar

* Feature: Migration now support Rails 3.2+ `change` method.
* API CHANGE: Old `t.has_attached_file` and `drop_attached_file` are now deprecated. You're advised
  to update your migration file before the next MAJOR version.
* Bug Fix: Tempfile now rewinded before generating fingerprint
* API CHANGE: Tempfiles are now unlinked after `after_flush_writes`

  If you need to interact with the generated tempfiles, please define an `after_flush_writes` method
  in your model. You'll be able to access files via `@queue_for_write` instance variable.

* Bug Fix: `:s3_protocol` can now be defined as either String or Symbol
* Bug Fix: Tempfiles are now rewinded before get passed into `after_flush_writes`
* Feature: Added expiring_url method to Fog Storage
* API CHANGE: Paperclip now tested against AWS::SDK 1.5.2 onward
* Bug Fix: Improved the output of the content_type validator so the actual failure is displayed
* Feature: Animated formats now identified using ImageMagick.
* Feature: AttachmentAdapter now support fetching attachment with specific style.
* Feature: Paperclip default options can now be configured in Rails.configuration.
* Feature: add Geometry#resize_to to calculate dimensions of new source.
* Bug Fix: Fixed a bug whereby a file type with multiple mime types but no official type would cause
  the best_content_type to throw an error on trying nil.content_type.
* Bug Fix: Fix problem when the gem cannot be installed on the system that has Asepsis installed.

3.0.4:

* Feature: Adds support for S3 scheme-less URL generation.

3.0.3:

* Bug Fix: ThumbnailProcessor now correctly detects and preserve animated GIF.
* Bug Fix: File extension is now preserved in generated Tempfile from adapter.
* Bug Fix: Uploading file with unicode file name now won't raise an error when
  logging in the AWS is turned on.
* Bug Fix: Task "paperclip:refresh:missing_styles" now work correctly.
* Bug Fix: Handle the case when :restricted_characters is nil.
* Bug Fix: Don't delete all the existing styles if we reprocess.
* Bug Fix: Content type is now ensured to not having a new line character.
* API CHANGE: Non-Rails usage should include Paperclip::Glue directly.

  `Paperclip::Railtie` was intended to be used with Ruby on Rails only. If you're
  using Paperclip without Rails, you should include `Paperclip::Glue` into
  `ActiveRecord::Base` instead of requiring `paperclip/railtie`:

      ActiveRecord::Base.send :include, Paperclip::Glue

* Bug Fix: AttachmentContentTypeValidator now allow you to specify :allow_blank/:allow_nil
* Bug Fix: Make sure content type always a String.
* Bug Fix: Fix attachment.reprocess! when using storage providers fog and s3.
* Bug Fix: Fix a problem with incorrect content_type detected with 'file' command for an empty file on Mac.

3.0.2:

* API CHANGE: Generated migration class name is now plural (AddAttachmentToUsers instead of AddAttachmentToUser)
* API CHANGE: Remove Rails plugin initialization code.
* API CHANGE: Explicitly require Ruby 1.9.2 in the Gemfile.
* Bug Fix: Fixes AWS::S3::Errors::RequestTimeout on Model#save.
* Bug Fix: Fix a problem when there's no logger specified.
* Bug Fix: Fix a problem when attaching Rack::Test::UploadedFile instance.

3.0.1:

* Feature: Introduce Paperlip IO adapter.
* Bug Fix: Regression in AttachmentContentTypeValidator has been fixed.
* API CHANGE: #to_file has been removed. Use the #copy_to_local_file method instead.

3.0.0:

* API CHANGE: Paperclip now requires at least Ruby on Rails version 3.0.0
* API CHANGE: The default :url and :path have changed. The new scheme avoids
  filesystem conflicts and scales to handle larger numbers of uploads.

  The easiest way to upgrade is to add an explicit :url and :path to your
  has_attached_file calls:

      has_attached_file :avatar,
        :path => ":rails_root/public/system/:attachment/:id/:style/:filename",
        :url => "/system/:attachment/:id/:style/:filename"

* Feature: Adding Rails 3 style validators, and adding `validates_attachment` method as a shorthand.
* Bug Fix: Paperclip's rake tasks now loading records in batch.
* Bug Fix: Attachment style name with leading number now not raising an error.
* Bug Fix: File given to S3 and Fog storage will now be rewinded after flush_write.
* Feature: You can now pass addional parameter to S3 expiring URL, such as :content_type.

2.7.0:

* Bug Fix: Checking the existence of a file on S3 handles all AWS errors.
* Bug Fix: Clear the fingerprint when removing an attachment.
* Bug Fix: Attachment size validation message reads more nicely now.
* Feature: Style names can be either symbols or strings.
* Compatibility: Support for ActiveSupport < 2.3.12.
* Compatibility: Support for Rails 3.2.

2.6.0:

* Bug Fix: Files are re-wound after reading.
* Feature: Remove Rails dependency from specs that need Paperclip.
* Feature: Validation matchers support conditionals.

2.5.2:

* Bug Fix: Can be installed on Windows.
* Feature: The Fog bucket name, authentication, and host can be determined at runtime via Proc.
* Feature: Special characters are replaced with underscores in #url and #path.

2.5.1:

* Feature: After we've computed the content type, pass it to Fog.
* Feature: S3 encryption with the new :s3_server_side_encryption option.
* Feature: Works without ActiveRecord, allowing for e.g. mongo backends.

2.5.0:

* Performance: Only connect to S3 when absolutely needed.
* Bug Fix: STI with cached classes respect new options.
* Bug Fix: conditional validations broke, and now work again.
* Feature: URL generation is now parameterized and can be changed with plugins or custom code.
* Feature: :convert_options and :source_file_options to control the ImageMagick processing.
* Performance: String geometry specifications now parse more quickly.
* Bug Fix: Handle files with question marks in the filename.
* Bug Fix: Don't raise an error when generating an expiring URL on an unassigned attachment.
* Bug Fix: The rake task runs over all instances of an ActiveRecord model, ignoring default scopes.
* Feature: DB migration has_attached_file and drop_attached_file methods.
* Bug Fix: Switch from AWS::S3 to AWS::SDK for the S3 backend.
* Bug Fix: URL generator uses '?' in the URL unless it already appears and there is no prior '='.
* Bug Fix: Always convert the content type to a string before stripping blanks.
* Feature: The :keep_old_files option preserves the files in storage even when the attachment is cleared or changed.
* Performance: Optimize Fog's public_url access by avoiding it when possible.
* Bug Fix: Avoid a runtime error when generating the ID partition for an unsaved attachment.
* Performance: Do not calculate the fingerprint if it is never persisted.
* Bug Fix: Process the :original style before all others, in case of a dependency.
* Feature: S3 headers can be set at runtime by passing a proc object as the value.
* Bug Fix: Generating missing attachment styles for a model which has had its attachment changed should not raise.
* Bug Fix: Do not collide with the built-in Ruby hashing method.


================================================
FILE: README.md
================================================
Paperclip
=========

# Deprecated

**[Paperclip is deprecated]**.

For new projects, we recommend Rails' own [ActiveStorage].

For existing projects, please consult and contribute to the migration guide,
available [in English], [en español], and as [a video] recorded at RailsConf
2019. You may also prefer [an alternative migration tutorial used by
Doorkeeper][].

Alternatively, for existing projects, [Kreeti] is maintaining [kt-paperclip],
an ongoing [fork of Paperclip].

We will leave the Issues open as a discussion forum _only_. We do _not_
guarantee a response from us in the Issues. All bug reports should go to
kt-paperclip.

We are no longer accepting pull requests _except_ pull requests against the
migration guide. All other pull requests will be closed without merging.

[Paperclip is deprecated]: https://robots.thoughtbot.com/closing-the-trombone
[ActiveStorage]: http://guides.rubyonrails.org/active_storage_overview.html
[in English]: https://github.com/thoughtbot/paperclip/blob/master/MIGRATING.md
[en español]: https://github.com/thoughtbot/paperclip/blob/master/MIGRATING-ES.md
[a video]: https://www.youtube.com/watch?v=tZ_WNUytO9o
[Kreeti]: https://www.kreeti.com/
[kt-paperclip]: https://rubygems.org/gems/kt-paperclip
[fork of Paperclip]: https://github.com/kreeti/kt-paperclip
[an alternative migration tutorial used by Doorkeeper]: https://www.tokyodev.com/2021/03/23/paperclip-activestorage/

# Existing documentation

## Documentation valid for `master` branch

Please check the documentation for the paperclip version you are using:
https://github.com/thoughtbot/paperclip/releases

---

[![Build Status](https://secure.travis-ci.org/thoughtbot/paperclip.svg?branch=master)](http://travis-ci.org/thoughtbot/paperclip)
[![Dependency Status](https://gemnasium.com/thoughtbot/paperclip.svg?travis)](https://gemnasium.com/thoughtbot/paperclip)
[![Code Climate](https://codeclimate.com/github/thoughtbot/paperclip.svg)](https://codeclimate.com/github/thoughtbot/paperclip)
[![Inline docs](http://inch-ci.org/github/thoughtbot/paperclip.svg)](http://inch-ci.org/github/thoughtbot/paperclip)
[![Security](https://hakiri.io/github/thoughtbot/paperclip/master.svg)](https://hakiri.io/github/thoughtbot/paperclip/master)

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

- [Requirements](#requirements)
  - [Ruby and Rails](#ruby-and-rails)
  - [Image Processor](#image-processor)
  - [`file`](#file)
- [Installation](#installation)
- [Quick Start](#quick-start)
  - [Models](#models)
  - [Migrations](#migrations)
  - [Edit and New Views](#edit-and-new-views)
  - [Edit and New Views with Simple Form](#edit-and-new-views-with-simple-form)
  - [Controller](#controller)
  - [View Helpers](#view-helpers)
  - [Checking a File Exists](#checking-a-file-exists)
  - [Deleting an Attachment](#deleting-an-attachment)
- [Usage](#usage)
- [Validations](#validations)
- [Internationalization (I18n)](#internationalization-i18n)
- [Security Validations](#security-validations)
- [Defaults](#defaults)
- [Migrations](#migrations-1)
  - [Add Attachment Column To A Table](#add-attachment-column-to-a-table)
  - [Schema Definition](#schema-definition)
  - [Vintage Syntax](#vintage-syntax)
- [Storage](#storage)
  - [Understanding Storage](#understanding-storage)
- [IO Adapters](#io-adapters)
- [Post Processing](#post-processing)
- [Custom Attachment Processors](#custom-attachment-processors)
- [Events](#events)
- [URI Obfuscation](#uri-obfuscation)
- [Checksum / Fingerprint](#checksum--fingerprint)
- [File Preservation for Soft-Delete](#file-preservation-for-soft-delete)
- [Dynamic Configuration](#dynamic-configuration)
  - [Dynamic Styles:](#dynamic-styles)
  - [Dynamic Processors:](#dynamic-processors)
- [Logging](#logging)
- [Deployment](#deployment)
  - [Attachment Styles](#attachment-styles)
- [Testing](#testing)
- [Contributing](#contributing)
- [License](#license)
- [About thoughtbot](#about-thoughtbot)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

Paperclip is intended as an easy file attachment library for ActiveRecord. The
intent behind it was to keep setup as easy as possible and to treat files as
much like other attributes as possible. This means they aren't saved to their
final locations on disk, nor are they deleted if set to nil, until
ActiveRecord::Base#save is called. It manages validations based on size and
presence, if required. It can transform its assigned image into thumbnails if
needed, and the prerequisites are as simple as installing ImageMagick (which,
for most modern Unix-based systems, is as easy as installing the right
packages). Attached files are saved to the filesystem and referenced in the
browser by an easily understandable specification, which has sensible and
useful defaults.

See the documentation for `has_attached_file` in [`Paperclip::ClassMethods`](http://www.rubydoc.info/gems/paperclip/Paperclip/ClassMethods) for
more detailed options.

The complete [RDoc](http://www.rubydoc.info/gems/paperclip) is online.

---

Requirements
------------

### Ruby and Rails

Paperclip now requires Ruby version **>= 2.1** and Rails version **>= 4.2**
(only if you're going to use Paperclip with Ruby on Rails).

### Image Processor

[ImageMagick](http://www.imagemagick.org) must be installed and Paperclip must have access to it. To ensure
that it does, on your command line, run `which convert` (one of the ImageMagick
utilities). This will give you the path where that utility is installed. For
example, it might return `/usr/local/bin/convert`.

Then, in your environment config file, let Paperclip know to look there by adding that
directory to its path.

In development mode, you might add this line to `config/environments/development.rb)`:

```ruby
Paperclip.options[:command_path] = "/usr/local/bin/"
```

If you're on Mac OS X, you'll want to run the following with [Homebrew](http://www.brew.sh):

    brew install imagemagick

If you are dealing with pdf uploads or running the test suite, you'll also need
to install GhostScript. On Mac OS X, you can also install that using Homebrew:

    brew install gs

If you are on Ubuntu (or any Debian base Linux distribution), you'll want to run
the following with apt-get:

    sudo apt-get install imagemagick -y

### `file`

The Unix [`file` command](https://en.wikipedia.org/wiki/File_(command)) is required for content-type checking.
This utility isn't available in Windows, but comes bundled with Ruby [Devkit](https://github.com/oneclick/rubyinstaller/wiki/Development-Kit),
so Windows users must make sure that the devkit is installed and added to the system `PATH`.

**Manual Installation**

If you're using Windows 7+ as a development environment, you may need to install the `file.exe` application manually. The `file spoofing` system in Paperclip 4+ relies on this; if you don't have it working, you'll receive `Validation failed: Upload file has an extension that does not match its contents.` errors.

To manually install, you should perform the following:

> **Download & install `file` from [this URL](http://gnuwin32.sourceforge.net/packages/file.htm)**

To test, you can use the image below:
![untitled](https://cloud.githubusercontent.com/assets/1104431/4524452/a1f8cce4-4d44-11e4-872e-17adb96f79c9.png)

Next, you need to integrate with your environment - preferably through the `PATH` variable, or by changing your `config/environments/development.rb` file

**PATH**

    1. Click "Start"
    2. On "Computer", right-click and select "Properties"
    3. In Properties, select "Advanced System Settings"
    4. Click the "Environment Variables" button
    5. Locate the "PATH" var - at the end, add the path to your newly installed `file.exe` (typically `C:\Program Files (x86)\GnuWin32\bin`)
    6. Restart any CMD shells you have open & see if it works

OR

**Environment**

    1. Open `config/environments/development.rb`
    2. Add the following line: `Paperclip.options[:command_path] = 'C:\Program Files (x86)\GnuWin32\bin'`
    3. Restart your Rails server

Either of these methods will give your Rails setup access to the `file.exe` functionality, thus providing the ability to check the contents of a file (fixing the spoofing problem)

---

Installation
------------

Paperclip is distributed as a gem, which is how it should be used in your app.

Include the gem in your Gemfile:

```ruby
gem "paperclip", "~> 6.0.0"
```

Or, if you want to get the latest, you can get master from the main paperclip repository:

```ruby
gem "paperclip", git: "git://github.com/thoughtbot/paperclip.git"
```

If you're trying to use features that don't seem to be in the latest released gem, but are
mentioned in this README, then you probably need to specify the master branch if you want to
use them. This README is probably ahead of the latest released version if you're reading it
on GitHub.

For Non-Rails usage:

```ruby
class ModuleName < ActiveRecord::Base
  include Paperclip::Glue
  ...
end
```

---

Quick Start
-----------

### Models

```ruby
class User < ActiveRecord::Base
  has_attached_file :avatar, styles: { medium: "300x300>", thumb: "100x100>" }, default_url: "/images/:style/missing.png"
  validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\z/
end
```

### Migrations


Assuming you have a `users` table, add an `avatar` column to the `users` table:
```ruby
class AddAvatarColumnsToUsers < ActiveRecord::Migration
  def up
    add_attachment :users, :avatar
  end

  def down
    remove_attachment :users, :avatar
  end
end
```

(Or you can use the Rails migration generator: `rails generate paperclip user avatar`)

### Edit and New Views
Make sure you have corresponding methods in your controller:
```erb
<%= form_for @user, url: users_path, html: { multipart: true } do |form| %>
  <%= form.file_field :avatar %>
  <%= form.submit %>
<% end %>
```

### Edit and New Views with [Simple Form](https://github.com/plataformatec/simple_form)

```erb
<%= simple_form_for @user, url: users_path do |form| %>
  <%= form.input :avatar, as: :file %>
  <%= form.submit %>
<% end %>
```

### Controller

```ruby
def create
  @user = User.create(user_params)
end

private

# Use strong_parameters for attribute whitelisting
# Be sure to update your create() and update() controller methods.

def user_params
  params.require(:user).permit(:avatar)
end
```

### View Helpers
Add these to the view where you want your images displayed:
```erb
<%= image_tag @user.avatar.url %>
<%= image_tag @user.avatar.url(:medium) %>
<%= image_tag @user.avatar.url(:thumb) %>
```

### Checking a File Exists

There are two methods for checking if a file exists:

- `file?` and `present?` checks if the `_file_name` field is populated
- `exists?` checks if the file exists (will perform a TCP connection if stored in the cloud)

Keep this in mind if you are checking if files are present in a loop. The first
version is significantly more performant, but has different semantics.

### Deleting an Attachment

Set the attribute to `nil` and save.

```ruby
@user.avatar = nil
@user.save
```
---

Usage
-----

The basics of Paperclip are quite simple: Declare that your model has an
attachment with the `has_attached_file` method, and give it a name.

Paperclip will wrap up to four attributes (all prefixed with that attachment's name,
so you can have multiple attachments per model if you wish) and give them a
friendly front end. These attributes are:

* `<attachment>_file_name`
* `<attachment>_file_size`
* `<attachment>_content_type`
* `<attachment>_updated_at`

By default, only `<attachment>_file_name` is required for Paperclip to operate.
You'll need to add `<attachment>_content_type` in case you want to use content type
validation.

More information about the options passed to `has_attached_file` is available in the
documentation of [`Paperclip::ClassMethods`](http://www.rubydoc.info/gems/paperclip/Paperclip/ClassMethods).

Validations
-----------

For validations, Paperclip introduces several validators to validate your attachment:

* `AttachmentContentTypeValidator`
* `AttachmentPresenceValidator`
* `AttachmentSizeValidator`

Example Usage:

```ruby
validates :avatar, attachment_presence: true
validates_with AttachmentPresenceValidator, attributes: :avatar
validates_with AttachmentSizeValidator, attributes: :avatar, less_than: 1.megabytes

```

Validators can also be defined using the old helper style:

* `validates_attachment_presence`
* `validates_attachment_content_type`
* `validates_attachment_size`

Example Usage:

```ruby
validates_attachment_presence :avatar
```

Lastly, you can also define multiple validations on a single attachment using `validates_attachment`:

```ruby
validates_attachment :avatar, presence: true,
  content_type: "image/jpeg",
  size: { in: 0..10.kilobytes }
```

_NOTE: Post-processing will not even **start** if the attachment is not valid
according to the validations. Your callbacks and processors will **only** be
called with valid attachments._

```ruby
class Message < ActiveRecord::Base
  has_attached_file :asset, styles: { thumb: "100x100#" }

  before_post_process :skip_for_audio

  def skip_for_audio
    ! %w(audio/ogg application/ogg).include?(asset_content_type)
  end
end
```

If you have other validations that depend on assignment order, the recommended
course of action is to prevent the assignment of the attachment until
afterwards, then assign manually:

```ruby
class Book < ActiveRecord::Base
  has_attached_file :document, styles: { thumbnail: "60x60#" }
  validates_attachment :document, content_type: "application/pdf"
  validates_something_else # Other validations that conflict with Paperclip's
end

class BooksController < ApplicationController
  def create
    @book = Book.new(book_params)
    @book.document = params[:book][:document]
    @book.save
    respond_with @book
  end

  private

  def book_params
    params.require(:book).permit(:title, :author)
  end
end
```

**A note on content_type validations and security**

You should ensure that you validate files to be only those MIME types you
explicitly want to support.  If you don't, you could be open to
<a href="https://www.owasp.org/index.php/Testing_for_Stored_Cross_site_scripting_(OWASP-DV-002)">XSS attacks</a>
if a user uploads a file with a malicious HTML payload.

If you're only interested in images, restrict your allowed content_types to
image-y ones:

```ruby
validates_attachment :avatar,
  content_type: ["image/jpeg", "image/gif", "image/png"]
```

`Paperclip::ContentTypeDetector` will attempt to match a file's extension to an
inferred content_type, regardless of the actual contents of the file.

---

Internationalization (I18n)
---------------------------

For using or adding locale files in different languages, check the project
https://github.com/thoughtbot/paperclip-i18n.

Security Validations
====================

Thanks to a report from [Egor Homakov](http://homakov.blogspot.com/) we have
taken steps to prevent people from spoofing Content-Types and getting data
you weren't expecting onto your server.

NOTE: Starting at version 4.0.0, all attachments are *required* to include a
content_type validation, a file_name validation, or to explicitly state that
they're not going to have either. *Paperclip will raise an error* if you do not
do this.

```ruby
class ActiveRecord::Base
  has_attached_file :avatar
  # Validate content type
  validates_attachment_content_type :avatar, content_type: /\Aimage/
  # Validate filename
  validates_attachment_file_name :avatar, matches: [/png\z/, /jpe?g\z/]
  # Explicitly do not validate
  do_not_validate_attachment_file_type :avatar
end
```

This keeps Paperclip secure-by-default, and will prevent people trying to mess
with your filesystem.

NOTE: Also starting at version 4.0.0, Paperclip has another validation that
cannot be turned off. This validation will prevent content type spoofing. That
is, uploading a PHP document (for example) as part of the EXIF tags of a
well-formed JPEG. This check is limited to the media type (the first part of the
MIME type, so, 'text' in `text/plain`). This will prevent HTML documents from
being uploaded as JPEGs, but will not prevent GIFs from being uploaded with a
`.jpg` extension. This validation will only add validation errors to the form. It
will not cause errors to be raised.

This can sometimes cause false validation errors in applications that use custom
file extensions. In these cases you may wish to add your custom extension to the
list of content type mappings by creating `config/initializers/paperclip.rb`:

```ruby
# Allow ".foo" as an extension for files with the MIME type "text/plain".
Paperclip.options[:content_type_mappings] = {
  foo: %w(text/plain)
}
```

---

Defaults
--------
Global defaults for all your Paperclip attachments can be defined by changing the Paperclip::Attachment.default_options Hash. This can be useful for setting your default storage settings per example so you won't have to define them in every `has_attached_file` definition.

If you're using Rails, you can define a Hash with default options in `config/application.rb` or in any of the `config/environments/*.rb` files on config.paperclip_defaults. These will get merged into `Paperclip::Attachment.default_options` as your Rails app boots. An example:

```ruby
module YourApp
  class Application < Rails::Application
    # Other code...

    config.paperclip_defaults = { storage: :fog, fog_credentials: { provider: "Local", local_root: "#{Rails.root}/public"}, fog_directory: "", fog_host: "localhost"}
  end
end
```

Another option is to directly modify the `Paperclip::Attachment.default_options` Hash - this method works for non-Rails applications or is an option if you prefer to place the Paperclip default settings in an initializer.

An example Rails initializer would look something like this:

```ruby
Paperclip::Attachment.default_options[:storage] = :fog
Paperclip::Attachment.default_options[:fog_credentials] = { provider: "Local", local_root: "#{Rails.root}/public"}
Paperclip::Attachment.default_options[:fog_directory] = ""
Paperclip::Attachment.default_options[:fog_host] = "http://localhost:3000"
```
---

Migrations
----------

Paperclip defines several migration methods which can be used to create the necessary columns in your
model. There are two types of helper methods to aid in this, as follows:

### Add Attachment Column To A Table

The `attachment` helper can be used when creating a table:

```ruby
class CreateUsersWithAttachments < ActiveRecord::Migration
  def up
    create_table :users do |t|
      t.attachment :avatar
    end
  end

  # This is assuming you are only using the users table for Paperclip attachment. Drop with care!
  def down
    drop_table :users
  end
end
```

You can also use the `change` method, instead of the `up`/`down` combination above, as shown below:

```ruby
class CreateUsersWithAttachments < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.attachment :avatar
    end
  end
end
```

### Schema Definition

Alternatively, the `add_attachment` and `remove_attachment` methods can be used to add new Paperclip columns to an existing table:

```ruby
class AddAttachmentColumnsToUsers < ActiveRecord::Migration
  def up
    add_attachment :users, :avatar
  end

  def down
    remove_attachment :users, :avatar
  end
end
```

Or you can do this with the `change` method:

```ruby
class AddAttachmentColumnsToUsers < ActiveRecord::Migration
  def change
    add_attachment :users, :avatar
  end
end
```

### Vintage Syntax

Vintage syntax (such as `t.has_attached_file` and `drop_attached_file`) is still supported in
Paperclip 3.x, but you're advised to update those migration files to use this new syntax.

---

Storage
-------

Paperclip ships with 3 storage adapters:

* File Storage
* S3 Storage (via `aws-sdk-s3`)
* Fog Storage

If you would like to use Paperclip with another storage, you can install these
gems along side with Paperclip:

* [paperclip-azure](https://github.com/supportify/paperclip-azure)
* [paperclip-azure-storage](https://github.com/gmontard/paperclip-azure-storage)
* [paperclip-dropbox](https://github.com/janko-m/paperclip-dropbox)

### Understanding Storage

The files that are assigned as attachments are, by default, placed in the
directory specified by the `:path` option to `has_attached_file`. By default, this
location is `:rails_root/public/system/:class/:attachment/:id_partition/:style/:filename`.
This location was chosen because, on standard Capistrano deployments, the
`public/system` directory can be symlinked to the app's shared directory, meaning it
survives between deployments. For example, using that `:path`, you may have a
file at

    /data/myapp/releases/20081229172410/public/system/users/avatar/000/000/013/small/my_pic.png

_**NOTE**: This is a change from previous versions of Paperclip, but is overall a
safer choice for the default file store._

You may also choose to store your files using Amazon's S3 service. To do so, include
the `aws-sdk-s3` gem in your Gemfile:

```ruby
gem 'aws-sdk-s3'
```

And then you can specify using S3 from `has_attached_file`.
You can find more information about configuring and using S3 storage in
[the `Paperclip::Storage::S3` documentation](http://www.rubydoc.info/gems/paperclip/Paperclip/Storage/S3).

Files on the local filesystem (and in the Rails app's public directory) will be
available to the internet at large. If you require access control, it's
possible to place your files in a different location. You will need to change
both the `:path` and `:url` options in order to make sure the files are unavailable
to the public. Both `:path` and `:url` allow the same set of interpolated
variables.

---

IO Adapters
-----------

When a file is uploaded or attached, it can be in one of a few different input
forms, from Rails' UploadedFile object to a StringIO to a Tempfile or even a
simple String that is a URL that points to an image.

Paperclip will accept, by default, many of these sources. It also is capable of
handling even more with a little configuration. The IO Adapters that handle
images from non-local sources are not enabled by default. They can be enabled by
adding a line similar to the following into `config/initializers/paperclip.rb`:

```ruby
Paperclip::DataUriAdapter.register
```

It's best to only enable a remote-loading adapter if you need it. Otherwise
there's a chance that someone can gain insight into your internal network
structure using it as a vector.

The following adapters are *not* loaded by default:

* `Paperclip::UriAdapter` - which accepts a `URI` instance.
* `Paperclip::HttpUrlProxyAdapter` - which accepts a `http` string.
* `Paperclip::DataUriAdapter` - which accepts a Base64-encoded `data:` string.

---

Post Processing
---------------

Paperclip supports an extensible selection of post-processors. When you define
a set of styles for an attachment, by default it is expected that those
"styles" are actually "thumbnails." These are processed by
`Paperclip::Thumbnail`.  For backward compatibility reasons you can pass either
a single geometry string, or an array containing a geometry and a format that
the file will be converted to, like so:

```ruby
has_attached_file :avatar, styles: { thumb: ["32x32#", :png] }
```

This will convert the "thumb" style to a 32x32 square in PNG format, regardless
of what was uploaded. If the format is not specified, it is kept the same (e.g.
JPGs will remain JPGs). `Paperclip::Thumbnail` uses ImageMagick to process
images; [ImageMagick's geometry documentation](http://www.imagemagick.org/script/command-line-processing.php#geometry)
has more information on the accepted style formats.

For more fine-grained control of the conversion process, `source_file_options` and `convert_options` can be used to pass flags and settings directly to ImageMagick's powerful Convert tool, [documented here](https://www.imagemagick.org/script/convert.php). For example:

```ruby
has_attached_file :image, styles: { regular: ['800x800>', :png]}, 
    source_file_options: { regular: "-density 96 -depth 8 -quality 85" },
    convert_options: { regular: "-posterize 3"}
```

ImageMagick supports a number of environment variables for controlling its resource limits. For example, you can enforce memory or execution time limits by setting the following variables in your application's process environment:

* `MAGICK_MEMORY_LIMIT=128MiB`
* `MAGICK_MAP_LIMIT=64MiB`
* `MAGICK_TIME_LIMIT=30`

For a full list of variables and description, see [ImageMagick's resources documentation](http://www.imagemagick.org/script/resources.php).

---

Custom Attachment Processors
-------

You can write your own custom attachment processors to carry out tasks like
adding watermarks, compressing images, or encrypting files. Custom processors
must be defined within the `Paperclip` module, inherit from
`Paperclip::Processor` (see [`lib/paperclip/processor.rb`](https://github.com/thoughtbot/paperclip/blob/master/lib/paperclip/processor.rb)),
and implement a `make` method that returns a `File`. All files in your Rails
app's `lib/paperclip` and `lib/paperclip_processors` directories will be
automatically loaded by Paperclip. Processors are specified using the
`:processors` option to `has_attached_file`:

```ruby
has_attached_file :scan, styles: { text: { quality: :better } },
                         processors: [:ocr]
```

This would load the hypothetical class `Paperclip::Ocr`, and pass it the
options hash `{ quality: :better }`, along with the uploaded file.

Multiple processors can be specified, and they will be invoked in the order
they are defined in the `:processors` array. Each successive processor is given
the result from the previous processor. All processors receive the same
parameters, which are defined in the `:styles` hash.  For example, assuming we
had this definition:

```ruby
has_attached_file :scan, styles: { text: { quality: :better } },
                         processors: [:rotator, :ocr]
```

Both the `:rotator` processor and the `:ocr` processor would receive the
options `{ quality: :better }`. If a processor receives an option it doesn't
recognise, it's expected to ignore it.

_NOTE: Because processors operate by turning the original attachment into the
styles, no processors will be run if there are no styles defined._

If you're interested in caching your thumbnail's width, height and size in the
database, take a look at the [paperclip-meta](https://github.com/teeparham/paperclip-meta)
gem.

Also, if you're interested in generating the thumbnail on-the-fly, you might want
to look into the [attachment_on_the_fly](https://github.com/drpentode/Attachment-on-the-Fly)
gem.

Paperclip's thumbnail generator (see [`lib/paperclip/thumbnail.rb`](lib/paperclip/thumbnail.rb))
is implemented as a processor, and may be a good reference for writing your own
processors.

---

Events
------

Before and after the Post Processing step, Paperclip calls back to the model
with a few callbacks, allowing the model to change or cancel the processing
step. The callbacks are `before_post_process` and `after_post_process` (which
are called before and after the processing of each attachment), and the
attachment-specific `before_<attachment>_post_process` and
`after_<attachment>_post_process`. The callbacks are intended to be as close to
normal ActiveRecord callbacks as possible, so if you return false (specifically
\- returning nil is not the same) in a `before_filter`, the post processing step
will halt. Returning false in an `after_filter` will not halt anything, but you
can access the model and the attachment if necessary.

_NOTE: Post processing will not even **start** if the attachment is not valid
according to the validations. Your callbacks and processors will **only** be
called with valid attachments._

```ruby
class Message < ActiveRecord::Base
  has_attached_file :asset, styles: { thumb: "100x100#" }

  before_post_process :skip_for_audio

  def skip_for_audio
    ! %w(audio/ogg application/ogg).include?(asset_content_type)
  end
end
```

---

URI Obfuscation
---------------

Paperclip has an interpolation called `:hash` for obfuscating filenames of
publicly-available files.

Example Usage:

```ruby
has_attached_file :avatar, {
    url: "/system/:hash.:extension",
    hash_secret: "longSecretString"
}
```


The `:hash` interpolation will be replaced with a unique hash made up of whatever
is specified in `:hash_data`. The default value for `:hash_data` is `":class/:attachment/:id/:style/:updated_at"`.

`:hash_secret` is required - an exception will be raised if `:hash` is used without `:hash_secret` present.

For more on this feature, read [the author's own explanation](https://github.com/thoughtbot/paperclip/pull/416)

Checksum / Fingerprint
-------

A checksum of the original file assigned will be placed in the model if it
has an attribute named fingerprint.  Following the user model migration example
above, the migration would look like the following:

```ruby
class AddAvatarFingerprintColumnToUser < ActiveRecord::Migration
  def up
    add_column :users, :avatar_fingerprint, :string
  end

  def down
    remove_column :users, :avatar_fingerprint
  end
end
```

The algorithm can be specified using a configuration option; it defaults to MD5
for backwards compatibility with Paperclip 5 and earlier.

```ruby
has_attached_file :some_attachment, adapter_options: { hash_digest: Digest::SHA256 }
```

Run `CLASS=User ATTACHMENT=avatar rake paperclip:refresh:fingerprints` after
changing the digest on existing attachments to update the fingerprints in the
database.

File Preservation for Soft-Delete
-------

An option is available to preserve attachments in order to play nicely with soft-deleted models. (acts_as_paranoid, paranoia, etc.)

```ruby
has_attached_file :some_attachment, {
    preserve_files: true,
}
```

This will prevent ```some_attachment``` from being wiped out when the model gets destroyed, so it will still exist when the object is restored later.

---

Dynamic Configuration
---------------------

Callable objects (lambdas, Procs) can be used in a number of places for dynamic
configuration throughout Paperclip.  This strategy exists in a number of
components of the library but is most significant in the possibilities for
allowing custom styles and processors to be applied for specific model
instances, rather than applying defined styles and processors across all
instances.

### Dynamic Styles:

Imagine a user model that had different styles based on the role of the user.
Perhaps some users are bosses (e.g. a User model instance responds to `#boss?`)
and merit a bigger avatar thumbnail than regular users. The configuration to
determine what style parameters are to be used based on the user role might
look as follows where a boss will receive a `300x300` thumbnail otherwise a
`100x100` thumbnail will be created.

```ruby
class User < ActiveRecord::Base
  has_attached_file :avatar, styles: lambda { |attachment| { thumb: (attachment.instance.boss? ? "300x300>" : "100x100>") } }
end
```

### Dynamic Processors:

Another contrived example is a user model that is aware of which file processors
should be applied to it (beyond the implied `thumbnail` processor invoked when
`:styles` are defined). Perhaps we have a watermark processor available and it is
only used on the avatars of certain models.  The configuration for this might be
where the instance is queried for which processors should be applied to it.
Presumably some users might return `[:thumbnail, :watermark]` for its
processors, where a defined `watermark` processor is invoked after the
`thumbnail` processor already defined by Paperclip.

```ruby
class User < ActiveRecord::Base
  has_attached_file :avatar, processors: lambda { |instance| instance.processors }
  attr_accessor :processors
end
```

---

Logging
----------

By default, Paperclip outputs logging according to your logger level. If you want to disable logging (e.g. during testing) add this into your environment's configuration:
```ruby
Your::Application.configure do
...
  Paperclip.options[:log] = false
...
end
```

More information in the [rdocs](http://www.rubydoc.info/github/thoughtbot/paperclip/Paperclip.options)

---

Deployment
----------

To make Capistrano symlink the `public/system` directory so that attachments
survive new deployments, set the `linked_dirs` option in your `config/deploy.rb`
file:

```ruby
set :linked_dirs, fetch(:linked_dirs, []).push('public/system')
```

### Attachment Styles

Paperclip is aware of new attachment styles you have added in previous deploys. The only thing you should do after each deployment is to call
`rake paperclip:refresh:missing_styles`.  It will store current attachment styles in `RAILS_ROOT/public/system/paperclip_attachments.yml`
by default. You can change it by:

```ruby
Paperclip.registered_attachments_styles_path = '/tmp/config/paperclip_attachments.yml'
```

Here is an example for Capistrano:

```ruby
namespace :paperclip do
  desc "build missing paperclip styles"
  task :build_missing_styles do
    on roles(:app) do
      within release_path do
        with rails_env: fetch(:rails_env) do
          execute :rake, "paperclip:refresh:missing_styles"
        end
      end
    end
  end
end

after("deploy:compile_assets", "paperclip:build_missing_styles")
```

Now you don't have to remember to refresh thumbnails in production every time you add a new style.
Unfortunately, it does not work with dynamic styles - it just ignores them.

If you already have a working app and don't want `rake paperclip:refresh:missing_styles` to refresh old pictures, you need to tell
Paperclip about existing styles. Simply create a `paperclip_attachments.yml` file by hand. For example:

```ruby
class User < ActiveRecord::Base
  has_attached_file :avatar, styles: { thumb: 'x100', croppable: '600x600>', big: '1000x1000>' }
end

class Book < ActiveRecord::Base
  has_attached_file :cover, styles: { small: 'x100', large: '1000x1000>' }
  has_attached_file :sample, styles: { thumb: 'x100' }
end
```

Then in `RAILS_ROOT/public/system/paperclip_attachments.yml`:

```yml
---
:User:
  :avatar:
  - :thumb
  - :croppable
  - :big
:Book:
  :cover:
  - :small
  - :large
  :sample:
  - :thumb
```

---

Testing
-------

Paperclip provides rspec-compatible matchers for testing attachments. See the
documentation on [Paperclip::Shoulda::Matchers](http://www.rubydoc.info/gems/paperclip/Paperclip/Shoulda/Matchers)
for more information.

**Parallel Tests**

Because of the default `path` for Paperclip storage, if you try to run tests in
parallel, you may find that files get overwritten because the same path is being
calculated for them in each test process. While this fix works for
parallel_tests, a similar concept should be used for any other mechanism for
running tests concurrently.

```ruby
if ENV['PARALLEL_TEST_GROUPS']
  Paperclip::Attachment.default_options[:path] = ":rails_root/public/system/:rails_env/#{ENV['TEST_ENV_NUMBER'].to_i}/:class/:attachment/:id_partition/:filename"
else
  Paperclip::Attachment.default_options[:path] = ":rails_root/public/system/:rails_env/:class/:attachment/:id_partition/:filename"
end
```

The important part here being the inclusion of `ENV['TEST_ENV_NUMBER']`, or a
similar mechanism for whichever parallel testing library you use.

**Integration Tests**

Using integration tests with FactoryBot may save multiple copies of
your test files within the app. To avoid this, specify a custom path in
the `config/environments/test.rb` like so:

```ruby
Paperclip::Attachment.default_options[:path] = "#{Rails.root}/spec/test_files/:class/:id_partition/:style.:extension"
```

Then, make sure to delete that directory after the test suite runs by adding
this to `spec_helper.rb`.

```ruby
config.after(:suite) do
  FileUtils.rm_rf(Dir["#{Rails.root}/spec/test_files/"])
end
```

**Example of test configuration with Factory Bot**


```ruby
FactoryBot.define do
  factory :user do
    avatar { File.new("#{Rails.root}/spec/support/fixtures/image.jpg") }
  end
end
```
---

Contributing
------------

If you'd like to contribute a feature or bugfix: Thanks! To make sure your
fix/feature has a high chance of being included, please read the following
guidelines:

1. Post a [pull request](https://github.com/thoughtbot/paperclip/compare/).
2. Make sure there are tests! We will not accept any patch that is not tested.
   It's a rare time when explicit tests aren't needed. If you have questions
   about writing tests for paperclip, please open a
   [GitHub issue](https://github.com/thoughtbot/paperclip/issues/new).

Please see [`CONTRIBUTING.md`](./CONTRIBUTING.md) for more details on contributing and running test.

Thank you to all [the contributors](https://github.com/thoughtbot/paperclip/graphs/contributors)!

License
-------

Paperclip is Copyright © 2008-2017 thoughtbot, inc. It is free software, and may be
redistributed under the terms specified in the MIT-LICENSE file.

About thoughtbot
----------------

![thoughtbot](http://presskit.thoughtbot.com/images/thoughtbot-logo-for-readmes.svg)

Paperclip is maintained and funded by thoughtbot.
The names and logos for thoughtbot are trademarks of thoughtbot, inc.

We love open source software!
See [our other projects][community] or
[hire us][hire] to design, develop, and grow your product.

[community]: https://thoughtbot.com/community?utm_source=github
[hire]: https://thoughtbot.com?utm_source=github


================================================
FILE: RELEASING.md
================================================
Releasing paperclip

1. Update `lib/paperclip/version.rb` file accordingly.
2. Update `NEWS` to reflect the changes since last release.
3. Commit changes. There shouldn’t be code changes, and thus CI doesn’t need to
   run, you can then add “[ci skip]” to the commit message.
4. Tag the release: `git tag -m 'vVERSION' vVERSION`
5. Push changes: `git push --tags`
6. Build and publish the gem:

   ```bash
   gem build paperclip.gemspec
   gem push paperclip-VERSION.gem
   ```

7. Announce the new release, making sure to say “thank you” to the contributors
   who helped shape this version.


================================================
FILE: Rakefile
================================================
require 'bundler/gem_tasks'
require 'appraisal'
require 'rspec/core/rake_task'
require 'cucumber/rake/task'

desc 'Default: run unit tests.'
task :default => [:clean, :all]

desc 'Test the paperclip plugin under all supported Rails versions.'
task :all do |t|
  if ENV['BUNDLE_GEMFILE']
    exec('rake spec && cucumber')
  else
    exec("rm -f gemfiles/*.lock")
    Rake::Task["appraisal:gemfiles"].execute
    Rake::Task["appraisal:install"].execute
    exec('rake appraisal')
  end
end

desc 'Test the paperclip plugin.'
RSpec::Core::RakeTask.new(:spec)

desc 'Run integration test'
Cucumber::Rake::Task.new do |t|
  t.cucumber_opts = %w{--format progress}
end

desc 'Start an IRB session with all necessary files required.'
task :shell do |t|
  chdir File.dirname(__FILE__)
  exec 'irb -I lib/ -I lib/paperclip -r rubygems -r active_record -r tempfile -r init'
end

desc 'Clean up files.'
task :clean do |t|
  FileUtils.rm_rf "doc"
  FileUtils.rm_rf "tmp"
  FileUtils.rm_rf "pkg"
  FileUtils.rm_rf "public"
  FileUtils.rm "test/debug.log" rescue nil
  FileUtils.rm "test/paperclip.db" rescue nil
  Dir.glob("paperclip-*.gem").each{|f| FileUtils.rm f }
end


================================================
FILE: UPGRADING
================================================
##################################################
#  NOTE FOR UPGRADING FROM 4.3.0 OR EARLIER      #
##################################################

Paperclip is now compatible with aws-sdk-s3.

If you are using S3 storage, aws-sdk-s3 requires you to make a few small
changes:

* You must set the `s3_region`
* If you are explicitly setting permissions anywhere, such as in an initializer,
  note that the format of the permissions changed from using an underscore to
  using a hyphen. For example, `:public_read` needs to be changed to
  `public-read`.

For a walkthrough of upgrading from 4 to *5* (not 6) and aws-sdk >= 2.0 you can watch
http://rubythursday.com/episodes/ruby-snack-27-upgrade-paperclip-and-aws-sdk-in-prep-for-rails-5


================================================
FILE: features/basic_integration.feature
================================================
Feature: Rails integration

  Background:
    Given I generate a new rails application
    And I run a rails generator to generate a "User" scaffold with "name:string"
    And I run a paperclip generator to add a paperclip "attachment" to the "User" model
    And I run a migration
    And I update my new user view to include the file upload field
    And I update my user view to include the attachment
    And I allow the attachment to be submitted

  Scenario: Configure defaults for all attachments through Railtie
    Given I add this snippet to config/application.rb:
      """
      config.paperclip_defaults = {
        :url => "/paperclip/custom/:attachment/:style/:filename",
        :validate_media_type => false
      }
      """
    And I attach :attachment
    And I start the rails application
    When I go to the new user page
    And I fill in "Name" with "something"
    And I attach the file "spec/support/fixtures/animated.unknown" to "Attachment"
    And I press "Submit"
    Then I should see "Name: something"
    And I should see an image with a path of "/paperclip/custom/attachments/original/animated.unknown"
    And the file at "/paperclip/custom/attachments/original/animated.unknown" should be the same as "spec/support/fixtures/animated.unknown"

  Scenario: Add custom processors
    Given I add a "test" processor in "lib/paperclip"
    And I add a "cool" processor in "lib/paperclip_processors"
    And I attach :attachment with:
      """
      styles: { original: {} }, processors: [:test, :cool]
      """
    And I start the rails application
    When I go to the new user page
    And I fill in "Name" with "something"
    And I attach the file "spec/support/fixtures/5k.png" to "Attachment"
    And I press "Submit"
    Then I should see "Name: something"
    And I should see an image with a path of "/paperclip/custom/attachments/original/5k.png"

  Scenario: Filesystem integration test
    Given I attach :attachment with:
      """
        :url => "/system/:attachment/:style/:filename"
      """
    And I start the rails application
    When I go to the new user page
    And I fill in "Name" with "something"
    And I attach the file "spec/support/fixtures/5k.png" to "Attachment"
    And I press "Submit"
    Then I should see "Name: something"
    And I should see an image with a path of "/system/attachments/original/5k.png"
    And the file at "/system/attachments/original/5k.png" should be the same as "spec/support/fixtures/5k.png"

  Scenario: S3 Integration test
    Given I attach :attachment with:
      """
        :storage => :s3,
        :path => "/:attachment/:style/:filename",
        :s3_credentials => Rails.root.join("config/s3.yml"),
        :styles => { :square => "100x100#" }
      """
    And I write to "config/s3.yml" with:
      """
      bucket: paperclip
      access_key_id: access_key
      secret_access_key: secret_key
      s3_region: us-west-2
      """
    And I start the rails application
    When I go to the new user page
    And I fill in "Name" with "something"
    And I attach the file "spec/support/fixtures/5k.png" to "Attachment" on S3
    And I press "Submit"
    Then I should see "Name: something"
    And I should see an image with a path of "//s3.amazonaws.com/paperclip/attachments/original/5k.png"
    And the file at "//s3.amazonaws.com/paperclip/attachments/original/5k.png" should be uploaded to S3


================================================
FILE: features/migration.feature
================================================
Feature: Migration

  Background:
    Given I generate a new rails application
    And I write to "app/models/user.rb" with:
      """
      class User < ActiveRecord::Base; end
      """

  Scenario: Vintage syntax
    When I write to "db/migrate/01_add_attachment_to_users.rb" with:
      """
      class AddAttachmentToUsers < ActiveRecord::Migration
        def self.up
          create_table :users do |t|
            t.has_attached_file :avatar
          end
        end

        def self.down
          drop_attached_file :users, :avatar
        end
      end
      """
    And I run a migration
    Then I should have attachment columns for "avatar"

    When I rollback a migration
    Then I should not have attachment columns for "avatar"

  Scenario: New syntax with create_table
    When I write to "db/migrate/01_add_attachment_to_users.rb" with:
      """
      class AddAttachmentToUsers < ActiveRecord::Migration
        def self.up
          create_table :users do |t|
            t.attachment :avatar
          end
        end
      end
      """
    And I run a migration
    Then I should have attachment columns for "avatar"

  Scenario: New syntax outside of create_table
    When I write to "db/migrate/01_create_users.rb" with:
      """
      class CreateUsers < ActiveRecord::Migration
        def self.up
          create_table :users
        end
      end
      """
    And I write to "db/migrate/02_add_attachment_to_users.rb" with:
      """
      class AddAttachmentToUsers < ActiveRecord::Migration
        def self.up
          add_attachment :users, :avatar
        end

        def self.down
          remove_attachment :users, :avatar
        end
      end
      """
    And I run a migration
    Then I should have attachment columns for "avatar"

    When I rollback a migration
    Then I should not have attachment columns for "avatar"


================================================
FILE: features/rake_tasks.feature
================================================
Feature: Rake tasks

  Background:
    Given I generate a new rails application
    And I run a rails generator to generate a "User" scaffold with "name:string"
    And I run a paperclip generator to add a paperclip "attachment" to the "User" model
    And I run a migration
    And I attach :attachment with:
      """
        :path => ":rails_root/public/system/:attachment/:style/:filename"
      """

  Scenario: Paperclip refresh thumbnails task
    When I modify my attachment definition to:
      """
      has_attached_file :attachment, :path => ":rails_root/public/system/:attachment/:style/:filename",
                                     :styles => { :medium => "200x200#" }
      """
    And I upload the fixture "5k.png"
    Then the attachment "medium/5k.png" should have a dimension of 200x200
    When I modify my attachment definition to:
      """
      has_attached_file :attachment, :path => ":rails_root/public/system/:attachment/:style/:filename",
                                     :styles => { :medium => "100x100#" }
      """
    When I successfully run `bundle exec rake paperclip:refresh:thumbnails CLASS=User --trace`
    Then the attachment "original/5k.png" should exist
    And the attachment "medium/5k.png" should have a dimension of 100x100

  Scenario: Paperclip refresh metadata task
    When I upload the fixture "5k.png"
    And I swap the attachment "original/5k.png" with the fixture "12k.png"
    And I successfully run `bundle exec rake paperclip:refresh:metadata CLASS=User --trace`
    Then the attachment should have the same content type as the fixture "12k.png"
    And the attachment should have the same file size as the fixture "12k.png"

  Scenario: Paperclip refresh missing styles task
    When I upload the fixture "5k.png"
    Then the attachment file "original/5k.png" should exist
    And the attachment file "medium/5k.png" should not exist
    When I modify my attachment definition to:
      """
      has_attached_file :attachment, :path => ":rails_root/public/system/:attachment/:style/:filename",
                                     :styles => { :medium => "200x200#" }
      """
    When I successfully run `bundle exec rake paperclip:refresh:missing_styles --trace`
    Then the attachment file "original/5k.png" should exist
    And the attachment file "medium/5k.png" should exist

  Scenario: Paperclip clean task
    When I upload the fixture "5k.png"
    And I upload the fixture "12k.png"
    Then the attachment file "original/5k.png" should exist
    And the attachment file "original/12k.png" should exist
    When I modify my attachment definition to:
      """
      has_attached_file :attachment, :path => ":rails_root/public/system/:attachment/:style/:filename"
      validates_attachment_size :attachment, :less_than => 10.kilobytes
      """
    And I successfully run `bundle exec rake paperclip:clean CLASS=User --trace`
    Then the attachment file "original/5k.png" should exist
    But the attachment file "original/12k.png" should not exist


================================================
FILE: features/step_definitions/attachment_steps.rb
================================================
module AttachmentHelpers
  def fixture_path(filename)
    File.expand_path("#{PROJECT_ROOT}/spec/support/fixtures/#{filename}")
  end

  def attachment_path(filename)
    File.expand_path("public/system/attachments/#{filename}")
  end
end
World(AttachmentHelpers)

When /^I modify my attachment definition to:$/ do |definition|
  content = cd(".") { File.read("app/models/user.rb") }
  name = content[/has_attached_file :\w+/][/:\w+/]
  content.gsub!(/has_attached_file.+end/m, <<-FILE)
      #{definition}
      do_not_validate_attachment_file_type #{name}
    end
  FILE

  write_file "app/models/user.rb", content
  cd(".") { FileUtils.rm_rf ".rbx" }
end

When /^I upload the fixture "([^"]*)"$/ do |filename|
  run_simple %(bundle exec rails runner "User.create!(:attachment => File.open('#{fixture_path(filename)}'))")
end

Then /^the attachment "([^"]*)" should have a dimension of (\d+x\d+)$/ do |filename, dimension|
  cd(".") do
    geometry = `identify -format "%wx%h" "#{attachment_path(filename)}"`.strip
    expect(geometry).to eq(dimension)
  end
end

Then /^the attachment "([^"]*)" should exist$/ do |filename|
  cd(".") do
    expect(File.exist?(attachment_path(filename))).to be true
  end
end

When /^I swap the attachment "([^"]*)" with the fixture "([^"]*)"$/ do |attachment_filename, fixture_filename|
  cd(".") do
    require 'fileutils'
    FileUtils.rm_f attachment_path(attachment_filename)
    FileUtils.cp fixture_path(fixture_filename), attachment_path(attachment_filename)
  end
end

Then /^the attachment should have the same content type as the fixture "([^"]*)"$/ do |filename|
  cd(".") do
    begin
      # Use mime/types/columnar if available, for reduced memory usage
      require "mime/types/columnar"
    rescue LoadError
      require "mime/types"
    end

    attachment_content_type = `bundle exec rails runner "puts User.last.attachment_content_type"`.strip
    expected = MIME::Types.type_for(filename).first.content_type
    expect(attachment_content_type).to eq(expected)
  end
end

Then /^the attachment should have the same file name as the fixture "([^"]*)"$/ do |filename|
  cd(".") do
    attachment_file_name = `bundle exec rails runner "puts User.last.attachment_file_name"`.strip
    expect(attachment_file_name).to eq(File.name(fixture_path(filename)).to_s)
  end
end

Then /^the attachment should have the same file size as the fixture "([^"]*)"$/ do |filename|
  cd(".") do
    attachment_file_size = `bundle exec rails runner "puts User.last.attachment_file_size"`.strip
    expect(attachment_file_size).to eq(File.size(fixture_path(filename)).to_s)
  end
end

Then /^the attachment file "([^"]*)" should (not )?exist$/ do |filename, not_exist|
  cd(".") do
    expect(attachment_path(filename)).not_to be_an_existing_file
  end
end

Then /^I should have attachment columns for "([^"]*)"$/ do |attachment_name|
  cd(".") do
    columns = eval(`bundle exec rails runner "puts User.columns.map{ |column| [column.name, column.sql_type] }.inspect"`.strip)
    expect_columns = [
      ["#{attachment_name}_file_name", "varchar"],
      ["#{attachment_name}_content_type", "varchar"],
      ["#{attachment_name}_file_size", "bigint"],
      ["#{attachment_name}_updated_at", "datetime"]
    ]
    expect(columns).to include(*expect_columns)
  end
end

Then /^I should not have attachment columns for "([^"]*)"$/ do |attachment_name|
  cd(".") do
    columns = eval(`bundle exec rails runner "puts User.columns.map{ |column| [column.name, column.sql_type] }.inspect"`.strip)
    expect_columns = [
      ["#{attachment_name}_file_name", "varchar"],
      ["#{attachment_name}_content_type", "varchar"],
      ["#{attachment_name}_file_size", "bigint"],
      ["#{attachment_name}_updated_at", "datetime"]
    ]

    expect(columns).not_to include(*expect_columns)
  end
end


================================================
FILE: features/step_definitions/html_steps.rb
================================================
Then %r{I should see an image with a path of "([^"]*)"} do |path|
  expect(page).to have_css("img[src^='#{path}']")
end

Then %r{^the file at "([^"]*)" is the same as "([^"]*)"$} do |web_file, path|
  expected = IO.read(path)
  actual = if web_file.match %r{^https?://}
    Net::HTTP.get(URI.parse(web_file))
  else
    visit(web_file)
    page.body
  end
  actual.force_encoding("UTF-8") if actual.respond_to?(:force_encoding)
  expect(actual).to eq(expected)
end


================================================
FILE: features/step_definitions/rails_steps.rb
================================================
Given /^I generate a new rails application$/ do
  steps %{
    When I successfully run `rails new #{APP_NAME} --skip-bundle`
    And I cd to "#{APP_NAME}"
  }

  FileUtils.chdir("tmp/aruba/testapp/")

  steps %{
    And I turn off class caching
    And I write to "Gemfile" with:
      """
      source "http://rubygems.org"
      gem "rails", "#{framework_version}"
      gem "sqlite3", :platform => [:ruby, :rbx]
      gem "activerecord-jdbcsqlite3-adapter", :platform => :jruby
      gem "jruby-openssl", :platform => :jruby
      gem "capybara"
      gem "gherkin"
      gem "aws-sdk-s3"
      gem "racc", :platform => :rbx
      gem "rubysl", :platform => :rbx
      """
    And I remove turbolinks
    And I comment out lines that contain "action_mailer" in "config/environments/*.rb"
    And I empty the application.js file
    And I configure the application to use "paperclip" from this project
  }

  FileUtils.chdir("../../..")
end

Given "I allow the attachment to be submitted" do
  cd(".") do
    transform_file("app/controllers/users_controller.rb") do |content|
      content.gsub("params.require(:user).permit(:name)",
                   "params.require(:user).permit!")
    end
  end
end

Given "I remove turbolinks" do
  cd(".") do
    transform_file("app/assets/javascripts/application.js") do |content|
      content.gsub("//= require turbolinks", "")
    end
    transform_file("app/views/layouts/application.html.erb") do |content|
      content.gsub(', "data-turbolinks-track" => true', "")
    end
  end
end

Given /^I comment out lines that contain "([^"]+)" in "([^"]+)"$/ do |contains, glob|
  cd(".") do
    Dir.glob(glob).each do |file|
      transform_file(file) do |content|
        content.gsub(/^(.*?#{contains}.*?)$/) { |line| "# #{line}" }
      end
    end
  end
end

Given /^I attach :attachment$/ do
  attach_attachment("attachment")
end

Given /^I attach :attachment with:$/ do |definition|
  attach_attachment("attachment", definition)
end

def attach_attachment(name, definition = nil)
  snippet = "has_attached_file :#{name}"
  if definition
    snippet += ", \n"
    snippet += definition
  end
  snippet += "\ndo_not_validate_attachment_file_type :#{name}\n"
  cd(".") do
    transform_file("app/models/user.rb") do |content|
      content.sub(/end\Z/, "#{snippet}\nend")
    end
  end
end

Given "I empty the application.js file" do
  cd(".") do
    transform_file("app/assets/javascripts/application.js") do |content|
      ""
    end
  end
end

Given /^I run a rails generator to generate a "([^"]*)" scaffold with "([^"]*)"$/ do |model_name, attributes|
  step %[I successfully run `rails generate scaffold #{model_name} #{attributes}`]
end

Given /^I run a paperclip generator to add a paperclip "([^"]*)" to the "([^"]*)" model$/ do |attachment_name, model_name|
  step %[I successfully run `rails generate paperclip #{model_name} #{attachment_name}`]
end

Given /^I run a migration$/ do
  step %[I successfully run `rake db:migrate --trace`]
end

When /^I rollback a migration$/ do
  step %[I successfully run `rake db:rollback STEPS=1 --trace`]
end

Given /^I update my new user view to include the file upload field$/ do
  steps %{
    Given I overwrite "app/views/users/new.html.erb" with:
      """
      <%= form_for @user, :html => { :multipart => true } do |f| %>
        <%= f.label :name %>
        <%= f.text_field :name %>
        <%= f.label :attachment %>
        <%= f.file_field :attachment %>
        <%= submit_tag "Submit" %>
      <% end %>
      """
  }
end

Given /^I update my user view to include the attachment$/ do
  steps %{
    Given I overwrite "app/views/users/show.html.erb" with:
      """
      <p>Name: <%= @user.name %></p>
      <p>Attachment: <%= image_tag @user.attachment.url %></p>
      """
  }
end

Given /^I add this snippet to the User model:$/ do |snippet|
  file_name = "app/models/user.rb"
  cd(".") do
    content = File.read(file_name)
    File.open(file_name, 'w') { |f| f << content.sub(/end\Z/, "#{snippet}\nend") }
  end
end

Given /^I add this snippet to config\/application.rb:$/ do |snippet|
  file_name = "config/application.rb"
  cd(".") do
    content = File.read(file_name)
    File.open(file_name, 'w') {|f| f << content.sub(/class Application < Rails::Application.*$/, "class Application < Rails::Application\n#{snippet}\n")}
  end
end

Given /^I start the rails application$/ do
  cd(".") do
    require "rails"
    require "./config/environment"
    require "capybara"
    Capybara.app = Rails.application
  end
end

Given /^I reload my application$/ do
  Rails::Application.reload!
end

When /^I turn off class caching$/ do
  cd(".") do
    file = "config/environments/test.rb"
    config = IO.read(file)
    config.gsub!(%r{^\s*config.cache_classes.*$},
                 "config.cache_classes = false")
    File.open(file, "w"){|f| f.write(config) }
  end
end

Then /^the file at "([^"]*)" should be the same as "([^"]*)"$/ do |web_file, path|
  expected = IO.read(path)
  actual = read_from_web(web_file)
  expect(actual).to eq(expected)
end

When /^I configure the application to use "([^\"]+)" from this project$/ do |name|
  append_to_gemfile "gem '#{name}', :path => '#{PROJECT_ROOT}'"
  steps %{And I successfully run `bundle install --local`}
end

When /^I configure the application to use "([^\"]+)"$/ do |gem_name|
  append_to_gemfile "gem '#{gem_name}'"
end

When /^I append gems from Appraisal Gemfile$/ do
  File.read(ENV['BUNDLE_GEMFILE']).split(/\n/).each do |line|
    if line =~ /^gem "(?!rails|appraisal)/
      append_to_gemfile line.strip
    end
  end
end

When /^I comment out the gem "([^"]*)" from the Gemfile$/ do |gemname|
  comment_out_gem_in_gemfile gemname
end

Given(/^I add a "(.*?)" processor in "(.*?)"$/) do |processor, directory|
  filename = "#{directory}/#{processor}.rb"
  cd(".") do
    FileUtils.mkdir_p directory
    File.open(filename, "w") do |f|
      f.write(<<-CLASS)
        module Paperclip
          class #{processor.capitalize} < Processor
            def make
              basename = File.basename(file.path, File.extname(file.path))
              dst_format = options[:format] ? ".\#{options[:format]}" : ''

              dst = Tempfile.new([basename, dst_format])
              dst.binmode

              convert(':src :dst',
                src: File.expand_path(file.path),
                dst: File.expand_path(dst.path)
              )

              dst
            end
          end
        end
      CLASS
    end
  end
end

def transform_file(filename)
  if File.exist?(filename)
    content = File.read(filename)
    File.open(filename, "w") do |f|
      content = yield(content)
      f.write(content)
    end
  end
end


================================================
FILE: features/step_definitions/s3_steps.rb
================================================
When /^I attach the file "([^"]*)" to "([^"]*)" on S3$/ do |file_path, field|
  definition = Paperclip::AttachmentRegistry.definitions_for(User)[field.downcase.to_sym]
  path = "https://paperclip.s3.us-west-2.amazonaws.com#{definition[:path]}"
  path.gsub!(':filename', File.basename(file_path))
  path.gsub!(/:([^\/\.]+)/) do |match|
    "([^\/\.]+)"
  end
  FakeWeb.register_uri(:put, Regexp.new(path), :body => "<xml></xml>")
  step "I attach the file \"#{file_path}\" to \"#{field}\""
end

Then /^the file at "([^"]*)" should be uploaded to S3$/ do |url|
  FakeWeb.registered_uri?(:put, url)
end


================================================
FILE: features/step_definitions/web_steps.rb
================================================
# TL;DR: YOU SHOULD DELETE THIS FILE
#
# This file was generated by Cucumber-Rails and is only here to get you a head start
# These step definitions are thin wrappers around the Capybara/Webrat API that lets you
# visit pages, interact with widgets and make assertions about page content.
#
# If you use these step definitions as basis for your features you will quickly end up
# with features that are:
#
# * Hard to maintain
# * Verbose to read
#
# A much better approach is to write your own higher level step definitions, following
# the advice in the following blog posts:
#
# * http://benmabey.com/2008/05/19/imperative-vs-declarative-scenarios-in-user-stories.html
# * http://dannorth.net/2011/01/31/whose-domain-is-it-anyway/
# * http://elabs.se/blog/15-you-re-cuking-it-wrong
#


require 'uri'
require 'cgi'
require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths"))
require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "selectors"))

module WithinHelpers
  def with_scope(locator)
    locator ? within(*selector_for(locator)) { yield } : yield
  end
end
World(WithinHelpers)

# Single-line step scoper
When /^(.*) within (.*[^:])$/ do |step, parent|
  with_scope(parent) { When step }
end

# Multi-line step scoper
When /^(.*) within (.*[^:]):$/ do |step, parent, table_or_string|
  with_scope(parent) { When "#{step}:", table_or_string }
end

Given /^(?:|I )am on (.+)$/ do |page_name|
  visit path_to(page_name)
end

When /^(?:|I )go to (.+)$/ do |page_name|
  visit path_to(page_name)
end

When /^(?:|I )press "([^"]*)"$/ do |button|
  click_button(button)
end

When /^(?:|I )follow "([^"]*)"$/ do |link|
  click_link(link)
end

When /^(?:|I )fill in "([^"]*)" with "([^"]*)"$/ do |field, value|
  fill_in(field, :with => value)
end

When /^(?:|I )fill in "([^"]*)" for "([^"]*)"$/ do |value, field|
  fill_in(field, :with => value)
end

# Use this to fill in an entire form with data from a table. Example:
#
#   When I fill in the following:
#     | Account Number | 5002       |
#     | Expiry date    | 2009-11-01 |
#     | Note           | Nice guy   |
#     | Wants Email?   |            |
#
# TODO: Add support for checkbox, select og option
# based on naming conventions.
#
When /^(?:|I )fill in the following:$/ do |fields|
  fields.rows_hash.each do |name, value|
    When %{I fill in "#{name}" with "#{value}"}
  end
end

When /^(?:|I )select "([^"]*)" from "([^"]*)"$/ do |value, field|
  select(value, :from => field)
end

When /^(?:|I )check "([^"]*)"$/ do |field|
  check(field)
end

When /^(?:|I )uncheck "([^"]*)"$/ do |field|
  uncheck(field)
end

When /^(?:|I )choose "([^"]*)"$/ do |field|
  choose(field)
end

When /^(?:|I )attach the file "([^"]*)" to "([^"]*)"$/ do |path, field|
  attach_file(field, File.expand_path(path))
end

Then /^(?:|I )should see "([^"]*)"$/ do |text|
  expect(page).to have_content(text)
end


================================================
FILE: features/support/env.rb
================================================
require 'aruba/cucumber'
require 'capybara/cucumber'
require 'rspec/matchers'

$CUCUMBER=1

World(RSpec::Matchers)

Before do
  aruba.config.command_launcher = ENV.fetch("DEBUG", nil) ? :debug : :spawn
  @aruba_timeout_seconds = 120
end


================================================
FILE: features/support/fakeweb.rb
================================================
require 'fake_web'

FakeWeb.allow_net_connect = false

module FakeWeb
  class StubSocket
    def read_timeout=(_ignored)
    end

    def continue_timeout=(_ignored)
    end
  end
end


================================================
FILE: features/support/file_helpers.rb
================================================
module FileHelpers
  def append_to(path, contents)
    cd(".") do
      File.open(path, "a") do |file|
        file.puts
        file.puts contents
      end
    end
  end

  def append_to_gemfile(contents)
    append_to('Gemfile', contents)
  end

  def comment_out_gem_in_gemfile(gemname)
    cd(".") do
      gemfile = File.read("Gemfile")
      gemfile.sub!(/^(\s*)(gem\s*['"]#{gemname})/, "\\1# \\2")
      File.open("Gemfile", 'w'){ |file| file.write(gemfile) }
    end
  end

  def read_from_web(url)
    file = if url.match %r{^https?://}
             Net::HTTP.get(URI.parse(url))
           else
             visit(url)
             page.source
           end
    file.force_encoding("UTF-8") if file.respond_to?(:force_encoding)
  end
end

World(FileHelpers)


================================================
FILE: features/support/fixtures/boot_config.txt
================================================
class Rails::Boot
  def run
    load_initializer

    Rails::Initializer.class_eval do
      def load_gems
        @bundler_loaded ||= Bundler.require :default, Rails.env
      end
    end

    Rails::Initializer.run(:set_load_path)
  end
end

Rails.boot!


================================================
FILE: features/support/fixtures/gemfile.txt
================================================
source "http://rubygems.org"

gem "rails", "RAILS_VERSION"
gem "rdoc"
gem "sqlite3", "1.3.8"


================================================
FILE: features/support/fixtures/preinitializer.txt
================================================
begin
  require "rubygems"
  require "bundler"
rescue LoadError
  raise "Could not load the bundler gem. Install it with `gem install bundler`."
end

if Gem::Version.new(Bundler::VERSION) <= Gem::Version.new("0.9.24")
  raise RuntimeError, "Your bundler version is too old for Rails 2.3." +
   "Run `gem install bundler` to upgrade."
end

begin
  # Set up load paths for all bundled gems
  ENV["BUNDLE_GEMFILE"] = File.expand_path("../../Gemfile", __FILE__)
  Bundler.setup
rescue Bundler::GemNotFound
  raise RuntimeError, "Bundler couldn't find some gems." +
    "Did you run `bundle install`?"
end


================================================
FILE: features/support/paths.rb
================================================
module NavigationHelpers
  # Maps a name to a path. Used by the
  #
  #   When /^I go to (.+)$/ do |page_name|
  #
  # step definition in web_steps.rb
  #
  def path_to(page_name)
    case page_name

    when /the home\s?page/
      '/'
    when /the new user page/
      '/users/new'
    else
      begin
        page_name =~ /the (.*) page/
        path_components = $1.split(/\s+/)
        self.send(path_components.push('path').join('_').to_sym)
      rescue Object
        raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
          "Now, go and add a mapping in #{__FILE__}"
      end
    end
  end
end

World(NavigationHelpers)


================================================
FILE: features/support/rails.rb
================================================
PROJECT_ROOT     = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')).freeze
APP_NAME         = 'testapp'.freeze
BUNDLE_ENV_VARS = %w(RUBYOPT BUNDLE_PATH BUNDLE_BIN_PATH BUNDLE_GEMFILE)
ORIGINAL_BUNDLE_VARS = Hash[ENV.select{ |key,value| BUNDLE_ENV_VARS.include?(key) }]

ENV['RAILS_ENV'] = 'test'

Before do
  gemfile = ENV['BUNDLE_GEMFILE'].to_s
  ENV['BUNDLE_GEMFILE'] = File.join(Dir.pwd, gemfile) unless gemfile.start_with?(Dir.pwd)
  @framework_version = nil
end

After do
  ORIGINAL_BUNDLE_VARS.each_pair do |key, value|
    ENV[key] = value
  end
end

When /^I reset Bundler environment variable$/ do
  BUNDLE_ENV_VARS.each do |key|
    ENV[key] = nil
  end
end

module RailsCommandHelpers
  def framework_version?(version_string)
    framework_version =~ /^#{version_string}/
  end

  def framework_version
    @framework_version ||= `rails -v`[/^Rails (.+)$/, 1]
  end

  def framework_major_version
    framework_version.split(".").first.to_i
  end
end
World(RailsCommandHelpers)


================================================
FILE: features/support/selectors.rb
================================================
module HtmlSelectorsHelpers
  # Maps a name to a selector. Used primarily by the
  #
  #   When /^(.+) within (.+)$/ do |step, scope|
  #
  # step definitions in web_steps.rb
  #
  def selector_for(locator)
    case locator
    when "the page"
      "html > body"
    else
      raise "Can't find mapping from \"#{locator}\" to a selector.\n" +
        "Now, go and add a mapping in #{__FILE__}"
    end
  end
end

World(HtmlSelectorsHelpers)


================================================
FILE: gemfiles/4.2.gemfile
================================================
# This file was generated by Appraisal

source "https://rubygems.org"

gem "sqlite3", "~> 1.3.8", :platforms => :ruby
gem "pry"
gem "rails", "~> 4.2.0"

group :development, :test do
  gem "activerecord-import"
  gem "mime-types"
  gem "builder"
  gem "rubocop", :require => false
  gem "rspec"
end

gemspec :path => "../"


================================================
FILE: gemfiles/5.0.gemfile
================================================
# This file was generated by Appraisal

source "https://rubygems.org"

gem "sqlite3", "~> 1.3.8", :platforms => :ruby
gem "pry"
gem "rails", "~> 5.0.0"

group :development, :test do
  gem "activerecord-import"
  gem "mime-types"
  gem "builder"
  gem "rubocop", :require => false
  gem "rspec"
end

gemspec :path => "../"


================================================
FILE: lib/generators/paperclip/USAGE
================================================
Description:
    Explain the generator

Example:
    rails generate paperclip Thing

    This will create:
        what/will/it/create


================================================
FILE: lib/generators/paperclip/paperclip_generator.rb
================================================
require 'rails/generators/active_record'

class PaperclipGenerator < ActiveRecord::Generators::Base
  desc "Create a migration to add paperclip-specific fields to your model. " +
       "The NAME argument is the name of your model, and the following " +
       "arguments are the name of the attachments"

  argument :attachment_names, :required => true, :type => :array, :desc => "The names of the attachment(s) to add.",
           :banner => "attachment_one attachment_two attachment_three ..."

  def self.source_root
    @source_root ||= File.expand_path('../templates', __FILE__)
  end

  def generate_migration
    migration_template("paperclip_migration.rb.erb",
                       "db/migrate/#{migration_file_name}",
                       migration_version: migration_version)
  end

  def migration_name
    "add_attachment_#{attachment_names.join("_")}_to_#{name.underscore.pluralize}"
  end

  def migration_file_name
    "#{migration_name}.rb"
  end

  def migration_class_name
    migration_name.camelize
  end

  def migration_version
    if Rails.version.start_with? "5"
      "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
    end
  end
end


================================================
FILE: lib/generators/paperclip/templates/paperclip_migration.rb.erb
================================================
class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
  def self.up
    change_table :<%= table_name %> do |t|
<% attachment_names.each do |attachment| -%>
      t.attachment :<%= attachment %>
<% end -%>
    end
  end

  def self.down
<% attachment_names.each do |attachment| -%>
    remove_attachment :<%= table_name %>, :<%= attachment %>
<% end -%>
  end
end


================================================
FILE: lib/paperclip/attachment.rb
================================================
require 'uri'
require 'paperclip/url_generator'
require 'active_support/deprecation'
require 'active_support/core_ext/string/inflections'

module Paperclip
  # The Attachment class manages the files for a given attachment. It saves
  # when the model saves, deletes when the model is destroyed, and processes
  # the file upon assignment.
  class Attachment
    def self.default_options
      @default_options ||= {
        :convert_options       => {},
        :default_style         => :original,
        :default_url           => "/:attachment/:style/missing.png",
        :escape_url            => true,
        :restricted_characters => /[&$+,\/:;=?@<>\[\]\{\}\|\\\^~%# ]/,
        :filename_cleaner      => nil,
        :hash_data             => ":class/:attachment/:id/:style/:updated_at",
        :hash_digest           => "SHA1",
        :interpolator          => Paperclip::Interpolations,
        :only_process          => [],
        :path                  => ":rails_root/public:url",
        :preserve_files        => false,
        :processors            => [:thumbnail],
        :source_file_options   => {},
        :storage               => :filesystem,
        :styles                => {},
        :url                   => "/system/:class/:attachment/:id_partition/:style/:filename",
        :url_generator         => Paperclip::UrlGenerator,
        :use_default_time_zone => true,
        :use_timestamp         => true,
        :whiny                 => Paperclip.options[:whiny] || Paperclip.options[:whiny_thumbnails],
        :validate_media_type   => true,
        :adapter_options       => { hash_digest: Digest::MD5 },
        :check_validity_before_processing => true
      }
    end

    attr_reader :name, :instance, :default_style, :convert_options, :queued_for_write, :whiny,
                :options, :interpolator, :source_file_options
    attr_accessor :post_processing

    # Creates an Attachment object. +name+ is the name of the attachment,
    # +instance+ is the model object instance it's attached to, and
    # +options+ is the same as the hash passed to +has_attached_file+.
    #
    # Options include:
    #
    # +url+ - a relative URL of the attachment. This is interpolated using +interpolator+
    # +path+ - where on the filesystem to store the attachment. This is interpolated using +interpolator+
    # +styles+ - a hash of options for processing the attachment. See +has_attached_file+ for the details
    # +only_process+ - style args to be run through the post-processor. This defaults to the empty list (which is
    #                  a special case that indicates all styles should be processed)
    # +default_url+ - a URL for the missing image
    # +default_style+ - the style to use when an argument is not specified e.g. #url, #path
    # +storage+ - the storage mechanism. Defaults to :filesystem
    # +use_timestamp+ - whether to append an anti-caching timestamp to image URLs. Defaults to true
    # +whiny+, +whiny_thumbnails+ - whether to raise when thumbnailing fails
    # +use_default_time_zone+ - related to +use_timestamp+. Defaults to true
    # +hash_digest+ - a string representing a class that will be used to hash URLs for obfuscation
    # +hash_data+ - the relative URL for the hash data. This is interpolated using +interpolator+
    # +hash_secret+ - a secret passed to the +hash_digest+
    # +convert_options+ - flags passed to the +convert+ command for processing
    # +source_file_options+ - flags passed to the +convert+ command that controls how the file is read
    # +processors+ - classes that transform the attachment. Defaults to [:thumbnail]
    # +preserve_files+ - whether to keep files on the filesystem when deleting or clearing the attachment. Defaults to false
    # +filename_cleaner+ - An object that responds to #call(filename) that will strip unacceptable charcters from filename
    # +interpolator+ - the object used to interpolate filenames and URLs. Defaults to Paperclip::Interpolations
    # +url_generator+ - the object used to generate URLs, using the interpolator. Defaults to Paperclip::UrlGenerator
    # +escape_url+ - Perform URI escaping to URLs. Defaults to true
    def initialize(name, instance, options = {})
      @name              = name.to_sym
      @name_string       = name.to_s
      @instance          = instance

      options = self.class.default_options.deep_merge(options)

      @options               = options
      @post_processing       = true
      @queued_for_delete     = []
      @queued_for_write      = {}
      @errors                = {}
      @dirty                 = false
      @interpolator          = options[:interpolator]
      @url_generator         = options[:url_generator].new(self)
      @source_file_options   = options[:source_file_options]
      @whiny                 = options[:whiny]

      initialize_storage
    end

    # What gets called when you call instance.attachment = File. It clears
    # errors, assigns attributes, and processes the file. It also queues up the
    # previous file for deletion, to be flushed away on #save of its host.  In
    # addition to form uploads, you can also assign another Paperclip
    # attachment:
    #   new_user.avatar = old_user.avatar
    def assign(uploaded_file)
      @file = Paperclip.io_adapters.for(uploaded_file,
                                        @options[:adapter_options])
      ensure_required_accessors!
      ensure_required_validations!

      if @file.assignment?
        clear(*only_process)

        if @file.nil?
          nil
        else
          assign_attributes
          post_process_file
          reset_file_if_original_reprocessed
        end
      else
        nil
      end
    end

    # Returns the public URL of the attachment with a given style. This does
    # not necessarily need to point to a file that your Web server can access
    # and can instead point to an action in your app, for example for fine grained
    # security; this has a serious performance tradeoff.
    #
    # Options:
    #
    # +timestamp+ - Add a timestamp to the end of the URL. Default: true.
    # +escape+    - Perform URI escaping to the URL. Default: true.
    #
    # Global controls (set on has_attached_file):
    #
    # +interpolator+  - The object that fills in a URL pattern's variables.
    # +default_url+   - The image to show when the attachment has no image.
    # +url+           - The URL for a saved image.
    # +url_generator+ - The object that generates a URL. Default: Paperclip::UrlGenerator.
    #
    # As mentioned just above, the object that generates this URL can be passed
    # in, for finer control. This object must respond to two methods:
    #
    # +#new(Paperclip::Attachment, options_hash)+
    # +#for(style_name, options_hash)+

    def url(style_name = default_style, options = {})
      if options == true || options == false # Backwards compatibility.
        @url_generator.for(style_name, default_options.merge(:timestamp => options))
      else
        @url_generator.for(style_name, default_options.merge(options))
      end
    end

    def default_options
      {
        :timestamp => @options[:use_timestamp],
        :escape => @options[:escape_url]
      }
    end

    # Alias to +url+ that allows using the expiring_url method provided by the cloud
    # storage implementations, but keep using filesystem storage for development and
    # testing.
    def expiring_url(time = 3600, style_name = default_style)
      url(style_name)
    end

    # Returns the path of the attachment as defined by the :path option. If the
    # file is stored in the filesystem the path refers to the path of the file
    # on disk. If the file is stored in S3, the path is the "key" part of the
    # URL, and the :bucket option refers to the S3 bucket.
    def path(style_name = default_style)
      path = original_filename.nil? ? nil : interpolate(path_option, style_name)
      path.respond_to?(:unescape) ? path.unescape : path
    end

    # :nodoc:
    def staged_path(style_name = default_style)
      if staged?
        @queued_for_write[style_name].path
      end
    end

    # :nodoc:
    def staged?
      ! @queued_for_write.empty?
    end

    # Alias to +url+
    def to_s style_name = default_style
      url(style_name)
    end

    def as_json(options = nil)
      to_s((options && options[:style]) || default_style)
    end

    def default_style
      @options[:default_style]
    end

    def styles
      if @options[:styles].respond_to?(:call) || @normalized_styles.nil?
        styles = @options[:styles]
        styles = styles.call(self) if styles.respond_to?(:call)

        @normalized_styles = styles.dup
        styles.each_pair do |name, options|
          @normalized_styles[name.to_sym] = Paperclip::Style.new(name.to_sym, options.dup, self)
        end
      end
      @normalized_styles
    end

    def only_process
      only_process = @options[:only_process].dup
      only_process = only_process.call(self) if only_process.respond_to?(:call)
      only_process.map(&:to_sym)
    end

    def processors
      processing_option = @options[:processors]

      if processing_option.respond_to?(:call)
        processing_option.call(instance)
      else
        processing_option
      end
    end

    # Returns an array containing the errors on this attachment.
    def errors
      @errors
    end

    # Returns true if there are changes that need to be saved.
    def dirty?
      @dirty
    end

    # Saves the file, if there are no errors. If there are, it flushes them to
    # the instance's errors and returns false, cancelling the save.
    def save
      flush_deletes unless @options[:keep_old_files]
      process = only_process
      if process.any? && !process.include?(:original)
        @queued_for_write.except!(:original)
      end
      flush_writes
      @dirty = false
      true
    end

    # Clears out the attachment. Has the same effect as previously assigning
    # nil to the attachment. Does NOT save. If you wish to clear AND save,
    # use #destroy.
    def clear(*styles_to_clear)
      if styles_to_clear.any?
        queue_some_for_delete(*styles_to_clear)
      else
        queue_all_for_delete
        @queued_for_write  = {}
        @errors            = {}
      end
    end

    # Destroys the attachment. Has the same effect as previously assigning
    # nil to the attachment *and saving*. This is permanent. If you wish to
    # wipe out the existing attachment but not save, use #clear.
    def destroy
      clear
      save
    end

    # Returns the uploaded file if present.
    def uploaded_file
      instance_read(:uploaded_file)
    end

    # Returns the name of the file as originally assigned, and lives in the
    # <attachment>_file_name attribute of the model.
    def original_filename
      instance_read(:file_name)
    end

    # Returns the size of the file as originally assigned, and lives in the
    # <attachment>_file_size attribute of the model.
    def size
      instance_read(:file_size) || (@queued_for_write[:original] && @queued_for_write[:original].size)
    end

    # Returns the fingerprint of the file, if one's defined. The fingerprint is
    # stored in the <attachment>_fingerprint attribute of the model.
    def fingerprint
      instance_read(:fingerprint)
    end

    # Returns the content_type of the file as originally assigned, and lives
    # in the <attachment>_content_type attribute of the model.
    def content_type
      instance_read(:content_type)
    end

    # Returns the creation time of the file as originally assigned, and
    # lives in the <attachment>_created_at attribute of the model.
    def created_at
      if able_to_store_created_at?
        time = instance_read(:created_at)
        time && time.to_f.to_i
      end
    end

    # Returns the last modified time of the file as originally assigned, and
    # lives in the <attachment>_updated_at attribute of the model.
    def updated_at
      time = instance_read(:updated_at)
      time && time.to_f.to_i
    end

    # The time zone to use for timestamp interpolation.  Using the default
    # time zone ensures that results are consistent across all threads.
    def time_zone
      @options[:use_default_time_zone] ? Time.zone_default : Time.zone
    end

    # Returns a unique hash suitable for obfuscating the URL of an otherwise
    # publicly viewable attachment.
    def hash_key(style_name = default_style)
      raise ArgumentError, "Unable to generate hash without :hash_secret" unless @options[:hash_secret]
      require 'openssl' unless defined?(OpenSSL)
      data = interpolate(@options[:hash_data], style_name)
      OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@options[:hash_digest]).new, @options[:hash_secret], data)
    end

    # This method really shouldn't be called that often. Its expected use is
    # in the paperclip:refresh rake task and that's it. It will regenerate all
    # thumbnails forcefully, by reobtaining the original file and going through
    # the post-process again.
    # NOTE: Calling reprocess WILL NOT delete existing files. This is due to
    # inconsistencies in timing of S3 commands. It's possible that calling
    # #reprocess! will lose data if the files are not kept.
    def reprocess!(*style_args)
      saved_flags = @options.slice(
        :only_process,
        :preserve_files,
        :check_validity_before_processing
      )
      @options[:only_process] = style_args
      @options[:preserve_files] = true
      @options[:check_validity_before_processing] = false

      begin
        assign(self)
        save
        instance.save
      rescue Errno::EACCES => e
        warn "#{e} - skipping file."
        false
      ensure
        @options.merge!(saved_flags)
      end
    end

    # Returns true if a file has been assigned.
    def file?
      original_filename.present?
    end

    alias :present? :file?

    def blank?
      not present?
    end

    # Determines whether the instance responds to this attribute. Used to prevent
    # calculations on fields we won't even store.
    def instance_respond_to?(attr)
      instance.respond_to?(:"#{name}_#{attr}")
    end

    # Writes the attachment-specific attribute on the instance. For example,
    # instance_write(:file_name, "me.jpg") will write "me.jpg" to the instance's
    # "avatar_file_name" field (assuming the attachment is called avatar).
    def instance_write(attr, value)
      setter = :"#{@name_string}_#{attr}="
      if instance.respond_to?(setter)
        instance.send(setter, value)
      end
    end

    # Reads the attachment-specific attribute on the instance. See instance_write
    # for more details.
    def instance_read(attr)
      getter = :"#{@name_string}_#{attr}"
      if instance.respond_to?(getter)
        instance.send(getter)
      end
    end

    private

    def path_option
      @options[:path].respond_to?(:call) ? @options[:path].call(self) : @options[:path]
    end

    def active_validator_classes
      @instance.class.validators.map(&:class)
    end

    def missing_required_validator?
      (active_validator_classes.flat_map(&:ancestors) & Paperclip::REQUIRED_VALIDATORS).empty?
    end

    def ensure_required_validations!
      if missing_required_validator?
        raise Paperclip::Errors::MissingRequiredValidatorError
      end
    end

    def ensure_required_accessors! #:nodoc:
      %w(file_name).each do |field|
        unless @instance.respond_to?("#{@name_string}_#{field}") && @instance.respond_to?("#{@name_string}_#{field}=")
          raise Paperclip::Error.new("#{@instance.class} model missing required attr_accessor for '#{@name_string}_#{field}'")
        end
      end
    end

    def log message #:nodoc:
      Paperclip.log(message)
    end

    def initialize_storage #:nodoc:
      storage_class_name = @options[:storage].to_s.downcase.camelize
      begin
        storage_module = Paperclip::Storage.const_get(storage_class_name)
      rescue NameError
        raise Errors::StorageMethodNotFound, "Cannot load storage module '#{storage_class_name}'"
      end
      self.extend(storage_module)
    end

    def assign_attributes
      @queued_for_write[:original] = @file
      assign_file_information
      assign_fingerprint { @file.fingerprint }
      assign_timestamps
    end

    def assign_file_information
      instance_write(:file_name, cleanup_filename(@file.original_filename))
      instance_write(:content_type, @file.content_type.to_s.strip)
      instance_write(:file_size, @file.size)
    end

    def assign_fingerprint
      if instance_respond_to?(:fingerprint)
        instance_write(:fingerprint, yield)
      end
    end

    def assign_timestamps
      if has_enabled_but_unset_created_at?
        instance_write(:created_at, Time.now)
      end

      instance_write(:updated_at, Time.now)
    end

    def post_process_file
      dirty!

      if post_processing
        post_process(*only_process)
      end
    end

    def dirty!
      @dirty = true
    end

    def reset_file_if_original_reprocessed
      instance_write(:file_size, @queued_for_write[:original].size)
      assign_fingerprint { @queued_for_write[:original].fingerprint }
      reset_updater
    end

    def reset_updater
      if instance.respond_to?(updater)
        instance.send(updater)
      end
    end

    def updater
      :"#{name}_file_name_will_change!"
    end

    def extra_options_for(style) #:nodoc:
      process_options(:convert_options, style)
    end

    def extra_source_file_options_for(style) #:nodoc:
      process_options(:source_file_options, style)
    end

    def process_options(options_type, style) #:nodoc:
      all_options   = @options[options_type][:all]
      all_options   = all_options.call(instance)   if all_options.respond_to?(:call)
      style_options = @options[options_type][style]
      style_options = style_options.call(instance) if style_options.respond_to?(:call)

      [ style_options, all_options ].compact.join(" ")
    end

    def post_process(*style_args) #:nodoc:
      return if @queued_for_write[:original].nil?

      instance.run_paperclip_callbacks(:post_process) do
        instance.run_paperclip_callbacks(:"#{name}_post_process") do
          if !@options[:check_validity_before_processing] || !instance.errors.any?
            post_process_styles(*style_args)
          end
        end
      end
    end

    def post_process_styles(*style_args) #:nodoc:
      post_process_style(:original, styles[:original]) if styles.include?(:original) && process_style?(:original, style_args)
      styles.reject{ |name, style| name == :original }.each do |name, style|
        post_process_style(name, style) if process_style?(name, style_args)
      end
    end

    def post_process_style(name, style) #:nodoc:
      begin
        raise RuntimeError.new("Style #{name} has no processors defined.") if style.processors.blank?
        intermediate_files = []
        original = @queued_for_write[:original]

        @queued_for_write[name] = style.processors.
          reduce(original) do |file, processor|
          file = Paperclip.processor(processor).make(file, style.processor_options, self)
          intermediate_files << file unless file == @queued_for_write[:original]
          # if we're processing the original, close + unlink the source tempfile
          if name == :original
            @queued_for_write[:original].close(true)
          end
          file
        end

        unadapted_file = @queued_for_write[name]
        @queued_for_write[name] = Paperclip.io_adapters.
          for(@queued_for_write[name], @options[:adapter_options])
        unadapted_file.close if unadapted_file.respond_to?(:close)
        @queued_for_write[name]
      rescue Paperclip::Errors::NotIdentifiedByImageMagickError => e
        log("An error was received while processing: #{e.inspect}")
        (@errors[:processing] ||= []) << e.message if @options[:whiny]
      ensure
        unlink_files(intermediate_files)
      end
    end

    def process_style?(style_name, style_args) #:nodoc:
      style_args.empty? || style_args.include?(style_name)
    end

    def interpolate(pattern, style_name = default_style) #:nodoc:
      interpolator.interpolate(pattern, self, style_name)
    end

    def queue_some_for_delete(*styles)
      @queued_for_delete += styles.uniq.map do |style|
        path(style) if exists?(style)
      end.compact
    end

    def queue_all_for_delete #:nodoc:
      return if !file?
      unless @options[:preserve_files]
        @queued_for_delete += [:original, *styles.keys].uniq.map do |style|
          path(style) if exists?(style)
        end.compact
      end
      instance_write(:file_name, nil)
      instance_write(:content_type, nil)
      instance_write(:file_size, nil)
      instance_write(:fingerprint, nil)
      instance_write(:created_at, nil) if has_enabled_but_unset_created_at?
      instance_write(:updated_at, nil)
    end

    def flush_errors #:nodoc:
      @errors.each do |error, message|
        [message].flatten.each {|m| instance.errors.add(name, m) }
      end
    end

    # called by storage after the writes are flushed and before @queued_for_write is cleared
    def after_flush_writes
      unlink_files(@queued_for_write.values)
    end

    def unlink_files(files)
      Array(files).each do |file|
        file.close unless file.closed?

        begin
          file.unlink if file.respond_to?(:unlink)
        rescue Errno::ENOENT
        end
      end
    end

    # You can either specifiy :restricted_characters or you can define your own
    # :filename_cleaner object. This object needs to respond to #call and takes
    # the filename that will be cleaned. It should return the cleaned filename.
    def filename_cleaner
      @options[:filename_cleaner] || FilenameCleaner.new(@options[:restricted_characters])
    end

    def cleanup_filename(filename)
      filename_cleaner.call(filename)
    end

    # Check if attachment database table has a created_at field
    def able_to_store_created_at?
      @instance.respond_to?("#{name}_created_at".to_sym)
    end

    # Check if attachment database table has a created_at field which is not yet set
    def has_enabled_but_unset_created_at?
      able_to_store_created_at? && !instance_read(:created_at)
    end
  end
end


================================================
FILE: lib/paperclip/attachment_registry.rb
================================================
require 'singleton'

module Paperclip
  class AttachmentRegistry
    include Singleton

    def self.register(klass, attachment_name, attachment_options)
      instance.register(klass, attachment_name, attachment_options)
    end

    def self.clear
      instance.clear
    end

    def self.names_for(klass)
      instance.names_for(klass)
    end

    def self.each_definition(&block)
      instance.each_definition(&block)
    end

    def self.definitions_for(klass)
      instance.definitions_for(klass)
    end

    def initialize
      clear
    end

    def register(klass, attachment_name, attachment_options)
      @attachments ||= {}
      @attachments[klass] ||= {}
      @attachments[klass][attachment_name] = attachment_options
    end

    def clear
      @attachments = Hash.new { |h,k| h[k] = {} }
    end

    def names_for(klass)
      @attachments[klass].keys
    end

    def each_definition
      @attachments.each do |klass, attachments|
        attachments.each do |name, options|
          yield klass, name, options
        end
      end
    end

    def definitions_for(klass)
      parent_classes = klass.ancestors.reverse
      parent_classes.each_with_object({}) do |ancestor, inherited_definitions|
        inherited_definitions.deep_merge! @attachments[ancestor]
      end
    end
  end
end


================================================
FILE: lib/paperclip/callbacks.rb
================================================
module Paperclip
  module Callbacks
    def self.included(base)
      base.extend(Defining)
      base.send(:include, Running)
    end

    module Defining
      def define_paperclip_callbacks(*callbacks)
        define_callbacks(*[callbacks, { terminator: hasta_la_vista_baby }].flatten)
        callbacks.each do |callback|
          eval <<-end_callbacks
            def before_#{callback}(*args, &blk)
              set_callback(:#{callback}, :before, *args, &blk)
            end
            def after_#{callback}(*args, &blk)
              set_callback(:#{callback}, :after, *args, &blk)
            end
          end_callbacks
        end
      end

      private

      def hasta_la_vista_baby
        lambda do |_, result|
          if result.respond_to?(:call)
            result.call == false
          else
            result == false
          end
        end
      end
    end

    module Running
      def run_paperclip_callbacks(callback, &block)
        run_callbacks(callback, &block)
      end
    end
  end
end


================================================
FILE: lib/paperclip/content_type_detector.rb
================================================
module Paperclip
  class ContentTypeDetector
    # The content-type detection strategy is as follows:
    #
    # 1. Blank/Empty files: If there's no filepath or the file is empty,
    #    provide a sensible default (application/octet-stream or inode/x-empty)
    #
    # 2. Calculated match: Return the first result that is found by both the
    #    `file` command and MIME::Types.
    #
    # 3. Standard types: Return the first standard (without an x- prefix) entry
    #    in MIME::Types
    #
    # 4. Experimental types: If there were no standard types in MIME::Types
    #    list, try to return the first experimental one
    #
    # 5. Raw `file` command: Just use the output of the `file` command raw, or
    #    a sensible default. This is cached from Step 2.

    EMPTY_TYPE = "inode/x-empty"
    SENSIBLE_DEFAULT = "application/octet-stream"

    def initialize(filepath)
      @filepath = filepath
    end

    # Returns a String describing the file's content type
    def detect
      if blank_name?
        SENSIBLE_DEFAULT
      elsif empty_file?
        EMPTY_TYPE
      elsif calculated_type_matches.any?
        calculated_type_matches.first
      else
        type_from_file_contents || SENSIBLE_DEFAULT
      end.to_s
    end

    private

    def blank_name?
      @filepath.nil? || @filepath.empty?
    end

    def empty_file?
      File.exist?(@filepath) && File.size(@filepath) == 0
    end

    alias :empty? :empty_file?

    def calculated_type_matches
      possible_types.select do |content_type|
        content_type == type_from_file_contents
      end
    end

    def possible_types
      MIME::Types.type_for(@filepath).collect(&:content_type)
    end

    def type_from_file_contents
      type_from_mime_magic || type_from_file_command
    rescue Errno::ENOENT => e
      Paperclip.log("Error while determining content type: #{e}")
      SENSIBLE_DEFAULT
    end

    def type_from_mime_magic
      @type_from_mime_magic ||= File.open(@filepath) do |file|
        MimeMagic.by_magic(file).try(:type)
      end
    end

    def type_from_file_command
      @type_from_file_command ||=
        FileCommandContentTypeDetector.new(@filepath).detect
    end
  end
end


================================================
FILE: lib/paperclip/errors.rb
================================================
module Paperclip
  # A base error class for Paperclip. Most of the error that will be thrown
  # from Paperclip will inherits from this class.
  class Error < StandardError
  end

  module Errors
    # Will be thrown when a storage method is not found.
    class StorageMethodNotFound < Paperclip::Error
    end

    # Will be thrown when a command or executable is not found.
    class CommandNotFoundError < Paperclip::Error
    end

    # Attachments require a content_type or file_name validator,
    # or to have explicitly opted out of them.
    class MissingRequiredValidatorError < Paperclip::Error
    end

    # Will be thrown when ImageMagic cannot determine the uploaded file's
    # metadata, usually this would mean the file is not an image. If you are
    # consistently receiving this error on PDFs make sure that you have
    # installed Ghostscript.
    class NotIdentifiedByImageMagickError < Paperclip::Error
    end

    # Will be thrown if the interpolation is creating an infinite loop. If you
    # are creating an interpolator which might cause an infinite loop, you
    # should be throwing this error upon the infinite loop as well.
    class InfiniteInterpolationError < Paperclip::Error
    end
  end
end


================================================
FILE: lib/paperclip/file_command_content_type_detector.rb
================================================
module Paperclip
  class FileCommandContentTypeDetector
    SENSIBLE_DEFAULT = "application/octet-stream"

    def initialize(filename)
      @filename = filename
    end

    def detect
      type_from_file_command
    end

    private

    def type_from_file_command
      # On BSDs, `file` doesn't give a result code of 1 if the file doesn't exist.
      type = begin
               Paperclip.run("file", "-b --mime :file", file: @filename)
             rescue Terrapin::CommandLineError => e
               Paperclip.log("Error while determining content type: #{e}")
               SENSIBLE_DEFAULT
             end

      if type.nil? || type.match(/\(.*?\)/)
        type = SENSIBLE_DEFAULT
      end
      type.split(/[:;\s]+/)[0]
    end
  end
end


================================================
FILE: lib/paperclip/filename_cleaner.rb
================================================
module Paperclip
  class FilenameCleaner
    def initialize(invalid_character_regex)
      @invalid_character_regex = invalid_character_regex
    end

    def call(filename)
      if @invalid_character_regex
        filename.gsub(@invalid_character_regex, "_")
      else
        filename
      end
    end
  end
end


================================================
FILE: lib/paperclip/geometry.rb
================================================
module Paperclip

  # Defines the geometry of an image.
  class Geometry
    attr_accessor :height, :width, :modifier

    EXIF_ROTATED_ORIENTATION_VALUES = [5, 6, 7, 8]

    # Gives a Geometry representing the given height and width
    def initialize(width = nil, height = nil, modifier = nil)
      if width.is_a?(Hash)
        options = width
        @height = options[:height].to_f
        @width = options[:width].to_f
        @modifier = options[:modifier]
        @orientation = options[:orientation].to_i
      else
        @height = height.to_f
        @width  = width.to_f
        @modifier = modifier
      end
    end

    # Extracts the Geometry from a file (or path to a file)
    def self.from_file(file)
      GeometryDetector.new(file).make
    end

    # Extracts the Geometry from a "WxH,O" string
    # Where W is the width, H is the height,
    # and O is the EXIF orientation
    def self.parse(string)
      GeometryParser.new(string).make
    end

    # Swaps the height and width if necessary
    def auto_orient
      if EXIF_ROTATED_ORIENTATION_VALUES.include?(@orientation)
        @height, @width = @width, @height
        @orientation -= 4
      end
    end

    # True if the dimensions represent a square
    def square?
      height == width
    end

    # True if the dimensions represent a horizontal rectangle
    def horizontal?
      height < width
    end

    # True if the dimensions represent a vertical rectangle
    def vertical?
      height > width
    end

    # The aspect ratio of the dimensions.
    def aspect
      width / height
    end

    # Returns the larger of the two dimensions
    def larger
      [height, width].max
    end

    # Returns the smaller of the two dimensions
    def smaller
      [height, width].min
    end

    # Returns the width and height in a format suitable to be passed to Geometry.parse
    def to_s
      s = ""
      s << width.to_i.to_s if width > 0
      s << "x#{height.to_i}" if height > 0
      s << modifier.to_s
      s
    end

    # Same as to_s
    def inspect
      to_s
    end

    # Returns the scaling and cropping geometries (in string-based ImageMagick format)
    # neccessary to transform this Geometry into the Geometry given. If crop is true,
    # then it is assumed the destination Geometry will be the exact final resolution.
    # In this case, the source Geometry is scaled so that an image containing the
    # destination Geometry would be completely filled by the source image, and any
    # overhanging image would be cropped. Useful for square thumbnail images. The cropping
    # is weighted at the center of the Geometry.
    def transformation_to dst, crop = false
      if crop
        ratio = Geometry.new( dst.width / self.width, dst.height / self.height )
        scale_geometry, scale = scaling(dst, ratio)
        crop_geometry         = cropping(dst, ratio, scale)
      else
        scale_geometry        = dst.to_s
      end

      [ scale_geometry, crop_geometry ]
    end

    # resize to a new geometry
    # @param geometry [String] the Paperclip geometry definition to resize to
    # @example
    #   Paperclip::Geometry.new(150, 150).resize_to('50x50!')
    #   #=> Paperclip::Geometry(50, 50)
    def resize_to(geometry)
      new_geometry = Paperclip::Geometry.parse geometry
      case new_geometry.modifier
      when '!', '#'
        new_geometry
      when '>'
        if new_geometry.width >= self.width && new_geometry.height >= self.height
          self
        else
          scale_to new_geometry
        end
      when '<'
        if new_geometry.width <= self.width || new_geometry.height <= self.height
          self
        else
          scale_to new_geometry
        end
      else
        scale_to new_geometry
      end
    end

    private

    def scaling dst, ratio
      if ratio.horizontal? || ratio.square?
        [ "%dx" % dst.width, ratio.width ]
      else
        [ "x%d" % dst.height, ratio.height ]
      end
    end

    def cropping dst, ratio, scale
      if ratio.horizontal? || ratio.square?
        "%dx%d+%d+%d" % [ dst.width, dst.height, 0, (self.height * scale - dst.height) / 2 ]
      else
        "%dx%d+%d+%d" % [ dst.width, dst.height, (self.width * scale - dst.width) / 2, 0 ]
      end
    end

    # scale to the requested geomet
Download .txt
gitextract_33jzlcve/

├── .codeclimate.yml
├── .github/
│   └── issue_template.md
├── .gitignore
├── .hound.yml
├── .rubocop.yml
├── .travis.yml
├── Appraisals
├── CONTRIBUTING.md
├── Gemfile
├── LICENSE
├── MIGRATING-ES.md
├── MIGRATING.md
├── NEWS
├── README.md
├── RELEASING.md
├── Rakefile
├── UPGRADING
├── features/
│   ├── basic_integration.feature
│   ├── migration.feature
│   ├── rake_tasks.feature
│   ├── step_definitions/
│   │   ├── attachment_steps.rb
│   │   ├── html_steps.rb
│   │   ├── rails_steps.rb
│   │   ├── s3_steps.rb
│   │   └── web_steps.rb
│   └── support/
│       ├── env.rb
│       ├── fakeweb.rb
│       ├── file_helpers.rb
│       ├── fixtures/
│       │   ├── boot_config.txt
│       │   ├── gemfile.txt
│       │   └── preinitializer.txt
│       ├── paths.rb
│       ├── rails.rb
│       └── selectors.rb
├── gemfiles/
│   ├── 4.2.gemfile
│   └── 5.0.gemfile
├── lib/
│   ├── generators/
│   │   └── paperclip/
│   │       ├── USAGE
│   │       ├── paperclip_generator.rb
│   │       └── templates/
│   │           └── paperclip_migration.rb.erb
│   ├── paperclip/
│   │   ├── attachment.rb
│   │   ├── attachment_registry.rb
│   │   ├── callbacks.rb
│   │   ├── content_type_detector.rb
│   │   ├── errors.rb
│   │   ├── file_command_content_type_detector.rb
│   │   ├── filename_cleaner.rb
│   │   ├── geometry.rb
│   │   ├── geometry_detector_factory.rb
│   │   ├── geometry_parser_factory.rb
│   │   ├── glue.rb
│   │   ├── has_attached_file.rb
│   │   ├── helpers.rb
│   │   ├── interpolations/
│   │   │   └── plural_cache.rb
│   │   ├── interpolations.rb
│   │   ├── io_adapters/
│   │   │   ├── abstract_adapter.rb
│   │   │   ├── attachment_adapter.rb
│   │   │   ├── data_uri_adapter.rb
│   │   │   ├── empty_string_adapter.rb
│   │   │   ├── file_adapter.rb
│   │   │   ├── http_url_proxy_adapter.rb
│   │   │   ├── identity_adapter.rb
│   │   │   ├── nil_adapter.rb
│   │   │   ├── registry.rb
│   │   │   ├── stringio_adapter.rb
│   │   │   ├── uploaded_file_adapter.rb
│   │   │   └── uri_adapter.rb
│   │   ├── locales/
│   │   │   └── en.yml
│   │   ├── logger.rb
│   │   ├── matchers/
│   │   │   ├── have_attached_file_matcher.rb
│   │   │   ├── validate_attachment_content_type_matcher.rb
│   │   │   ├── validate_attachment_presence_matcher.rb
│   │   │   └── validate_attachment_size_matcher.rb
│   │   ├── matchers.rb
│   │   ├── media_type_spoof_detector.rb
│   │   ├── missing_attachment_styles.rb
│   │   ├── processor.rb
│   │   ├── processor_helpers.rb
│   │   ├── rails_environment.rb
│   │   ├── railtie.rb
│   │   ├── schema.rb
│   │   ├── storage/
│   │   │   ├── filesystem.rb
│   │   │   ├── fog.rb
│   │   │   └── s3.rb
│   │   ├── storage.rb
│   │   ├── style.rb
│   │   ├── tempfile.rb
│   │   ├── tempfile_factory.rb
│   │   ├── thumbnail.rb
│   │   ├── url_generator.rb
│   │   ├── validators/
│   │   │   ├── attachment_content_type_validator.rb
│   │   │   ├── attachment_file_name_validator.rb
│   │   │   ├── attachment_file_type_ignorance_validator.rb
│   │   │   ├── attachment_presence_validator.rb
│   │   │   ├── attachment_size_validator.rb
│   │   │   └── media_type_spoof_detection_validator.rb
│   │   ├── validators.rb
│   │   └── version.rb
│   ├── paperclip.rb
│   └── tasks/
│       └── paperclip.rake
├── paperclip.gemspec
├── shoulda_macros/
│   └── paperclip.rb
└── spec/
    ├── database.yml
    ├── paperclip/
    │   ├── attachment_definitions_spec.rb
    │   ├── attachment_processing_spec.rb
    │   ├── attachment_registry_spec.rb
    │   ├── attachment_spec.rb
    │   ├── content_type_detector_spec.rb
    │   ├── file_command_content_type_detector_spec.rb
    │   ├── filename_cleaner_spec.rb
    │   ├── geometry_detector_spec.rb
    │   ├── geometry_parser_spec.rb
    │   ├── geometry_spec.rb
    │   ├── glue_spec.rb
    │   ├── has_attached_file_spec.rb
    │   ├── integration_spec.rb
    │   ├── interpolations_spec.rb
    │   ├── io_adapters/
    │   │   ├── abstract_adapter_spec.rb
    │   │   ├── attachment_adapter_spec.rb
    │   │   ├── data_uri_adapter_spec.rb
    │   │   ├── empty_string_adapter_spec.rb
    │   │   ├── file_adapter_spec.rb
    │   │   ├── http_url_proxy_adapter_spec.rb
    │   │   ├── identity_adapter_spec.rb
    │   │   ├── nil_adapter_spec.rb
    │   │   ├── registry_spec.rb
    │   │   ├── stringio_adapter_spec.rb
    │   │   ├── uploaded_file_adapter_spec.rb
    │   │   └── uri_adapter_spec.rb
    │   ├── matchers/
    │   │   ├── have_attached_file_matcher_spec.rb
    │   │   ├── validate_attachment_content_type_matcher_spec.rb
    │   │   ├── validate_attachment_presence_matcher_spec.rb
    │   │   └── validate_attachment_size_matcher_spec.rb
    │   ├── media_type_spoof_detector_spec.rb
    │   ├── meta_class_spec.rb
    │   ├── paperclip_missing_attachment_styles_spec.rb
    │   ├── paperclip_spec.rb
    │   ├── plural_cache_spec.rb
    │   ├── processor_helpers_spec.rb
    │   ├── processor_spec.rb
    │   ├── rails_environment_spec.rb
    │   ├── rake_spec.rb
    │   ├── schema_spec.rb
    │   ├── storage/
    │   │   ├── filesystem_spec.rb
    │   │   ├── fog_spec.rb
    │   │   ├── s3_live_spec.rb
    │   │   └── s3_spec.rb
    │   ├── style_spec.rb
    │   ├── tempfile_factory_spec.rb
    │   ├── tempfile_spec.rb
    │   ├── thumbnail_spec.rb
    │   ├── url_generator_spec.rb
    │   ├── validators/
    │   │   ├── attachment_content_type_validator_spec.rb
    │   │   ├── attachment_file_name_validator_spec.rb
    │   │   ├── attachment_presence_validator_spec.rb
    │   │   ├── attachment_size_validator_spec.rb
    │   │   └── media_type_spoof_detection_validator_spec.rb
    │   └── validators_spec.rb
    ├── spec_helper.rb
    └── support/
        ├── assertions.rb
        ├── fake_model.rb
        ├── fake_rails.rb
        ├── fixtures/
        │   ├── animated
        │   ├── animated.unknown
        │   ├── empty.html
        │   ├── empty.xlsx
        │   ├── fog.yml
        │   ├── s3.yml
        │   └── text.txt
        ├── matchers/
        │   ├── accept.rb
        │   ├── exist.rb
        │   └── have_column.rb
        ├── mock_attachment.rb
        ├── mock_interpolator.rb
        ├── mock_url_generator_builder.rb
        ├── model_reconstruction.rb
        ├── reporting.rb
        ├── test_data.rb
        └── version_helper.rb
Download .txt
SYMBOL INDEX (805 symbols across 103 files)

FILE: features/step_definitions/attachment_steps.rb
  type AttachmentHelpers (line 1) | module AttachmentHelpers
    function fixture_path (line 2) | def fixture_path(filename)
    function attachment_path (line 6) | def attachment_path(filename)

FILE: features/step_definitions/rails_steps.rb
  function attach_attachment (line 71) | def attach_attachment(name, definition = nil)
  function transform_file (line 229) | def transform_file(filename)

FILE: features/step_definitions/web_steps.rb
  type WithinHelpers (line 27) | module WithinHelpers
    function with_scope (line 28) | def with_scope(locator)

FILE: features/support/fakeweb.rb
  type FakeWeb (line 5) | module FakeWeb
    class StubSocket (line 6) | class StubSocket
      method read_timeout= (line 7) | def read_timeout=(_ignored)
      method continue_timeout= (line 10) | def continue_timeout=(_ignored)

FILE: features/support/file_helpers.rb
  type FileHelpers (line 1) | module FileHelpers
    function append_to (line 2) | def append_to(path, contents)
    function append_to_gemfile (line 11) | def append_to_gemfile(contents)
    function comment_out_gem_in_gemfile (line 15) | def comment_out_gem_in_gemfile(gemname)
    function read_from_web (line 23) | def read_from_web(url)

FILE: features/support/paths.rb
  type NavigationHelpers (line 1) | module NavigationHelpers
    function path_to (line 8) | def path_to(page_name)

FILE: features/support/rails.rb
  type RailsCommandHelpers (line 26) | module RailsCommandHelpers
    function framework_version? (line 27) | def framework_version?(version_string)
    function framework_version (line 31) | def framework_version
    function framework_major_version (line 35) | def framework_major_version

FILE: features/support/selectors.rb
  type HtmlSelectorsHelpers (line 1) | module HtmlSelectorsHelpers
    function selector_for (line 8) | def selector_for(locator)

FILE: lib/generators/paperclip/paperclip_generator.rb
  class PaperclipGenerator (line 3) | class PaperclipGenerator < ActiveRecord::Generators::Base
    method source_root (line 11) | def self.source_root
    method generate_migration (line 15) | def generate_migration
    method migration_name (line 21) | def migration_name
    method migration_file_name (line 25) | def migration_file_name
    method migration_class_name (line 29) | def migration_class_name
    method migration_version (line 33) | def migration_version

FILE: lib/paperclip.rb
  type Paperclip (line 76) | module Paperclip
    function options (line 91) | def self.options
    function io_adapters= (line 105) | def self.io_adapters=(new_registry)
    function io_adapters (line 109) | def self.io_adapters
    type ClassMethods (line 113) | module ClassMethods
      function has_attached_file (line 197) | def has_attached_file(name, options = {})

FILE: lib/paperclip/attachment.rb
  type Paperclip (line 6) | module Paperclip
    class Attachment (line 10) | class Attachment
      method default_options (line 11) | def self.default_options
      method initialize (line 72) | def initialize(name, instance, options = {})
      method assign (line 99) | def assign(uploaded_file)
      method url (line 143) | def url(style_name = default_style, options = {})
      method default_options (line 151) | def default_options
      method expiring_url (line 161) | def expiring_url(time = 3600, style_name = default_style)
      method path (line 169) | def path(style_name = default_style)
      method staged_path (line 175) | def staged_path(style_name = default_style)
      method staged? (line 182) | def staged?
      method to_s (line 187) | def to_s style_name = default_style
      method as_json (line 191) | def as_json(options = nil)
      method default_style (line 195) | def default_style
      method styles (line 199) | def styles
      method only_process (line 212) | def only_process
      method processors (line 218) | def processors
      method errors (line 229) | def errors
      method dirty? (line 234) | def dirty?
      method save (line 240) | def save
      method clear (line 254) | def clear(*styles_to_clear)
      method destroy (line 267) | def destroy
      method uploaded_file (line 273) | def uploaded_file
      method original_filename (line 279) | def original_filename
      method size (line 285) | def size
      method fingerprint (line 291) | def fingerprint
      method content_type (line 297) | def content_type
      method created_at (line 303) | def created_at
      method updated_at (line 312) | def updated_at
      method time_zone (line 319) | def time_zone
      method hash_key (line 325) | def hash_key(style_name = default_style)
      method reprocess! (line 339) | def reprocess!(*style_args)
      method file? (line 362) | def file?
      method blank? (line 368) | def blank?
      method instance_respond_to? (line 374) | def instance_respond_to?(attr)
      method instance_write (line 381) | def instance_write(attr, value)
      method instance_read (line 390) | def instance_read(attr)
      method path_option (line 399) | def path_option
      method active_validator_classes (line 403) | def active_validator_classes
      method missing_required_validator? (line 407) | def missing_required_validator?
      method ensure_required_validations! (line 411) | def ensure_required_validations!
      method ensure_required_accessors! (line 417) | def ensure_required_accessors! #:nodoc:
      method log (line 425) | def log message #:nodoc:
      method initialize_storage (line 429) | def initialize_storage #:nodoc:
      method assign_attributes (line 439) | def assign_attributes
      method assign_file_information (line 446) | def assign_file_information
      method assign_fingerprint (line 452) | def assign_fingerprint
      method assign_timestamps (line 458) | def assign_timestamps
      method post_process_file (line 466) | def post_process_file
      method dirty! (line 474) | def dirty!
      method reset_file_if_original_reprocessed (line 478) | def reset_file_if_original_reprocessed
      method reset_updater (line 484) | def reset_updater
      method updater (line 490) | def updater
      method extra_options_for (line 494) | def extra_options_for(style) #:nodoc:
      method extra_source_file_options_for (line 498) | def extra_source_file_options_for(style) #:nodoc:
      method process_options (line 502) | def process_options(options_type, style) #:nodoc:
      method post_process (line 511) | def post_process(*style_args) #:nodoc:
      method post_process_styles (line 523) | def post_process_styles(*style_args) #:nodoc:
      method post_process_style (line 530) | def post_process_style(name, style) #:nodoc:
      method process_style? (line 560) | def process_style?(style_name, style_args) #:nodoc:
      method interpolate (line 564) | def interpolate(pattern, style_name = default_style) #:nodoc:
      method queue_some_for_delete (line 568) | def queue_some_for_delete(*styles)
      method queue_all_for_delete (line 574) | def queue_all_for_delete #:nodoc:
      method flush_errors (line 589) | def flush_errors #:nodoc:
      method after_flush_writes (line 596) | def after_flush_writes
      method unlink_files (line 600) | def unlink_files(files)
      method filename_cleaner (line 614) | def filename_cleaner
      method cleanup_filename (line 618) | def cleanup_filename(filename)
      method able_to_store_created_at? (line 623) | def able_to_store_created_at?
      method has_enabled_but_unset_created_at? (line 628) | def has_enabled_but_unset_created_at?

FILE: lib/paperclip/attachment_registry.rb
  type Paperclip (line 3) | module Paperclip
    class AttachmentRegistry (line 4) | class AttachmentRegistry
      method register (line 7) | def self.register(klass, attachment_name, attachment_options)
      method clear (line 11) | def self.clear
      method names_for (line 15) | def self.names_for(klass)
      method each_definition (line 19) | def self.each_definition(&block)
      method definitions_for (line 23) | def self.definitions_for(klass)
      method initialize (line 27) | def initialize
      method register (line 31) | def register(klass, attachment_name, attachment_options)
      method clear (line 37) | def clear
      method names_for (line 41) | def names_for(klass)
      method each_definition (line 45) | def each_definition
      method definitions_for (line 53) | def definitions_for(klass)

FILE: lib/paperclip/callbacks.rb
  type Paperclip (line 1) | module Paperclip
    type Callbacks (line 2) | module Callbacks
      function included (line 3) | def self.included(base)
      type Defining (line 8) | module Defining
        function define_paperclip_callbacks (line 9) | def define_paperclip_callbacks(*callbacks)
        function hasta_la_vista_baby (line 25) | def hasta_la_vista_baby
      type Running (line 36) | module Running
        function run_paperclip_callbacks (line 37) | def run_paperclip_callbacks(callback, &block)

FILE: lib/paperclip/content_type_detector.rb
  type Paperclip (line 1) | module Paperclip
    class ContentTypeDetector (line 2) | class ContentTypeDetector
      method initialize (line 23) | def initialize(filepath)
      method detect (line 28) | def detect
      method blank_name? (line 42) | def blank_name?
      method empty_file? (line 46) | def empty_file?
      method calculated_type_matches (line 52) | def calculated_type_matches
      method possible_types (line 58) | def possible_types
      method type_from_file_contents (line 62) | def type_from_file_contents
      method type_from_mime_magic (line 69) | def type_from_mime_magic
      method type_from_file_command (line 75) | def type_from_file_command

FILE: lib/paperclip/errors.rb
  type Paperclip (line 1) | module Paperclip
    class Error (line 4) | class Error < StandardError
    type Errors (line 7) | module Errors
      class StorageMethodNotFound (line 9) | class StorageMethodNotFound < Paperclip::Error
      class CommandNotFoundError (line 13) | class CommandNotFoundError < Paperclip::Error
      class MissingRequiredValidatorError (line 18) | class MissingRequiredValidatorError < Paperclip::Error
      class NotIdentifiedByImageMagickError (line 25) | class NotIdentifiedByImageMagickError < Paperclip::Error
      class InfiniteInterpolationError (line 31) | class InfiniteInterpolationError < Paperclip::Error

FILE: lib/paperclip/file_command_content_type_detector.rb
  type Paperclip (line 1) | module Paperclip
    class FileCommandContentTypeDetector (line 2) | class FileCommandContentTypeDetector
      method initialize (line 5) | def initialize(filename)
      method detect (line 9) | def detect
      method type_from_file_command (line 15) | def type_from_file_command

FILE: lib/paperclip/filename_cleaner.rb
  type Paperclip (line 1) | module Paperclip
    class FilenameCleaner (line 2) | class FilenameCleaner
      method initialize (line 3) | def initialize(invalid_character_regex)
      method call (line 7) | def call(filename)

FILE: lib/paperclip/geometry.rb
  type Paperclip (line 1) | module Paperclip
    class Geometry (line 4) | class Geometry
      method initialize (line 10) | def initialize(width = nil, height = nil, modifier = nil)
      method from_file (line 25) | def self.from_file(file)
      method parse (line 32) | def self.parse(string)
      method auto_orient (line 37) | def auto_orient
      method square? (line 45) | def square?
      method horizontal? (line 50) | def horizontal?
      method vertical? (line 55) | def vertical?
      method aspect (line 60) | def aspect
      method larger (line 65) | def larger
      method smaller (line 70) | def smaller
      method to_s (line 75) | def to_s
      method inspect (line 84) | def inspect
      method transformation_to (line 95) | def transformation_to dst, crop = false
      method resize_to (line 112) | def resize_to(geometry)
      method scaling (line 136) | def scaling dst, ratio
      method cropping (line 144) | def cropping dst, ratio, scale
      method scale_to (line 153) | def scale_to(new_geometry)

FILE: lib/paperclip/geometry_detector_factory.rb
  type Paperclip (line 1) | module Paperclip
    class GeometryDetector (line 2) | class GeometryDetector
      method initialize (line 3) | def initialize(file)
      method make (line 8) | def make
      method geometry_string (line 15) | def geometry_string
      method path (line 34) | def path
      method raise_if_blank_file (line 38) | def raise_if_blank_file
      method raise_because_imagemagick_missing (line 44) | def raise_because_imagemagick_missing

FILE: lib/paperclip/geometry_parser_factory.rb
  type Paperclip (line 1) | module Paperclip
    class GeometryParser (line 2) | class GeometryParser
      method initialize (line 4) | def initialize(string)
      method make (line 8) | def make
      method match (line 21) | def match

FILE: lib/paperclip/glue.rb
  type Paperclip (line 5) | module Paperclip
    type Glue (line 6) | module Glue
      function included (line 7) | def self.included(base)

FILE: lib/paperclip/has_attached_file.rb
  type Paperclip (line 1) | module Paperclip
    class HasAttachedFile (line 2) | class HasAttachedFile
      method define_on (line 3) | def self.define_on(klass, name, options)
      method initialize (line 7) | def initialize(klass, name, options)
      method define (line 13) | def define
      method define_flush_errors (line 26) | def define_flush_errors
      method define_getters (line 33) | def define_getters
      method define_instance_getter (line 38) | def define_instance_getter
      method define_class_getter (line 59) | def define_class_getter
      method define_setter (line 63) | def define_setter
      method define_query (line 70) | def define_query
      method register_new_attachment (line 77) | def register_new_attachment
      method add_required_validations (line 81) | def add_required_validations
      method add_active_record_callbacks (line 90) | def add_active_record_callbacks
      method add_paperclip_callbacks (line 103) | def add_paperclip_callbacks
      type ClassMethods (line 109) | module ClassMethods
        function attachment_definitions (line 110) | def attachment_definitions

FILE: lib/paperclip/helpers.rb
  type Paperclip (line 1) | module Paperclip
    type Helpers (line 2) | module Helpers
      function configure (line 3) | def configure
      function interpolates (line 7) | def interpolates key, &block
      function run (line 28) | def run(cmd, arguments = "", interpolation_values = {}, local_option...
      function each_instance_with_attachment (line 40) | def each_instance_with_attachment(klass, name)
      function class_for (line 46) | def class_for(class_name)
      function reset_duplicate_clash_check! (line 56) | def reset_duplicate_clash_check!

FILE: lib/paperclip/interpolations.rb
  type Paperclip (line 1) | module Paperclip
    type Interpolations (line 6) | module Interpolations
      function []= (line 12) | def self.[]= name, block
      function [] (line 19) | def self.[] name
      function all (line 24) | def self.all
      function interpolate (line 32) | def self.interpolate pattern, *args
      function interpolators_cache (line 41) | def self.interpolators_cache
      function plural_cache (line 45) | def self.plural_cache
      function filename (line 50) | def filename attachment, style_name
      function url (line 58) | def url attachment, style_name
      function timestamp (line 68) | def timestamp attachment, style_name
      function updated_at (line 74) | def updated_at attachment, style_name
      function rails_root (line 79) | def rails_root attachment, style_name
      function rails_env (line 84) | def rails_env attachment, style_name
      function class (line 92) | def class attachment = nil, style_name = nil
      function basename (line 98) | def basename attachment, style_name
      function extension (line 105) | def extension attachment, style_name
      function dotextension (line 113) | def dotextension attachment, style_name
      function content_type_extension (line 125) | def content_type_extension attachment, style_name
      function id (line 150) | def id attachment, style_name
      function param (line 155) | def param attachment, style_name
      function fingerprint (line 160) | def fingerprint attachment, style_name
      function hash (line 166) | def hash attachment=nil, style_name=nil
      function id_partition (line 176) | def id_partition attachment, style_name
      function attachment (line 193) | def attachment attachment, style_name
      function style (line 198) | def style attachment, style_name

FILE: lib/paperclip/interpolations/plural_cache.rb
  type Paperclip (line 1) | module Paperclip
    type Interpolations (line 2) | module Interpolations
      class PluralCache (line 3) | class PluralCache
        method initialize (line 4) | def initialize
        method pluralize_symbol (line 9) | def pluralize_symbol(symbol)
        method underscore_and_pluralize_class (line 13) | def underscore_and_pluralize_class(klass)

FILE: lib/paperclip/io_adapters/abstract_adapter.rb
  type Paperclip (line 3) | module Paperclip
    class AbstractAdapter (line 4) | class AbstractAdapter
      method initialize (line 11) | def initialize(target, options = {})
      method fingerprint (line 16) | def fingerprint
      method read (line 27) | def read(length = nil, buffer = nil)
      method inspect (line 31) | def inspect
      method original_filename= (line 35) | def original_filename=(new_filename)
      method nil? (line 40) | def nil?
      method assignment? (line 44) | def assignment?
      method destination (line 50) | def destination
      method copy_to_tempfile (line 54) | def copy_to_tempfile(src)
      method link_or_copy_file (line 59) | def link_or_copy_file(src, dest)

FILE: lib/paperclip/io_adapters/attachment_adapter.rb
  type Paperclip (line 1) | module Paperclip
    class AttachmentAdapter (line 2) | class AttachmentAdapter < AbstractAdapter
      method register (line 3) | def self.register
      method initialize (line 9) | def initialize(target, options = {})
      method cache_current_values (line 23) | def cache_current_values
      method copy_to_tempfile (line 30) | def copy_to_tempfile(source)

FILE: lib/paperclip/io_adapters/data_uri_adapter.rb
  type Paperclip (line 1) | module Paperclip
    class DataUriAdapter (line 2) | class DataUriAdapter < StringioAdapter
      method register (line 3) | def self.register
      method initialize (line 11) | def initialize(target_uri, options = {})
      method extract_target (line 17) | def extract_target(uri)

FILE: lib/paperclip/io_adapters/empty_string_adapter.rb
  type Paperclip (line 1) | module Paperclip
    class EmptyStringAdapter (line 2) | class EmptyStringAdapter < AbstractAdapter
      method register (line 3) | def self.register
      method nil? (line 9) | def nil?
      method assignment? (line 13) | def assignment?

FILE: lib/paperclip/io_adapters/file_adapter.rb
  type Paperclip (line 1) | module Paperclip
    class FileAdapter (line 2) | class FileAdapter < AbstractAdapter
      method register (line 3) | def self.register
      method initialize (line 9) | def initialize(target, options = {})
      method cache_current_values (line 16) | def cache_current_values

FILE: lib/paperclip/io_adapters/http_url_proxy_adapter.rb
  type Paperclip (line 1) | module Paperclip
    class HttpUrlProxyAdapter (line 2) | class HttpUrlProxyAdapter < UriAdapter
      method register (line 3) | def self.register
      method initialize (line 11) | def initialize(target, options = {})

FILE: lib/paperclip/io_adapters/identity_adapter.rb
  type Paperclip (line 1) | module Paperclip
    class IdentityAdapter (line 2) | class IdentityAdapter < AbstractAdapter
      method register (line 3) | def self.register
      method initialize (line 9) | def initialize
      method new (line 12) | def new(target, _)

FILE: lib/paperclip/io_adapters/nil_adapter.rb
  type Paperclip (line 1) | module Paperclip
    class NilAdapter (line 2) | class NilAdapter < AbstractAdapter
      method register (line 3) | def self.register
      method initialize (line 9) | def initialize(_target, _options = {}); end
      method original_filename (line 11) | def original_filename
      method content_type (line 15) | def content_type
      method size (line 19) | def size
      method nil? (line 23) | def nil?
      method read (line 27) | def read(*_args)
      method eof? (line 31) | def eof?

FILE: lib/paperclip/io_adapters/registry.rb
  type Paperclip (line 1) | module Paperclip
    class AdapterRegistry (line 2) | class AdapterRegistry
      class NoHandlerError (line 3) | class NoHandlerError < Paperclip::Error; end
      method initialize (line 7) | def initialize
      method register (line 11) | def register(handler_class, &block)
      method unregister (line 15) | def unregister(handler_class)
      method handler_for (line 19) | def handler_for(target)
      method registered? (line 26) | def registered?(target)
      method for (line 32) | def for(target, options = {})

FILE: lib/paperclip/io_adapters/stringio_adapter.rb
  type Paperclip (line 1) | module Paperclip
    class StringioAdapter (line 2) | class StringioAdapter < AbstractAdapter
      method register (line 3) | def self.register
      method initialize (line 9) | def initialize(target, options = {})
      method cache_current_values (line 18) | def cache_current_values
      method copy_to_tempfile (line 26) | def copy_to_tempfile(source)

FILE: lib/paperclip/io_adapters/uploaded_file_adapter.rb
  type Paperclip (line 1) | module Paperclip
    class UploadedFileAdapter (line 2) | class UploadedFileAdapter < AbstractAdapter
      method register (line 3) | def self.register
      method initialize (line 9) | def initialize(target, options = {})
      method cache_current_values (line 26) | def cache_current_values
      method content_type_detector (line 32) | def content_type_detector
      method determine_content_type (line 36) | def determine_content_type

FILE: lib/paperclip/io_adapters/uri_adapter.rb
  type Paperclip (line 3) | module Paperclip
    class UriAdapter (line 4) | class UriAdapter < AbstractAdapter
      method register (line 7) | def self.register
      method initialize (line 13) | def initialize(target, options = {})
      method cache_current_values (line 22) | def cache_current_values
      method content_type_from_content (line 30) | def content_type_from_content
      method filename_from_content_disposition (line 34) | def filename_from_content_disposition
      method filename_from_path (line 45) | def filename_from_path
      method default_filename (line 49) | def default_filename
      method download_content (line 53) | def download_content
      method copy_to_tempfile (line 59) | def copy_to_tempfile(src)

FILE: lib/paperclip/logger.rb
  type Paperclip (line 1) | module Paperclip
    type Logger (line 2) | module Logger
      function log (line 5) | def log(message)
      function logger (line 9) | def logger #:nodoc:
      function logger= (line 13) | def logger=(logger)
      function logging? (line 17) | def logging? #:nodoc:

FILE: lib/paperclip/matchers.rb
  type Paperclip (line 6) | module Paperclip
    type Shoulda (line 7) | module Shoulda
      type Matchers (line 61) | module Matchers

FILE: lib/paperclip/matchers/have_attached_file_matcher.rb
  type Paperclip (line 1) | module Paperclip
    type Shoulda (line 2) | module Shoulda
      type Matchers (line 3) | module Matchers
        function have_attached_file (line 11) | def have_attached_file name
        class HaveAttachedFileMatcher (line 15) | class HaveAttachedFileMatcher
          method initialize (line 16) | def initialize attachment_name
          method matches? (line 20) | def matches? subject
          method failure_message (line 26) | def failure_message
          method failure_message_when_negated (line 30) | def failure_message_when_negated
          method description (line 35) | def description
          method responds? (line 41) | def responds?
          method has_column? (line 48) | def has_column?

FILE: lib/paperclip/matchers/validate_attachment_content_type_matcher.rb
  type Paperclip (line 1) | module Paperclip
    type Shoulda (line 2) | module Shoulda
      type Matchers (line 3) | module Matchers
        function validate_attachment_content_type (line 13) | def validate_attachment_content_type name
        class ValidateAttachmentContentTypeMatcher (line 17) | class ValidateAttachmentContentTypeMatcher
          method initialize (line 18) | def initialize attachment_name
          method allowing (line 24) | def allowing *types
          method rejecting (line 29) | def rejecting *types
          method matches? (line 34) | def matches? subject
          method failure_message (line 41) | def failure_message
          method description (line 49) | def description
          method accepted_types_and_failures (line 55) | def accepted_types_and_failures
          method rejected_types_and_failures (line 66) | def rejected_types_and_failures
          method expected_attachment (line 78) | def expected_attachment
          method type_allowed? (line 82) | def type_allowed?(type)
          method allowed_types_allowed? (line 88) | def allowed_types_allowed?
          method rejected_types_rejected? (line 93) | def rejected_types_rejected?

FILE: lib/paperclip/matchers/validate_attachment_presence_matcher.rb
  type Paperclip (line 1) | module Paperclip
    type Shoulda (line 2) | module Shoulda
      type Matchers (line 3) | module Matchers
        function validate_attachment_presence (line 10) | def validate_attachment_presence name
        class ValidateAttachmentPresenceMatcher (line 14) | class ValidateAttachmentPresenceMatcher
          method initialize (line 15) | def initialize attachment_name
          method matches? (line 19) | def matches? subject
          method failure_message (line 25) | def failure_message
          method failure_message_when_negated (line 29) | def failure_message_when_negated
          method description (line 34) | def description
          method error_when_not_valid? (line 40) | def error_when_not_valid?
          method no_error_when_valid? (line 46) | def no_error_when_valid?

FILE: lib/paperclip/matchers/validate_attachment_size_matcher.rb
  type Paperclip (line 1) | module Paperclip
    type Shoulda (line 2) | module Shoulda
      type Matchers (line 3) | module Matchers
        function validate_attachment_size (line 14) | def validate_attachment_size name
        class ValidateAttachmentSizeMatcher (line 18) | class ValidateAttachmentSizeMatcher
          method initialize (line 19) | def initialize attachment_name
          method less_than (line 23) | def less_than size
          method greater_than (line 28) | def greater_than size
          method in (line 33) | def in range
          method matches? (line 38) | def matches? subject
          method failure_message (line 44) | def failure_message
          method failure_message_when_negated (line 48) | def failure_message_when_negated
          method description (line 53) | def description
          method override_method (line 59) | def override_method object, method, &replacement
          method passes_validation_with_size (line 65) | def passes_validation_with_size(new_size)
          method lower_than_low? (line 78) | def lower_than_low?
          method higher_than_low? (line 82) | def higher_than_low?
          method lower_than_high? (line 86) | def lower_than_high?
          method higher_than_high? (line 90) | def higher_than_high?

FILE: lib/paperclip/media_type_spoof_detector.rb
  type Paperclip (line 1) | module Paperclip
    class MediaTypeSpoofDetector (line 2) | class MediaTypeSpoofDetector
      method using (line 3) | def self.using(file, name, content_type)
      method initialize (line 7) | def initialize(file, name, content_type)
      method spoofed? (line 13) | def spoofed?
      method has_name? (line 24) | def has_name?
      method has_extension? (line 28) | def has_extension?
      method media_type_mismatch? (line 32) | def media_type_mismatch?
      method extension_type_mismatch? (line 36) | def extension_type_mismatch?
      method calculated_type_mismatch? (line 42) | def calculated_type_mismatch?
      method mapping_override_mismatch? (line 47) | def mapping_override_mismatch?
      method supplied_content_type (line 52) | def supplied_content_type
      method supplied_media_type (line 56) | def supplied_media_type
      method content_types_from_name (line 60) | def content_types_from_name
      method media_types_from_name (line 64) | def media_types_from_name
      method calculated_content_type (line 68) | def calculated_content_type
      method calculated_media_type (line 72) | def calculated_media_type
      method type_from_file_command (line 76) | def type_from_file_command
      method mapped_content_type (line 85) | def mapped_content_type
      method filename_extension (line 89) | def filename_extension

FILE: lib/paperclip/missing_attachment_styles.rb
  type Paperclip (line 4) | module Paperclip
    function registered_attachments_styles_path (line 7) | def registered_attachments_styles_path
    function get_registered_attachments_styles (line 13) | def self.get_registered_attachments_styles
    function save_current_attachments_styles! (line 20) | def self.save_current_attachments_styles!
    function current_attachments_styles (line 35) | def self.current_attachments_styles
    function missing_attachments_styles (line 59) | def self.missing_attachments_styles

FILE: lib/paperclip/processor.rb
  type Paperclip (line 1) | module Paperclip
    class Processor (line 21) | class Processor
      method initialize (line 24) | def initialize file, options = {}, attachment = nil
      method make (line 30) | def make
      method make (line 33) | def self.make file, options = {}, attachment = nil
      method convert (line 39) | def convert(arguments = "", local_options = {})
      method identify (line 49) | def identify(arguments = "", local_options = {})

FILE: lib/paperclip/processor_helpers.rb
  type Paperclip (line 1) | module Paperclip
    type ProcessorHelpers (line 2) | module ProcessorHelpers
      class NoSuchProcessor (line 3) | class NoSuchProcessor < StandardError; end
      function processor (line 5) | def processor(name) #:nodoc:
      function load_processor (line 17) | def load_processor(name)
      function clear_processors! (line 33) | def clear_processors!
      function register_processor (line 45) | def register_processor(name, processor)

FILE: lib/paperclip/rails_environment.rb
  type Paperclip (line 1) | module Paperclip
    class RailsEnvironment (line 2) | class RailsEnvironment
      method get (line 3) | def self.get
      method get (line 7) | def get
      method rails_exists? (line 17) | def rails_exists?
      method rails_environment_exists? (line 21) | def rails_environment_exists?

FILE: lib/paperclip/railtie.rb
  type Paperclip (line 4) | module Paperclip
    class Railtie (line 7) | class Railtie < Rails::Railtie
      method insert (line 22) | def self.insert
    class Railtie (line 21) | class Railtie
      method insert (line 22) | def self.insert

FILE: lib/paperclip/schema.rb
  type Paperclip (line 3) | module Paperclip
    type Schema (line 5) | module Schema
      function included (line 11) | def self.included(base)
      type Statements (line 18) | module Statements
        function add_attachment (line 19) | def add_attachment(table_name, *attachment_names)
        function remove_attachment (line 32) | def remove_attachment(table_name, *attachment_names)
        function drop_attached_file (line 42) | def drop_attached_file(*args)
      type TableDefinition (line 48) | module TableDefinition
        function attachment (line 49) | def attachment(*attachment_names)
        function has_attached_file (line 59) | def has_attached_file(*attachment_names)
      type CommandRecorder (line 65) | module CommandRecorder
        function add_attachment (line 66) | def add_attachment(*args)
        function invert_add_attachment (line 72) | def invert_add_attachment(args)

FILE: lib/paperclip/storage/filesystem.rb
  type Paperclip (line 1) | module Paperclip
    type Storage (line 2) | module Storage
      type Filesystem (line 24) | module Filesystem
        function extended (line 25) | def self.extended base
        function exists? (line 28) | def exists?(style_name = default_style)
        function flush_writes (line 36) | def flush_writes #:nodoc:
        function flush_deletes (line 60) | def flush_deletes #:nodoc:
        function copy_to_local_file (line 84) | def copy_to_local_file(style, local_dest_path)
        function move_file (line 90) | def move_file(src, dest)

FILE: lib/paperclip/storage/fog.rb
  type Paperclip (line 1) | module Paperclip
    type Storage (line 2) | module Storage
      type Fog (line 41) | module Fog
        function extended (line 42) | def self.extended base
        function exists? (line 63) | def exists?(style = default_style)
        function fog_credentials (line 71) | def fog_credentials
        function fog_file (line 75) | def fog_file
        function fog_public (line 88) | def fog_public(style = default_style)
        function flush_writes (line 103) | def flush_writes
        function flush_deletes (line 132) | def flush_deletes
        function public_url (line 140) | def public_url(style = default_style)
        function expiring_url (line 152) | def expiring_url(time = (Time.now + 3600), style_name = default_st...
        function parse_credentials (line 168) | def parse_credentials(creds)
        function copy_to_local_file (line 173) | def copy_to_local_file(style, local_dest_path)
        function convert_time (line 187) | def convert_time(time)
        function dynamic_fog_host_for_style (line 194) | def dynamic_fog_host_for_style(style)
        function host_name_for_directory (line 202) | def host_name_for_directory
        function find_credentials (line 210) | def find_credentials(creds)
        function connection (line 227) | def connection
        function directory (line 231) | def directory
        function directory_name (line 235) | def directory_name
        function scheme (line 243) | def scheme

FILE: lib/paperclip/storage/s3.rb
  type Paperclip (line 1) | module Paperclip
    type Storage (line 2) | module Storage
      type S3 (line 123) | module S3
        function extended (line 124) | def self.extended base
        function expiring_url (line 181) | def expiring_url(time = 3600, style_name = default_style)
        function s3_credentials (line 193) | def s3_credentials
        function s3_host_name (line 197) | def s3_host_name
        function s3_region (line 204) | def s3_region
        function s3_host_alias (line 211) | def s3_host_alias
        function s3_prefixes_in_alias (line 217) | def s3_prefixes_in_alias
        function s3_url_options (line 221) | def s3_url_options
        function bucket_name (line 227) | def bucket_name
        function s3_interface (line 233) | def s3_interface
        function obtain_s3_instance_for (line 259) | def obtain_s3_instance_for(options)
        function s3_bucket (line 264) | def s3_bucket
        function style_name_as_path (line 268) | def style_name_as_path(style_name)
        function s3_object (line 272) | def s3_object style_name = default_style
        function use_accelerate_endpoint? (line 276) | def use_accelerate_endpoint?
        function using_http_proxy? (line 280) | def using_http_proxy?
        function http_proxy_host (line 284) | def http_proxy_host
        function http_proxy_port (line 288) | def http_proxy_port
        function http_proxy_user (line 292) | def http_proxy_user
        function http_proxy_password (line 296) | def http_proxy_password
        function set_permissions (line 300) | def set_permissions permissions
        function set_storage_class (line 305) | def set_storage_class(storage_class)
        function parse_credentials (line 310) | def parse_credentials creds
        function exists? (line 316) | def exists?(style = default_style)
        function s3_permissions (line 326) | def s3_permissions(style = default_style)
        function s3_storage_class (line 332) | def s3_storage_class(style = default_style)
        function s3_protocol (line 336) | def s3_protocol(style = default_style, with_colon = false)
        function create_bucket (line 347) | def create_bucket
        function flush_writes (line 351) | def flush_writes #:nodoc:
        function flush_deletes (line 401) | def flush_deletes #:nodoc:
        function copy_to_local_file (line 413) | def copy_to_local_file(style, local_dest_path)
        function find_credentials (line 427) | def find_credentials creds
        function use_secure_protocol? (line 442) | def use_secure_protocol?(style_name)
        function merge_s3_headers (line 446) | def merge_s3_headers(http_headers, s3_headers, s3_metadata)

FILE: lib/paperclip/style.rb
  type Paperclip (line 1) | module Paperclip
    class Style (line 6) | class Style
      method initialize (line 13) | def initialize name, definition, attachment
      method processors (line 39) | def processors
      method whiny (line 44) | def whiny
      method whiny? (line 49) | def whiny?
      method convert_options (line 53) | def convert_options
      method source_file_options (line 58) | def source_file_options
      method geometry (line 65) | def geometry
      method processor_options (line 72) | def processor_options
      method [] (line 85) | def [](key)
      method []= (line 93) | def []=(key, value)
      method default_format (line 102) | def default_format

FILE: lib/paperclip/tempfile.rb
  type Paperclip (line 1) | module Paperclip
    class Tempfile (line 3) | class Tempfile < ::Tempfile
      method make_tmpname (line 11) | def make_tmpname(prefix_suffix, n)
    type TempfileEncoding (line 30) | module TempfileEncoding
      function binmode (line 34) | def binmode

FILE: lib/paperclip/tempfile_factory.rb
  type Paperclip (line 1) | module Paperclip
    class TempfileFactory (line 2) | class TempfileFactory
      method generate (line 4) | def generate(name = random_name)
      method extension (line 11) | def extension
      method basename (line 15) | def basename
      method random_name (line 19) | def random_name

FILE: lib/paperclip/thumbnail.rb
  type Paperclip (line 1) | module Paperclip
    class Thumbnail (line 3) | class Thumbnail < Processor
      method initialize (line 30) | def initialize(file, options = {}, attachment = nil)
      method crop? (line 55) | def crop?
      method convert_options? (line 60) | def convert_options?
      method make (line 66) | def make
      method transformation_command (line 101) | def transformation_command
      method multi_frame_format? (line 114) | def multi_frame_format?
      method animated? (line 118) | def animated?
      method identified_as_animated? (line 123) | def identified_as_animated?

FILE: lib/paperclip/url_generator.rb
  type Paperclip (line 4) | module Paperclip
    class UrlGenerator (line 5) | class UrlGenerator
      method initialize (line 6) | def initialize(attachment)
      method for (line 10) | def for(style_name, options)
      method default_url (line 25) | def default_url
      method most_appropriate_url (line 35) | def most_appropriate_url
      method timestamp_as_needed (line 43) | def timestamp_as_needed(url, options)
      method timestamp_possible? (line 52) | def timestamp_possible?
      method escape_url_as_needed (line 56) | def escape_url_as_needed(url, options)
      method escape_url (line 64) | def escape_url(url)
      method escape_regex (line 72) | def escape_regex

FILE: lib/paperclip/validators.rb
  type Paperclip (line 11) | module Paperclip
    type Validators (line 12) | module Validators
      type ClassMethods (line 22) | module ClassMethods
        function validates_attachment (line 35) | def validates_attachment(*attributes)
        function validate_before_processing (line 55) | def validate_before_processing(validator_class, options)
        function create_validating_before_filter (line 64) | def create_validating_before_filter(attribute, validator_class, op...

FILE: lib/paperclip/validators/attachment_content_type_validator.rb
  type Paperclip (line 1) | module Paperclip
    type Validators (line 2) | module Validators
      class AttachmentContentTypeValidator (line 3) | class AttachmentContentTypeValidator < ActiveModel::EachValidator
        method initialize (line 4) | def initialize(options)
        method helper_method_name (line 9) | def self.helper_method_name
        method validate_each (line 13) | def validate_each(record, attribute, value)
        method validate_whitelist (line 30) | def validate_whitelist(record, attribute, value)
        method validate_blacklist (line 36) | def validate_blacklist(record, attribute, value)
        method mark_invalid (line 42) | def mark_invalid(record, attribute, types)
        method allowed_types (line 46) | def allowed_types
        method forbidden_types (line 50) | def forbidden_types
        method check_validity! (line 54) | def check_validity!
      type HelperMethods (line 61) | module HelperMethods
        function validates_attachment_content_type (line 81) | def validates_attachment_content_type(*attr_names)

FILE: lib/paperclip/validators/attachment_file_name_validator.rb
  type Paperclip (line 1) | module Paperclip
    type Validators (line 2) | module Validators
      class AttachmentFileNameValidator (line 3) | class AttachmentFileNameValidator < ActiveModel::EachValidator
        method initialize (line 4) | def initialize(options)
        method helper_method_name (line 9) | def self.helper_method_name
        method validate_each (line 13) | def validate_each(record, attribute, value)
        method validate_whitelist (line 30) | def validate_whitelist(record, attribute, value)
        method validate_blacklist (line 36) | def validate_blacklist(record, attribute, value)
        method mark_invalid (line 42) | def mark_invalid(record, attribute, patterns)
        method allowed (line 46) | def allowed
        method forbidden (line 50) | def forbidden
        method check_validity! (line 54) | def check_validity!
      type HelperMethods (line 61) | module HelperMethods
        function validates_attachment_file_name (line 72) | def validates_attachment_file_name(*attr_names)

FILE: lib/paperclip/validators/attachment_file_type_ignorance_validator.rb
  type Paperclip (line 3) | module Paperclip
    type Validators (line 4) | module Validators
      class AttachmentFileTypeIgnoranceValidator (line 5) | class AttachmentFileTypeIgnoranceValidator < ActiveModel::EachValidator
        method validate_each (line 6) | def validate_each(record, attribute, value)
        method helper_method_name (line 11) | def self.helper_method_name
      type HelperMethods (line 16) | module HelperMethods
        function do_not_validate_attachment_file_type (line 22) | def do_not_validate_attachment_file_type(*attr_names)

FILE: lib/paperclip/validators/attachment_presence_validator.rb
  type Paperclip (line 3) | module Paperclip
    type Validators (line 4) | module Validators
      class AttachmentPresenceValidator (line 5) | class AttachmentPresenceValidator < ActiveModel::EachValidator
        method validate_each (line 6) | def validate_each(record, attribute, value)
        method helper_method_name (line 12) | def self.helper_method_name
      type HelperMethods (line 17) | module HelperMethods
        function validates_attachment_presence (line 23) | def validates_attachment_presence(*attr_names)

FILE: lib/paperclip/validators/attachment_size_validator.rb
  type Paperclip (line 3) | module Paperclip
    type Validators (line 4) | module Validators
      class AttachmentSizeValidator (line 5) | class AttachmentSizeValidator < ActiveModel::Validations::Numericali...
        method initialize (line 8) | def initialize(options)
        method helper_method_name (line 13) | def self.helper_method_name
        method validate_each (line 17) | def validate_each(record, attr_name, value)
        method check_validity! (line 41) | def check_validity!
        method extract_options (line 49) | def extract_options(options)
        method extract_option_value (line 61) | def extract_option_value(option, option_value)
        method human_size (line 73) | def human_size(size)
        method min_value_in_human_size (line 77) | def min_value_in_human_size(record)
        method max_value_in_human_size (line 84) | def max_value_in_human_size(record)
      type HelperMethods (line 92) | module HelperMethods
        function validates_attachment_size (line 102) | def validates_attachment_size(*attr_names)

FILE: lib/paperclip/validators/media_type_spoof_detection_validator.rb
  type Paperclip (line 3) | module Paperclip
    type Validators (line 4) | module Validators
      class MediaTypeSpoofDetectionValidator (line 5) | class MediaTypeSpoofDetectionValidator < ActiveModel::EachValidator
        method validate_each (line 6) | def validate_each(record, attribute, value)
      type HelperMethods (line 18) | module HelperMethods
        function validates_media_type_spoof_detection (line 24) | def validates_media_type_spoof_detection(*attr_names)

FILE: lib/paperclip/version.rb
  type Paperclip (line 1) | module Paperclip

FILE: lib/tasks/paperclip.rake
  type Paperclip (line 3) | module Paperclip
    type Task (line 4) | module Task
      function obtain_class (line 5) | def self.obtain_class
      function obtain_attachments (line 11) | def self.obtain_attachments(klass)
      function log_error (line 28) | def self.log_error(error)

FILE: shoulda_macros/paperclip.rb
  type Paperclip (line 3) | module Paperclip
    type Shoulda (line 11) | module Shoulda
      function should_have_attached_file (line 16) | def should_have_attached_file name
      function should_validate_attachment_presence (line 25) | def should_validate_attachment_presence name
      function should_validate_attachment_content_type (line 37) | def should_validate_attachment_content_type name, options = {}
      function should_validate_attachment_size (line 53) | def should_validate_attachment_size name, options = {}
      function stub_paperclip_s3 (line 68) | def stub_paperclip_s3(model, attachment, extension)
      function paperclip_fixture (line 97) | def paperclip_fixture(model, attachment, extension)
  class ActionDispatch::IntegrationTest::Session (line 107) | class ActionDispatch::IntegrationTest::Session  #:nodoc:
  class ActionController::Integration::Session (line 111) | class ActionController::Integration::Session  #:nodoc:
  class FactoryGirl::Factory (line 117) | class FactoryGirl::Factory
  class Factory (line 121) | class Factory
  class Minitest::Unit::TestCase (line 127) | class Minitest::Unit::TestCase #:nodoc:
  class Test::Unit::TestCase (line 131) | class Test::Unit::TestCase #:nodoc:

FILE: spec/paperclip/attachment_spec.rb
  function all (line 533) | def all;   "-all";   end
  function thumb (line 534) | def thumb; "-thumb"; end
  class Paperclip::Test (line 642) | class Paperclip::Test < Paperclip::Processor; end
    method make (line 753) | def make; @file; end
  class Paperclip::Test (line 704) | class Paperclip::Test < Paperclip::Processor; end
    method make (line 753) | def make; @file; end
  class Paperclip::Test (line 752) | class Paperclip::Test < Paperclip::Processor
    method make (line 753) | def make; @file; end
  function do_before_avatar (line 832) | def do_before_avatar; end
  function do_after_avatar (line 833) | def do_after_avatar; end
  function do_before_all (line 834) | def do_before_all; end
  function do_after_all (line 835) | def do_after_all; end
  class MyCleaner (line 1038) | class MyCleaner
    method call (line 1039) | def call(filename)

FILE: spec/paperclip/has_attached_file_spec.rb
  function assert_adding_attachment (line 65) | def assert_adding_attachment(attachment_name, options={})
  class AttachmentAdder (line 69) | class AttachmentAdder
    method initialize (line 73) | def initialize(attachment_name, options = {})
    method defines_method (line 83) | def defines_method(method_name)
    method defines_class_method (line 91) | def defines_class_method(method_name)
    method defines_validation (line 100) | def defines_validation
    method registers_attachment (line 108) | def registers_attachment
    method defines_callback (line 117) | def defines_callback(callback_name)
    method does_not_set_up_media_type_check_validation (line 125) | def does_not_set_up_media_type_check_validation
    method sets_up_media_type_check_validation (line 133) | def sets_up_media_type_check_validation
    method stub_class (line 143) | def stub_class

FILE: spec/paperclip/integration_spec.rb
  function s3_files_for (line 488) | def s3_files_for attachment
  function s3_headers_for (line 500) | def s3_headers_for attachment, style

FILE: spec/paperclip/interpolations_spec.rb
  class Thing (line 27) | class Thing ; end
  function url (line 190) | def url(*args)

FILE: spec/paperclip/io_adapters/abstract_adapter_spec.rb
  class TestAdapter (line 4) | class TestAdapter < Paperclip::AbstractAdapter
    method content_type (line 7) | def content_type
  function ln (line 102) | def ln(*)
  class TestLinkOrCopyAdapter (line 131) | class TestLinkOrCopyAdapter < Paperclip::AbstractAdapter

FILE: spec/paperclip/io_adapters/data_uri_adapter_spec.rb
  function original_base64_content (line 82) | def original_base64_content
  function original_file_contents (line 86) | def original_file_contents

FILE: spec/paperclip/io_adapters/registry_spec.rb
  class AdapterTest (line 6) | class AdapterTest
    method initialize (line 7) | def initialize(_target, _ = {}); end
    method initialize (line 21) | def initialize(_target, _ = {}); end
  class AdapterTest (line 20) | class AdapterTest
    method initialize (line 7) | def initialize(_target, _ = {}); end
    method initialize (line 21) | def initialize(_target, _ = {}); end

FILE: spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb
  class UploadedFile (line 9) | class UploadedFile < OpenStruct; end
  class UploadedFile (line 59) | class UploadedFile < OpenStruct; end
  class UploadedFile (line 82) | class UploadedFile < OpenStruct; end
  class UploadedFile (line 130) | class UploadedFile < OpenStruct; end

FILE: spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb
  function plain_matcher (line 99) | def plain_matcher
  function matcher (line 103) | def matcher

FILE: spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb
  function matcher (line 66) | def matcher

FILE: spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb
  function matcher (line 85) | def matcher

FILE: spec/paperclip/paperclip_missing_attachment_styles_spec.rb
  class ::Book (line 49) | class ::Book < ActiveRecord::Base
  class ::Dummy (line 67) | class ::Dummy

FILE: spec/paperclip/paperclip_spec.rb
  class ::One (line 100) | class ::One; class Two; end; end
    class Two (line 100) | class Two; end
  class ::Three (line 105) | class ::Three; end
  class ::Four (line 106) | class ::Four; end
  class ::SubDummy (line 130) | class ::SubDummy < Dummy; end
  function make (line 174) | def make(file, options = {}, attachment = nil)

FILE: spec/paperclip/plural_cache_spec.rb
  class BigBox (line 14) | class BigBox ; end
  class BigBox (line 31) | class BigBox ; end

FILE: spec/paperclip/rails_environment_spec.rb
  function resetting_rails_to (line 23) | def resetting_rails_to(new_value)

FILE: spec/paperclip/storage/fog_spec.rb
  function custom_method (line 102) | def custom_method

FILE: spec/paperclip/storage/s3_spec.rb
  function aws2_add_region (line 9) | def aws2_add_region
  function counter (line 389) | def counter
  function original_filename (line 510) | def original_filename
  function counter (line 605) | def counter
  class DummyCredentialProvider (line 992) | class DummyCredentialProvider; end
  function attachment_path (line 1671) | def attachment_path
  function rails_env (line 1685) | def rails_env(env)

FILE: spec/paperclip/thumbnail_spec.rb
  function from_file (line 239) | def self.from_file(file)
  function transformation_to (line 243) | def transformation_to(target, should_crop)
  function parse (line 270) | def self.parse(s)
  function to_s (line 274) | def to_s

FILE: spec/paperclip/url_generator_spec.rb
  function escape (line 75) | def escape
  function generate (line 198) | def generate(expected, updated_at=nil)

FILE: spec/paperclip/validators/attachment_content_type_validator_spec.rb
  function build_validator (line 9) | def build_validator(options)

FILE: spec/paperclip/validators/attachment_file_name_validator_spec.rb
  function build_validator (line 9) | def build_validator(options)

FILE: spec/paperclip/validators/attachment_presence_validator_spec.rb
  function build_validator (line 9) | def build_validator(options={})

FILE: spec/paperclip/validators/attachment_size_validator_spec.rb
  function build_validator (line 9) | def build_validator(options)
  function should_allow_attachment_file_size (line 15) | def self.should_allow_attachment_file_size(size)
  function should_not_allow_attachment_file_size (line 31) | def self.should_not_allow_attachment_file_size(size, options = {})

FILE: spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb
  function build_validator (line 9) | def build_validator(options = {})

FILE: spec/paperclip/validators_spec.rb
  function title_present? (line 86) | def title_present?
  function title_present? (line 96) | def title_present?
  class MyValidator (line 135) | class MyValidator < Paperclip::Validators::AttachmentContentTypeValidator
    method initialize (line 136) | def initialize(options)

FILE: spec/support/assertions.rb
  type Assertions (line 1) | module Assertions
    function assert (line 2) | def assert(truthy, message = nil)
    function assert_equal (line 6) | def assert_equal(expected, actual, message = nil)
    function assert_not_equal (line 10) | def assert_not_equal(expected, actual, message = nil)
    function assert_raises (line 14) | def assert_raises(exception_class, message = nil, &block)
    function assert_nothing_raised (line 18) | def assert_nothing_raised(&block)
    function assert_nil (line 22) | def assert_nil(thing)
    function assert_contains (line 26) | def assert_contains(haystack, needle)
    function assert_match (line 30) | def assert_match(pattern, value)
    function assert_no_match (line 34) | def assert_no_match(pattern, value)
    function assert_file_exists (line 38) | def assert_file_exists(path_to_file)
    function assert_file_not_exists (line 42) | def assert_file_not_exists(path_to_file)
    function assert_empty (line 46) | def assert_empty(object)
    function assert_success_response (line 50) | def assert_success_response(url)
    function assert_not_found_response (line 59) | def assert_not_found_response(url)
    function assert_forbidden_response (line 67) | def assert_forbidden_response(url)
    function assert_frame_dimensions (line 75) | def assert_frame_dimensions(range, frames)

FILE: spec/support/fake_model.rb
  class FakeModel (line 1) | class FakeModel
    method errors (line 11) | def errors
    method run_paperclip_callbacks (line 15) | def run_paperclip_callbacks name, *args
    method valid? (line 18) | def valid?
    method new_record? (line 22) | def new_record?

FILE: spec/support/fake_rails.rb
  class FakeRails (line 1) | class FakeRails
    method initialize (line 2) | def initialize(env, root)
    method const_defined? (line 9) | def const_defined?(const)

FILE: spec/support/mock_attachment.rb
  class MockAttachment (line 1) | class MockAttachment
    method initialize (line 5) | def initialize(options = {})
    method instance (line 13) | def instance
    method respond_to? (line 17) | def respond_to?(meth)

FILE: spec/support/mock_interpolator.rb
  class MockInterpolator (line 1) | class MockInterpolator
    method initialize (line 2) | def initialize(options = {})
    method interpolate (line 6) | def interpolate(pattern, attachment, style_name)
    method has_interpolated_pattern? (line 13) | def has_interpolated_pattern?(pattern)
    method has_interpolated_style_name? (line 17) | def has_interpolated_style_name?(style_name)
    method has_interpolated_attachment? (line 21) | def has_interpolated_attachment?(attachment)

FILE: spec/support/mock_url_generator_builder.rb
  class MockUrlGeneratorBuilder (line 1) | class MockUrlGeneratorBuilder
    method initializer (line 2) | def initializer
    method new (line 5) | def new(attachment)
    method for (line 11) | def for(style_name, options)
    method has_generated_url_with_options? (line 17) | def has_generated_url_with_options?(options)
    method has_generated_url_with_style_name? (line 24) | def has_generated_url_with_style_name?(style_name)

FILE: spec/support/model_reconstruction.rb
  type ModelReconstruction (line 1) | module ModelReconstruction
    function reset_class (line 2) | def reset_class class_name
    function reset_table (line 25) | def reset_table table_name, &block
    function modify_table (line 30) | def modify_table &block
    function rebuild_model (line 34) | def rebuild_model options = {}
    function rebuild_class (line 47) | def rebuild_class options = {}
    function rebuild_meta_class_of (line 55) | def rebuild_meta_class_of obj, options = {}
    function meta_class_of (line 63) | def meta_class_of(obj)

FILE: spec/support/reporting.rb
  type Reporting (line 1) | module Reporting
    function silence_stream (line 2) | def silence_stream(stream)

FILE: spec/support/test_data.rb
  type TestData (line 1) | module TestData
    function attachment (line 2) | def attachment(options={})
    function stringy_file (line 6) | def stringy_file
    function fixture_file (line 10) | def fixture_file(filename)

FILE: spec/support/version_helper.rb
  type VersionHelper (line 1) | module VersionHelper
    function active_support_version (line 2) | def active_support_version
    function ruby_version (line 6) | def ruby_version
Condensed preview — 178 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (702K chars).
[
  {
    "path": ".codeclimate.yml",
    "chars": 209,
    "preview": "---\nengines:\n  duplication:\n    enabled: true\n    config:\n      languages:\n      - ruby\n  fixme:\n    enabled: true\n  rub"
  },
  {
    "path": ".github/issue_template.md",
    "chars": 381,
    "preview": "## Deprecation notice\n\nPaperclip is currently undergoing [deprecation in favor of ActiveStorage](https://github.com/thou"
  },
  {
    "path": ".gitignore",
    "chars": 122,
    "preview": "*~\n*.swp\n.rvmrc\n.bundle\ntmp\n.DS_Store\n\n*.log\n\npublic\npaperclip*.gem\ncapybara*.html\n\n*.rbc\n.rbx\n\n*SPIKE*\n*emfile.lock\ntag"
  },
  {
    "path": ".hound.yml",
    "chars": 36374,
    "preview": "AllCops:\n  Include:\n    - \"**/*.gemspec\"\n    - \"**/*.podspec\"\n    - \"**/*.jbuilder\"\n    - \"**/*.rake\"\n    - \"**/*.opal\"\n"
  },
  {
    "path": ".rubocop.yml",
    "chars": 25,
    "preview": "inherit_from: .hound.yml\n"
  },
  {
    "path": ".travis.yml",
    "chars": 312,
    "preview": "language: ruby\nsudo: false\n\nrvm:\n  - 2.1\n  - 2.2\n  - 2.3\n  - 2.4\n\nscript: \"bundle exec rake clean spec cucumber\"\n\naddons"
  },
  {
    "path": "Appraisals",
    "chars": 97,
    "preview": "appraise \"4.2\" do\n  gem \"rails\", \"~> 4.2.0\"\nend\n\nappraise \"5.0\" do\n  gem \"rails\", \"~> 5.0.0\"\nend\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 2928,
    "preview": "Contributing\n============\n\nWe love pull requests from everyone. By participating in this project, you agree\nto abide by "
  },
  {
    "path": "Gemfile",
    "chars": 332,
    "preview": "source \"https://rubygems.org\"\n\ngemspec\n\ngem 'sqlite3', '~> 1.3.8', :platforms => :ruby\ngem 'pry'\n\n# Hinting at developme"
  },
  {
    "path": "LICENSE",
    "chars": 1106,
    "preview": "\nLICENSE\n\nThe MIT License\n\nCopyright (c) 2008-2016 Jon Yurek and thoughtbot, inc.\n\nPermission is hereby granted, free of"
  },
  {
    "path": "MIGRATING-ES.md",
    "chars": 8712,
    "preview": "# Migrando de Paperclip a ActiveStorage\n\nPaperclip y ActiveStorage resuelven problemas similares con soluciones\nsimilare"
  },
  {
    "path": "MIGRATING.md",
    "chars": 15402,
    "preview": "# Migrating from Paperclip to ActiveStorage\n\n* [Video presentation](https://www.youtube.com/watch?v=tZ_WNUytO9o).\n* [En "
  },
  {
    "path": "NEWS",
    "chars": 24616,
    "preview": "6.1.0 (2018-07-27):\n\n* BUGFIX: Don't double-encode URLs (Roderick Monje).\n* BUGFIX: Only use the content_type when it ex"
  },
  {
    "path": "README.md",
    "chars": 37655,
    "preview": "Paperclip\n=========\n\n# Deprecated\n\n**[Paperclip is deprecated]**.\n\nFor new projects, we recommend Rails' own [ActiveStor"
  },
  {
    "path": "RELEASING.md",
    "chars": 593,
    "preview": "Releasing paperclip\n\n1. Update `lib/paperclip/version.rb` file accordingly.\n2. Update `NEWS` to reflect the changes sinc"
  },
  {
    "path": "Rakefile",
    "chars": 1159,
    "preview": "require 'bundler/gem_tasks'\nrequire 'appraisal'\nrequire 'rspec/core/rake_task'\nrequire 'cucumber/rake/task'\n\ndesc 'Defau"
  },
  {
    "path": "UPGRADING",
    "chars": 743,
    "preview": "##################################################\n#  NOTE FOR UPGRADING FROM 4.3.0 OR EARLIER      #\n##################"
  },
  {
    "path": "features/basic_integration.feature",
    "chars": 3409,
    "preview": "Feature: Rails integration\n\n  Background:\n    Given I generate a new rails application\n    And I run a rails generator t"
  },
  {
    "path": "features/migration.feature",
    "chars": 1877,
    "preview": "Feature: Migration\n\n  Background:\n    Given I generate a new rails application\n    And I write to \"app/models/user.rb\" w"
  },
  {
    "path": "features/rake_tasks.feature",
    "chars": 3031,
    "preview": "Feature: Rake tasks\n\n  Background:\n    Given I generate a new rails application\n    And I run a rails generator to gener"
  },
  {
    "path": "features/step_definitions/attachment_steps.rb",
    "chars": 3827,
    "preview": "module AttachmentHelpers\n  def fixture_path(filename)\n    File.expand_path(\"#{PROJECT_ROOT}/spec/support/fixtures/#{file"
  },
  {
    "path": "features/step_definitions/html_steps.rb",
    "chars": 465,
    "preview": "Then %r{I should see an image with a path of \"([^\"]*)\"} do |path|\n  expect(page).to have_css(\"img[src^='#{path}']\")\nend\n"
  },
  {
    "path": "features/step_definitions/rails_steps.rb",
    "chars": 6702,
    "preview": "Given /^I generate a new rails application$/ do\n  steps %{\n    When I successfully run `rails new #{APP_NAME} --skip-bun"
  },
  {
    "path": "features/step_definitions/s3_steps.rb",
    "chars": 600,
    "preview": "When /^I attach the file \"([^\"]*)\" to \"([^\"]*)\" on S3$/ do |file_path, field|\n  definition = Paperclip::AttachmentRegist"
  },
  {
    "path": "features/step_definitions/web_steps.rb",
    "chars": 2904,
    "preview": "# TL;DR: YOU SHOULD DELETE THIS FILE\n#\n# This file was generated by Cucumber-Rails and is only here to get you a head st"
  },
  {
    "path": "features/support/env.rb",
    "chars": 237,
    "preview": "require 'aruba/cucumber'\nrequire 'capybara/cucumber'\nrequire 'rspec/matchers'\n\n$CUCUMBER=1\n\nWorld(RSpec::Matchers)\n\nBefo"
  },
  {
    "path": "features/support/fakeweb.rb",
    "chars": 184,
    "preview": "require 'fake_web'\n\nFakeWeb.allow_net_connect = false\n\nmodule FakeWeb\n  class StubSocket\n    def read_timeout=(_ignored)"
  },
  {
    "path": "features/support/file_helpers.rb",
    "chars": 770,
    "preview": "module FileHelpers\n  def append_to(path, contents)\n    cd(\".\") do\n      File.open(path, \"a\") do |file|\n        file.puts"
  },
  {
    "path": "features/support/fixtures/boot_config.txt",
    "chars": 256,
    "preview": "class Rails::Boot\n  def run\n    load_initializer\n\n    Rails::Initializer.class_eval do\n      def load_gems\n        @bund"
  },
  {
    "path": "features/support/fixtures/gemfile.txt",
    "chars": 93,
    "preview": "source \"http://rubygems.org\"\n\ngem \"rails\", \"RAILS_VERSION\"\ngem \"rdoc\"\ngem \"sqlite3\", \"1.3.8\"\n"
  },
  {
    "path": "features/support/fixtures/preinitializer.txt",
    "chars": 601,
    "preview": "begin\n  require \"rubygems\"\n  require \"bundler\"\nrescue LoadError\n  raise \"Could not load the bundler gem. Install it with"
  },
  {
    "path": "features/support/paths.rb",
    "chars": 649,
    "preview": "module NavigationHelpers\n  # Maps a name to a path. Used by the\n  #\n  #   When /^I go to (.+)$/ do |page_name|\n  #\n  # s"
  },
  {
    "path": "features/support/rails.rb",
    "chars": 1004,
    "preview": "PROJECT_ROOT     = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')).freeze\nAPP_NAME         = 'testapp'.f"
  },
  {
    "path": "features/support/selectors.rb",
    "chars": 443,
    "preview": "module HtmlSelectorsHelpers\n  # Maps a name to a selector. Used primarily by the\n  #\n  #   When /^(.+) within (.+)$/ do "
  },
  {
    "path": "gemfiles/4.2.gemfile",
    "chars": 322,
    "preview": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"sqlite3\", \"~> 1.3.8\", :platforms => :ruby\nge"
  },
  {
    "path": "gemfiles/5.0.gemfile",
    "chars": 322,
    "preview": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"sqlite3\", \"~> 1.3.8\", :platforms => :ruby\nge"
  },
  {
    "path": "lib/generators/paperclip/USAGE",
    "chars": 135,
    "preview": "Description:\n    Explain the generator\n\nExample:\n    rails generate paperclip Thing\n\n    This will create:\n        what/"
  },
  {
    "path": "lib/generators/paperclip/paperclip_generator.rb",
    "chars": 1171,
    "preview": "require 'rails/generators/active_record'\n\nclass PaperclipGenerator < ActiveRecord::Generators::Base\n  desc \"Create a mig"
  },
  {
    "path": "lib/generators/paperclip/templates/paperclip_migration.rb.erb",
    "chars": 393,
    "preview": "class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>\n  def self.up\n    change_table :<%= "
  },
  {
    "path": "lib/paperclip/attachment.rb",
    "chars": 22426,
    "preview": "require 'uri'\nrequire 'paperclip/url_generator'\nrequire 'active_support/deprecation'\nrequire 'active_support/core_ext/st"
  },
  {
    "path": "lib/paperclip/attachment_registry.rb",
    "chars": 1324,
    "preview": "require 'singleton'\n\nmodule Paperclip\n  class AttachmentRegistry\n    include Singleton\n\n    def self.register(klass, att"
  },
  {
    "path": "lib/paperclip/callbacks.rb",
    "chars": 1031,
    "preview": "module Paperclip\n  module Callbacks\n    def self.included(base)\n      base.extend(Defining)\n      base.send(:include, Ru"
  },
  {
    "path": "lib/paperclip/content_type_detector.rb",
    "chars": 2206,
    "preview": "module Paperclip\n  class ContentTypeDetector\n    # The content-type detection strategy is as follows:\n    #\n    # 1. Bla"
  },
  {
    "path": "lib/paperclip/errors.rb",
    "chars": 1234,
    "preview": "module Paperclip\n  # A base error class for Paperclip. Most of the error that will be thrown\n  # from Paperclip will inh"
  },
  {
    "path": "lib/paperclip/file_command_content_type_detector.rb",
    "chars": 756,
    "preview": "module Paperclip\n  class FileCommandContentTypeDetector\n    SENSIBLE_DEFAULT = \"application/octet-stream\"\n\n    def initi"
  },
  {
    "path": "lib/paperclip/filename_cleaner.rb",
    "chars": 317,
    "preview": "module Paperclip\n  class FilenameCleaner\n    def initialize(invalid_character_regex)\n      @invalid_character_regex = in"
  },
  {
    "path": "lib/paperclip/geometry.rb",
    "chars": 4597,
    "preview": "module Paperclip\n\n  # Defines the geometry of an image.\n  class Geometry\n    attr_accessor :height, :width, :modifier\n\n "
  },
  {
    "path": "lib/paperclip/geometry_detector_factory.rb",
    "chars": 1275,
    "preview": "module Paperclip\n  class GeometryDetector\n    def initialize(file)\n      @file = file\n      raise_if_blank_file\n    end\n"
  },
  {
    "path": "lib/paperclip/geometry_parser_factory.rb",
    "chars": 653,
    "preview": "module Paperclip\n  class GeometryParser\n    FORMAT = /\\b(\\d*)x?(\\d*)\\b(?:,(\\d?))?(\\@\\>|\\>\\@|[\\>\\<\\#\\@\\%^!])?/i\n    def i"
  },
  {
    "path": "lib/paperclip/glue.rb",
    "chars": 492,
    "preview": "require 'paperclip/callbacks'\nrequire 'paperclip/validators'\nrequire 'paperclip/schema'\n\nmodule Paperclip\n  module Glue\n"
  },
  {
    "path": "lib/paperclip/has_attached_file.rb",
    "chars": 2780,
    "preview": "module Paperclip\n  class HasAttachedFile\n    def self.define_on(klass, name, options)\n      new(klass, name, options).de"
  },
  {
    "path": "lib/paperclip/helpers.rb",
    "chars": 2205,
    "preview": "module Paperclip\n  module Helpers\n    def configure\n      yield(self) if block_given?\n    end\n\n    def interpolates key,"
  },
  {
    "path": "lib/paperclip/interpolations/plural_cache.rb",
    "chars": 439,
    "preview": "module Paperclip\n  module Interpolations\n    class PluralCache\n      def initialize\n        @symbol_cache = {}.compare_b"
  },
  {
    "path": "lib/paperclip/interpolations.rb",
    "chars": 7371,
    "preview": "module Paperclip\n  # This module contains all the methods that are available for interpolation\n  # in paths and urls. To"
  },
  {
    "path": "lib/paperclip/io_adapters/abstract_adapter.rb",
    "chars": 1808,
    "preview": "require 'active_support/core_ext/module/delegation'\n\nmodule Paperclip\n  class AbstractAdapter\n    OS_RESTRICTED_CHARACTE"
  },
  {
    "path": "lib/paperclip/io_adapters/attachment_adapter.rb",
    "chars": 1198,
    "preview": "module Paperclip\n  class AttachmentAdapter < AbstractAdapter\n    def self.register\n      Paperclip.io_adapters.register "
  },
  {
    "path": "lib/paperclip/io_adapters/data_uri_adapter.rb",
    "chars": 527,
    "preview": "module Paperclip\n  class DataUriAdapter < StringioAdapter\n    def self.register\n      Paperclip.io_adapters.register sel"
  },
  {
    "path": "lib/paperclip/io_adapters/empty_string_adapter.rb",
    "chars": 327,
    "preview": "module Paperclip\n  class EmptyStringAdapter < AbstractAdapter\n    def self.register\n      Paperclip.io_adapters.register"
  },
  {
    "path": "lib/paperclip/io_adapters/file_adapter.rb",
    "chars": 704,
    "preview": "module Paperclip\n  class FileAdapter < AbstractAdapter\n    def self.register\n      Paperclip.io_adapters.register self d"
  },
  {
    "path": "lib/paperclip/io_adapters/http_url_proxy_adapter.rb",
    "chars": 400,
    "preview": "module Paperclip\n  class HttpUrlProxyAdapter < UriAdapter\n    def self.register\n      Paperclip.io_adapters.register sel"
  },
  {
    "path": "lib/paperclip/io_adapters/identity_adapter.rb",
    "chars": 349,
    "preview": "module Paperclip\n  class IdentityAdapter < AbstractAdapter\n    def self.register\n      Paperclip.io_adapters.register Pa"
  },
  {
    "path": "lib/paperclip/io_adapters/nil_adapter.rb",
    "chars": 538,
    "preview": "module Paperclip\n  class NilAdapter < AbstractAdapter\n    def self.register\n      Paperclip.io_adapters.register self do"
  },
  {
    "path": "lib/paperclip/io_adapters/registry.rb",
    "chars": 852,
    "preview": "module Paperclip\n  class AdapterRegistry\n    class NoHandlerError < Paperclip::Error; end\n\n    attr_reader :registered_h"
  },
  {
    "path": "lib/paperclip/io_adapters/stringio_adapter.rb",
    "chars": 846,
    "preview": "module Paperclip\n  class StringioAdapter < AbstractAdapter\n    def self.register\n      Paperclip.io_adapters.register se"
  },
  {
    "path": "lib/paperclip/io_adapters/uploaded_file_adapter.rb",
    "chars": 1107,
    "preview": "module Paperclip\n  class UploadedFileAdapter < AbstractAdapter\n    def self.register\n      Paperclip.io_adapters.registe"
  },
  {
    "path": "lib/paperclip/io_adapters/uri_adapter.rb",
    "chars": 1735,
    "preview": "require \"open-uri\"\n\nmodule Paperclip\n  class UriAdapter < AbstractAdapter\n    attr_writer :content_type\n\n    def self.re"
  },
  {
    "path": "lib/paperclip/locales/en.yml",
    "chars": 395,
    "preview": "en:\n  errors:\n    messages:\n      in_between: \"must be in between %{min} and %{max}\"\n      spoofed_media_type: \"has cont"
  },
  {
    "path": "lib/paperclip/logger.rb",
    "chars": 460,
    "preview": "module Paperclip\n  module Logger\n    # Log a paperclip-specific line. This will log to STDOUT\n    # by default. Set Pape"
  },
  {
    "path": "lib/paperclip/matchers/have_attached_file_matcher.rb",
    "chars": 1459,
    "preview": "module Paperclip\n  module Shoulda\n    module Matchers\n      # Ensures that the given instance or class has an attachment"
  },
  {
    "path": "lib/paperclip/matchers/validate_attachment_content_type_matcher.rb",
    "chars": 3191,
    "preview": "module Paperclip\n  module Shoulda\n    module Matchers\n      # Ensures that the given instance or class validates the con"
  },
  {
    "path": "lib/paperclip/matchers/validate_attachment_presence_matcher.rb",
    "chars": 1709,
    "preview": "module Paperclip\n  module Shoulda\n    module Matchers\n      # Ensures that the given instance or class validates the pre"
  },
  {
    "path": "lib/paperclip/matchers/validate_attachment_size_matcher.rb",
    "chars": 2845,
    "preview": "module Paperclip\n  module Shoulda\n    module Matchers\n      # Ensures that the given instance or class validates the siz"
  },
  {
    "path": "lib/paperclip/matchers.rb",
    "chars": 1999,
    "preview": "require 'paperclip/matchers/have_attached_file_matcher'\nrequire 'paperclip/matchers/validate_attachment_presence_matcher"
  },
  {
    "path": "lib/paperclip/media_type_spoof_detector.rb",
    "chars": 2357,
    "preview": "module Paperclip\n  class MediaTypeSpoofDetector\n    def self.using(file, name, content_type)\n      new(file, name, conte"
  },
  {
    "path": "lib/paperclip/missing_attachment_styles.rb",
    "chars": 2980,
    "preview": "require 'paperclip/attachment_registry'\nrequire 'set'\n\nmodule Paperclip\n  class << self\n    attr_writer :registered_atta"
  },
  {
    "path": "lib/paperclip/processor.rb",
    "chars": 2076,
    "preview": "module Paperclip\n  # Paperclip processors allow you to modify attached files when they are\n  # attached in any way you a"
  },
  {
    "path": "lib/paperclip/processor_helpers.rb",
    "chars": 1632,
    "preview": "module Paperclip\n  module ProcessorHelpers\n    class NoSuchProcessor < StandardError; end\n\n    def processor(name) #:nod"
  },
  {
    "path": "lib/paperclip/rails_environment.rb",
    "chars": 368,
    "preview": "module Paperclip\n  class RailsEnvironment\n    def self.get\n      new.get\n    end\n\n    def get\n      if rails_exists? && "
  },
  {
    "path": "lib/paperclip/railtie.rb",
    "chars": 748,
    "preview": "require 'paperclip'\nrequire 'paperclip/schema'\n\nmodule Paperclip\n  require 'rails'\n\n  class Railtie < Rails::Railtie\n   "
  },
  {
    "path": "lib/paperclip/schema.rb",
    "chars": 2803,
    "preview": "require 'active_support/deprecation'\n\nmodule Paperclip\n  # Provides helper methods that can be used in migrations.\n  mod"
  },
  {
    "path": "lib/paperclip/storage/filesystem.rb",
    "chars": 4094,
    "preview": "module Paperclip\n  module Storage\n    # The default place to store attachments is in the filesystem. Files on the local\n"
  },
  {
    "path": "lib/paperclip/storage/fog.rb",
    "chars": 8332,
    "preview": "module Paperclip\n  module Storage\n    # fog is a modern and versatile cloud computing library for Ruby.\n    # Among othe"
  },
  {
    "path": "lib/paperclip/storage/s3.rb",
    "chars": 18375,
    "preview": "module Paperclip\n  module Storage\n    # Amazon's S3 file hosting service is a scalable, easy place to store files for\n  "
  },
  {
    "path": "lib/paperclip/storage.rb",
    "chars": 102,
    "preview": "require \"paperclip/storage/filesystem\"\nrequire \"paperclip/storage/fog\"\nrequire \"paperclip/storage/s3\"\n"
  },
  {
    "path": "lib/paperclip/style.rb",
    "chars": 3972,
    "preview": "module Paperclip\n  # The Style class holds the definition of a thumbnail style,  applying\n  # whatever processing is req"
  },
  {
    "path": "lib/paperclip/tempfile.rb",
    "chars": 1371,
    "preview": "module Paperclip\n  # Overriding some implementation of Tempfile\n  class Tempfile < ::Tempfile\n    # Due to how ImageMagi"
  },
  {
    "path": "lib/paperclip/tempfile_factory.rb",
    "chars": 388,
    "preview": "module Paperclip\n  class TempfileFactory\n\n    def generate(name = random_name)\n      @name = name\n      file = Tempfile."
  },
  {
    "path": "lib/paperclip/thumbnail.rb",
    "chars": 6021,
    "preview": "module Paperclip\n  # Handles thumbnailing images that are uploaded.\n  class Thumbnail < Processor\n\n    attr_accessor :cu"
  },
  {
    "path": "lib/paperclip/url_generator.rb",
    "chars": 1835,
    "preview": "require 'uri'\nrequire 'active_support/core_ext/module/delegation'\n\nmodule Paperclip\n  class UrlGenerator\n    def initial"
  },
  {
    "path": "lib/paperclip/validators/attachment_content_type_validator.rb",
    "chars": 3510,
    "preview": "module Paperclip\n  module Validators\n    class AttachmentContentTypeValidator < ActiveModel::EachValidator\n      def ini"
  },
  {
    "path": "lib/paperclip/validators/attachment_file_name_validator.rb",
    "chars": 2739,
    "preview": "module Paperclip\n  module Validators\n    class AttachmentFileNameValidator < ActiveModel::EachValidator\n      def initia"
  },
  {
    "path": "lib/paperclip/validators/attachment_file_type_ignorance_validator.rb",
    "chars": 1003,
    "preview": "require 'active_model/validations/presence'\n\nmodule Paperclip\n  module Validators\n    class AttachmentFileTypeIgnoranceV"
  },
  {
    "path": "lib/paperclip/validators/attachment_presence_validator.rb",
    "chars": 1018,
    "preview": "require 'active_model/validations/presence'\n\nmodule Paperclip\n  module Validators\n    class AttachmentPresenceValidator "
  },
  {
    "path": "lib/paperclip/validators/attachment_size_validator.rb",
    "chars": 3952,
    "preview": "require 'active_model/validations/numericality'\n\nmodule Paperclip\n  module Validators\n    class AttachmentSizeValidator "
  },
  {
    "path": "lib/paperclip/validators/media_type_spoof_detection_validator.rb",
    "chars": 1149,
    "preview": "require 'active_model/validations/presence'\n\nmodule Paperclip\n  module Validators\n    class MediaTypeSpoofDetectionValid"
  },
  {
    "path": "lib/paperclip/validators.rb",
    "chars": 2932,
    "preview": "require 'active_model'\nrequire 'active_support/concern'\nrequire 'active_support/core_ext/array/wrap'\nrequire 'paperclip/"
  },
  {
    "path": "lib/paperclip/version.rb",
    "chars": 94,
    "preview": "module Paperclip\n  unless defined?(Paperclip::VERSION)\n    VERSION = \"6.1.0\".freeze\n  end\nend\n"
  },
  {
    "path": "lib/paperclip.rb",
    "chars": 10410,
    "preview": "# Paperclip allows file attachments that are stored in the filesystem. All graphical\n# transformations are done using th"
  },
  {
    "path": "lib/tasks/paperclip.rake",
    "chars": 5270,
    "preview": "require 'paperclip/attachment_registry'\n\nmodule Paperclip\n  module Task\n    def self.obtain_class\n      class_name = ENV"
  },
  {
    "path": "paperclip.gemspec",
    "chars": 2163,
    "preview": "$LOAD_PATH.push File.expand_path(\"../lib\", __FILE__)\nrequire 'paperclip/version'\n\nGem::Specification.new do |s|\n  s.name"
  },
  {
    "path": "shoulda_macros/paperclip.rb",
    "chars": 4896,
    "preview": "require 'paperclip/matchers'\n\nmodule Paperclip\n  # =Paperclip Shoulda Macros\n  #\n  # These macros are intended for use w"
  },
  {
    "path": "spec/database.yml",
    "chars": 49,
    "preview": "test:\n  adapter: sqlite3\n  database: \":memory:\"\n\n"
  },
  {
    "path": "spec/paperclip/attachment_definitions_spec.rb",
    "chars": 442,
    "preview": "require 'spec_helper'\n\ndescribe \"Attachment Definitions\" do\n  it 'returns all of the attachments on the class' do\n    re"
  },
  {
    "path": "spec/paperclip/attachment_processing_spec.rb",
    "chars": 2744,
    "preview": "require 'spec_helper'\n\ndescribe 'Attachment Processing' do\n  before { rebuild_class }\n\n  context 'using validates_attach"
  },
  {
    "path": "spec/paperclip/attachment_registry_spec.rb",
    "chars": 4127,
    "preview": "require 'spec_helper'\n\ndescribe 'Attachment Registry' do\n  before do\n    Paperclip::AttachmentRegistry.clear\n  end\n\n  co"
  },
  {
    "path": "spec/paperclip/attachment_spec.rb",
    "chars": 51655,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::Attachment do\n\n  it \"is not present when file not set\" do\n    rebuild_class\n "
  },
  {
    "path": "spec/paperclip/content_type_detector_spec.rb",
    "chars": 2001,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::ContentTypeDetector do\n  it 'returns a meaningful content type for open xml s"
  },
  {
    "path": "spec/paperclip/file_command_content_type_detector_spec.rb",
    "chars": 1399,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::FileCommandContentTypeDetector do\n  it 'returns a content type based on the c"
  },
  {
    "path": "spec/paperclip/filename_cleaner_spec.rb",
    "chars": 412,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::FilenameCleaner do\n  it 'converts invalid characters to underscores' do\n    c"
  },
  {
    "path": "spec/paperclip/geometry_detector_spec.rb",
    "chars": 1147,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::GeometryDetector do\n  it 'identifies an image and extract its dimensions' do\n"
  },
  {
    "path": "spec/paperclip/geometry_parser_spec.rb",
    "chars": 1822,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::GeometryParser do\n  it 'identifies an image and extract its dimensions with n"
  },
  {
    "path": "spec/paperclip/geometry_spec.rb",
    "chars": 9094,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::Geometry do\n  context \"Paperclip::Geometry\" do\n    it \"correctly reports its "
  },
  {
    "path": "spec/paperclip/glue_spec.rb",
    "chars": 1076,
    "preview": "# require \"spec_helper\"\n\ndescribe Paperclip::Glue do\n  describe \"when ActiveRecord does not exist\" do\n    before do\n    "
  },
  {
    "path": "spec/paperclip/has_attached_file_spec.rb",
    "chars": 4709,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::HasAttachedFile do\n  context '#define_on' do\n    it 'defines a setter on the "
  },
  {
    "path": "spec/paperclip/integration_spec.rb",
    "chars": 20940,
    "preview": "require 'spec_helper'\nrequire 'open-uri'\n\ndescribe 'Paperclip' do\n  around do |example|\n    files_before = ObjectSpace.e"
  },
  {
    "path": "spec/paperclip/interpolations_spec.rb",
    "chars": 10362,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::Interpolations do\n  it \"returns all methods but the infrastructure when sent "
  },
  {
    "path": "spec/paperclip/io_adapters/abstract_adapter_spec.rb",
    "chars": 3974,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::AbstractAdapter do\n  class TestAdapter < Paperclip::AbstractAdapter\n    attr_"
  },
  {
    "path": "spec/paperclip/io_adapters/attachment_adapter_spec.rb",
    "chars": 3680,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::AttachmentAdapter do\n  before do\n    rebuild_model path: \"tmp/:class/:attachm"
  },
  {
    "path": "spec/paperclip/io_adapters/data_uri_adapter_spec.rb",
    "chars": 2498,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::DataUriAdapter do\n  before do\n    Paperclip::DataUriAdapter.register\n  end\n\n "
  },
  {
    "path": "spec/paperclip/io_adapters/empty_string_adapter_spec.rb",
    "chars": 352,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::EmptyStringAdapter do\n  context 'a new instance' do\n    before do\n      @subj"
  },
  {
    "path": "spec/paperclip/io_adapters/file_adapter_spec.rb",
    "chars": 3755,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::FileAdapter do\n  context \"a new instance\" do\n    context \"with normal file\" d"
  },
  {
    "path": "spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb",
    "chars": 3730,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::HttpUrlProxyAdapter do\n  before do\n    @open_return = StringIO.new(\"xxx\")\n   "
  },
  {
    "path": "spec/paperclip/io_adapters/identity_adapter_spec.rb",
    "chars": 222,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::IdentityAdapter do\n  it \"responds to #new by returning the argument\" do\n    a"
  },
  {
    "path": "spec/paperclip/io_adapters/nil_adapter_spec.rb",
    "chars": 508,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::NilAdapter do\n  context 'a new instance' do\n    before do\n      @subject = Pa"
  },
  {
    "path": "spec/paperclip/io_adapters/registry_spec.rb",
    "chars": 934,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::AttachmentRegistry do\n  context \"for\" do\n    before do\n      class AdapterTes"
  },
  {
    "path": "spec/paperclip/io_adapters/stringio_adapter_spec.rb",
    "chars": 1857,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::StringioAdapter do\n  context \"a new instance\" do\n    before do\n      @content"
  },
  {
    "path": "spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb",
    "chars": 4352,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::UploadedFileAdapter do\n  context \"a new instance\" do\n    context \"with Upload"
  },
  {
    "path": "spec/paperclip/io_adapters/uri_adapter_spec.rb",
    "chars": 6260,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::UriAdapter do\n  let(:content_type) { \"image/png\" }\n  let(:meta) { {} }\n\n  bef"
  },
  {
    "path": "spec/paperclip/matchers/have_attached_file_matcher_spec.rb",
    "chars": 542,
    "preview": "require 'spec_helper'\nrequire 'paperclip/matchers'\n\ndescribe Paperclip::Shoulda::Matchers::HaveAttachedFileMatcher do\n  "
  },
  {
    "path": "spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb",
    "chars": 3625,
    "preview": "require 'spec_helper'\nrequire 'paperclip/matchers'\n\ndescribe Paperclip::Shoulda::Matchers::ValidateAttachmentContentType"
  },
  {
    "path": "spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb",
    "chars": 1709,
    "preview": "require 'spec_helper'\nrequire 'paperclip/matchers'\n\ndescribe Paperclip::Shoulda::Matchers::ValidateAttachmentPresenceMat"
  },
  {
    "path": "spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb",
    "chars": 2391,
    "preview": "require 'spec_helper'\nrequire 'paperclip/matchers'\n\ndescribe Paperclip::Shoulda::Matchers::ValidateAttachmentSizeMatcher"
  },
  {
    "path": "spec/paperclip/media_type_spoof_detector_spec.rb",
    "chars": 4423,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::MediaTypeSpoofDetector do\n  it 'rejects a file that is named .html and identi"
  },
  {
    "path": "spec/paperclip/meta_class_spec.rb",
    "chars": 685,
    "preview": "require 'spec_helper'\n\ndescribe 'Metaclasses' do\n  context \"A meta-class of dummy\" do\n    if active_support_version >= \""
  },
  {
    "path": "spec/paperclip/paperclip_missing_attachment_styles_spec.rb",
    "chars": 3679,
    "preview": "require 'spec_helper'\n\ndescribe 'Missing Attachment Styles' do\n  before do\n    Paperclip::AttachmentRegistry.clear\n  end"
  },
  {
    "path": "spec/paperclip/paperclip_spec.rb",
    "chars": 5336,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip do\n  context \".run\" do\n    before do\n      Paperclip.options[:log_command] = f"
  },
  {
    "path": "spec/paperclip/plural_cache_spec.rb",
    "chars": 983,
    "preview": "require 'spec_helper'\n\ndescribe 'Plural cache' do\n  it 'caches pluralizations' do\n    cache = Paperclip::Interpolations:"
  },
  {
    "path": "spec/paperclip/processor_helpers_spec.rb",
    "chars": 2245,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::ProcessorHelpers do\n  describe '.load_processor' do\n    context 'when the fil"
  },
  {
    "path": "spec/paperclip/processor_spec.rb",
    "chars": 917,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::Processor do\n  it \"instantiates and call #make when sent #make to the class\" "
  },
  {
    "path": "spec/paperclip/rails_environment_spec.rb",
    "chars": 881,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::RailsEnvironment do\n\n  it \"returns nil when Rails isn't defined\" do\n    reset"
  },
  {
    "path": "spec/paperclip/rake_spec.rb",
    "chars": 3213,
    "preview": "require 'spec_helper'\nrequire 'rake'\nload './lib/tasks/paperclip.rake'\n\ndescribe Rake do\n  context \"calling `rake paperc"
  },
  {
    "path": "spec/paperclip/schema_spec.rb",
    "chars": 9297,
    "preview": "require 'spec_helper'\nrequire 'paperclip/schema'\nrequire 'active_support/testing/deprecation'\n\ndescribe Paperclip::Schem"
  },
  {
    "path": "spec/paperclip/storage/filesystem_spec.rb",
    "chars": 2141,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::Storage::Filesystem do\n  context \"Filesystem\" do\n    context \"normal file\" do"
  },
  {
    "path": "spec/paperclip/storage/fog_spec.rb",
    "chars": 18323,
    "preview": "require 'spec_helper'\nrequire 'fog/aws'\nrequire 'fog/local'\nrequire 'timecop'\n\ndescribe Paperclip::Storage::Fog do\n  con"
  },
  {
    "path": "spec/paperclip/storage/s3_live_spec.rb",
    "chars": 5625,
    "preview": "require 'spec_helper'\n\nunless ENV[\"S3_BUCKET\"].blank?\n  describe Paperclip::Storage::S3, 'Live S3' do\n    context \"when "
  },
  {
    "path": "spec/paperclip/storage/s3_spec.rb",
    "chars": 49171,
    "preview": "require \"spec_helper\"\nrequire \"aws-sdk-s3\"\n\ndescribe Paperclip::Storage::S3 do\n  before do\n    Aws.config[:stub_response"
  },
  {
    "path": "spec/paperclip/style_spec.rb",
    "chars": 9504,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::Style do\n  context \"A style rule\" do\n    before do\n      @attachment = attach"
  },
  {
    "path": "spec/paperclip/tempfile_factory_spec.rb",
    "chars": 1052,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::TempfileFactory do\n  it \"is able to generate a tempfile with the right name\" "
  },
  {
    "path": "spec/paperclip/tempfile_spec.rb",
    "chars": 752,
    "preview": "require \"spec_helper\"\n\ndescribe Paperclip::Tempfile do\n  context \"A Paperclip Tempfile\" do\n    before do\n      @tempfile"
  },
  {
    "path": "spec/paperclip/thumbnail_spec.rb",
    "chars": 15729,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::Thumbnail do\n  context \"An image\" do\n    before do\n      @file = File.new(fix"
  },
  {
    "path": "spec/paperclip/url_generator_spec.rb",
    "chars": 8215,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::UrlGenerator do\n  it \"uses the given interpolator\" do\n    expected = \"the exp"
  },
  {
    "path": "spec/paperclip/validators/attachment_content_type_validator_spec.rb",
    "chars": 9498,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::Validators::AttachmentContentTypeValidator do\n  before do\n    rebuild_model\n "
  },
  {
    "path": "spec/paperclip/validators/attachment_file_name_validator_spec.rb",
    "chars": 4673,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::Validators::AttachmentFileNameValidator do\n  before do\n    rebuild_model\n    "
  },
  {
    "path": "spec/paperclip/validators/attachment_presence_validator_spec.rb",
    "chars": 1944,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::Validators::AttachmentPresenceValidator do\n  before do\n    rebuild_model\n    "
  },
  {
    "path": "spec/paperclip/validators/attachment_size_validator_spec.rb",
    "chars": 6538,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::Validators::AttachmentSizeValidator do\n  before do\n    rebuild_model\n    @dum"
  },
  {
    "path": "spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb",
    "chars": 1676,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::Validators::MediaTypeSpoofDetectionValidator do\n  before do\n    rebuild_model"
  },
  {
    "path": "spec/paperclip/validators_spec.rb",
    "chars": 6141,
    "preview": "require 'spec_helper'\n\ndescribe Paperclip::Validators do\n  context \"using the helper\" do\n    before do\n      rebuild_cla"
  },
  {
    "path": "spec/spec_helper.rb",
    "chars": 1449,
    "preview": "require 'rubygems'\nrequire 'rspec'\nrequire 'active_record'\nrequire 'active_record/version'\nrequire 'active_support'\nrequ"
  },
  {
    "path": "spec/support/assertions.rb",
    "chars": 2202,
    "preview": "module Assertions\n  def assert(truthy, message = nil)\n    expect(!!truthy).to(eq(true), message)\n  end\n\n  def assert_equ"
  },
  {
    "path": "spec/support/fake_model.rb",
    "chars": 331,
    "preview": "class FakeModel\n  attr_accessor(\n    :avatar_file_name,\n    :avatar_file_size,\n    :avatar_updated_at,\n    :avatar_conte"
  },
  {
    "path": "spec/support/fake_rails.rb",
    "chars": 160,
    "preview": "class FakeRails\n  def initialize(env, root)\n    @env = env\n    @root = root\n  end\n\n  attr_accessor :env, :root\n\n  def co"
  },
  {
    "path": "spec/support/fixtures/empty.html",
    "chars": 14,
    "preview": "<html></html>\n"
  },
  {
    "path": "spec/support/fixtures/fog.yml",
    "chars": 191,
    "preview": "development:\n    provider: AWS\n    aws_access_key_id: AWS_ID\n    aws_secret_access_key: AWS_SECRET\ntest:\n    provider: A"
  },
  {
    "path": "spec/support/fixtures/s3.yml",
    "chars": 174,
    "preview": "development:\n  key: 54321\nproduction:\n  key: 12345\ntest:\n  bucket: <%= ENV['S3_BUCKET'] %>\n  access_key_id: <%= ENV['S3_"
  },
  {
    "path": "spec/support/fixtures/text.txt",
    "chars": 11,
    "preview": "paperclip!\n"
  },
  {
    "path": "spec/support/matchers/accept.rb",
    "chars": 105,
    "preview": "RSpec::Matchers.define :accept do |expected|\n  match do |actual|\n    actual.matches?(expected)\n  end\nend\n"
  },
  {
    "path": "spec/support/matchers/exist.rb",
    "chars": 98,
    "preview": "RSpec::Matchers.define :exist do |expected|\n  match do |actual|\n    File.exist?(actual)\n  end\nend\n"
  },
  {
    "path": "spec/support/matchers/have_column.rb",
    "chars": 605,
    "preview": "RSpec::Matchers.define :have_column do |column_name|\n  chain :with_default do |default|\n    @default = default\n  end\n\n  "
  },
  {
    "path": "spec/support/mock_attachment.rb",
    "chars": 512,
    "preview": "class MockAttachment\n  attr_accessor :updated_at, :original_filename\n  attr_reader :options\n\n  def initialize(options = "
  },
  {
    "path": "spec/support/mock_interpolator.rb",
    "chars": 566,
    "preview": "class MockInterpolator\n  def initialize(options = {})\n    @options = options\n  end\n\n  def interpolate(pattern, attachmen"
  },
  {
    "path": "spec/support/mock_url_generator_builder.rb",
    "chars": 630,
    "preview": "class MockUrlGeneratorBuilder\n  def initializer\n  end\n\n  def new(attachment)\n    @attachment = attachment\n    @attachmen"
  },
  {
    "path": "spec/support/model_reconstruction.rb",
    "chars": 2048,
    "preview": "module ModelReconstruction\n  def reset_class class_name\n    ActiveRecord::Base.send(:include, Paperclip::Glue)\n    Objec"
  },
  {
    "path": "spec/support/reporting.rb",
    "chars": 264,
    "preview": "module Reporting\n  def silence_stream(stream)\n    old_stream = stream.dup\n    stream.reopen(RbConfig::CONFIG['host_os'] "
  },
  {
    "path": "spec/support/test_data.rb",
    "chars": 264,
    "preview": "module TestData\n  def attachment(options={})\n    Paperclip::Attachment.new(:avatar, FakeModel.new, options)\n  end\n\n  def"
  },
  {
    "path": "spec/support/version_helper.rb",
    "chars": 138,
    "preview": "module VersionHelper\n  def active_support_version\n    ActiveSupport::VERSION::STRING\n  end\n\n  def ruby_version\n    RUBY_"
  }
]

// ... and 3 more files (download for full content)

About this extraction

This page contains the full source code of the thoughtbot/paperclip GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 178 files (647.5 KB), approximately 167.0k tokens, and a symbol index with 805 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!