pax_global_header 0000666 0000000 0000000 00000000064 14577507162 0014530 g ustar 00root root 0000000 0000000 52 comment=cf2f011774ff449b3107c422540610e698d3dc28
carrierwave-3.0.7/ 0000775 0000000 0000000 00000000000 14577507162 0014051 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/.github/ 0000775 0000000 0000000 00000000000 14577507162 0015411 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/.github/dependabot.yml 0000664 0000000 0000000 00000000164 14577507162 0020242 0 ustar 00root root 0000000 0000000 version: 2
updates:
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: "weekly"
carrierwave-3.0.7/.github/workflows/ 0000775 0000000 0000000 00000000000 14577507162 0017446 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/.github/workflows/test.yml 0000664 0000000 0000000 00000004057 14577507162 0021156 0 ustar 00root root 0000000 0000000 name: Test
on: [push, pull_request]
jobs:
test:
name: RSpec and Cucumber
strategy:
fail-fast: false
matrix:
ruby: [ '2.7', '3.0', '3.1', '3.2', jruby ]
gemfile: [ gemfiles/rails-7-0.gemfile ]
experimental: [ false ]
include:
- ruby: '2.5'
gemfile: gemfiles/rails-6-0.gemfile
experimental: false
- ruby: '2.6'
gemfile: gemfiles/rails-6-1.gemfile
experimental: false
- ruby: '3.2'
gemfile: gemfiles/rails-7-1.gemfile
experimental: false
- ruby: '3.2'
gemfile: gemfiles/rails-main.gemfile
experimental: true
- ruby: ruby-head
gemfile: gemfiles/rails-7-1.gemfile
experimental: false
- ruby: jruby-head
gemfile: gemfiles/rails-7-0.gemfile
experimental: true
runs-on: ubuntu-20.04
continue-on-error: ${{ matrix.experimental }}
env:
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
JRUBY_OPTS: --debug
steps:
- uses: actions/checkout@v4
- name: Setup ImageMagick policy
run: sudo sh -c 'echo '\'''\'' > /etc/ImageMagick-6/policy.xml'
- name: Update package list
run: sudo apt update
- name: Install ghostscript to process PDF
run: sudo apt-get -y install ghostscript
- name: Install libvips-dev for Carrierwave::Vips
run: sudo apt-get install libvips-dev
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- name: Run RSpec
run: bundle exec rake spec
- name: Run Cucumber
run: bundle exec rake features
rubocop:
name: RuboCop
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: "3.2"
bundler-cache: true
- name: Run check
run: bundle exec rubocop
carrierwave-3.0.7/.gitignore 0000664 0000000 0000000 00000000360 14577507162 0016040 0 ustar 00root root 0000000 0000000 doc
.yardoc
.DS_Store
spec/public
pkg
doc
more/activerecord/spec/db
more/activerecord/spec/public
more/datamapper/spec/public
*.project
spec/fixtures/*_copy.png
spec/test.log
spec/tmp
*.swp
.rvmrc
.idea
.bundle
Gemfile.lock
gemfiles/*.lock
carrierwave-3.0.7/.rubocop.yml 0000664 0000000 0000000 00000010141 14577507162 0016320 0 ustar 00root root 0000000 0000000 AllCops:
NewCops: disable
SuggestExtensions: false
TargetRubyVersion: 2.5
Bundler/OrderedGems:
Exclude:
- 'Gemfile'
- 'gemfiles/*'
Gemspec/OrderedDependencies:
Exclude:
- 'carrierwave.gemspec'
Layout/AccessModifierIndentation:
EnforcedStyle: outdent
Layout/DotPosition:
Enabled: false
Layout/EmptyLineAfterGuardClause:
Enabled: false
Layout/EmptyLinesAroundBlockBody:
Enabled: false
Layout/EmptyLinesAroundClassBody:
Enabled: false
Layout/EmptyLinesAroundModuleBody:
Enabled: false
Layout/FirstHashElementIndentation:
EnforcedStyle: consistent
Layout/HashAlignment:
Enabled: false
Layout/MultilineMethodCallIndentation:
EnforcedStyle: indented
Layout/SpaceAroundEqualsInParameterDefault:
Enabled: false
Layout/SpaceAroundOperators:
Enabled: false
Layout/SpaceBeforeBlockBraces:
Enabled: false
Layout/SpaceInsideBlockBraces:
Enabled: false
Layout/SpaceInsideHashLiteralBraces:
Enabled: false
Layout/SpaceInsideParens:
Enabled: false
Lint/AmbiguousBlockAssociation:
Enabled: false
Lint/AmbiguousRegexpLiteral:
Enabled: false
Lint/MissingSuper:
Exclude:
- 'spec/processing/rmagick_spec.rb'
Lint/UnusedBlockArgument:
Enabled: false
Lint/UnusedMethodArgument:
Enabled: false
Lint/UselessMethodDefinition:
Exclude:
- 'spec/**/*'
Metrics/AbcSize:
Enabled: false
Metrics/BlockLength:
Enabled: false
Metrics/BlockNesting:
Max: 5
Metrics/ClassLength:
Enabled: false
Metrics/CyclomaticComplexity:
Max: 28
Metrics/MethodLength:
Enabled: false
Metrics/ModuleLength:
Enabled: false
Metrics/ParameterLists:
Enabled: false
Metrics/PerceivedComplexity:
Max: 31
Naming/AccessorMethodName:
Enabled: false
Naming/FileName:
Exclude:
- 'gemfiles/*'
Naming/MethodParameterName:
Enabled: false
Naming/PredicateName:
Exclude:
- 'spec/**/*'
- 'lib/carrierwave/sanitized_file.rb'
- 'lib/carrierwave/test/matchers.rb'
Naming/VariableNumber:
Enabled: false
Security/Eval:
Exclude:
- 'lib/carrierwave/uploader/configuration.rb'
- 'spec/storage/fog_helper.rb'
Security/Open:
Exclude:
- 'spec/storage/fog_helper.rb'
Style/Alias:
EnforcedStyle: prefer_alias_method
Style/BlockDelimiters:
Enabled: false
Style/ClassAndModuleChildren:
Exclude:
- 'spec/tmp/app/uploaders/my_module/avatar_uploader.rb'
Style/ClassVars:
Exclude:
- 'lib/carrierwave/uploader/cache.rb'
Style/CommentedKeyword:
Enabled: false
Style/ConditionalAssignment:
Enabled: false
Style/Documentation:
Enabled: false
Style/EmptyMethod:
Enabled: false
Style/EvalWithLocation:
Exclude:
- 'spec/storage/fog_helper.rb'
Style/FormatString:
Enabled: false
Style/FormatStringToken:
EnforcedStyle: template
Style/FrozenStringLiteralComment:
Enabled: false
Style/GuardClause:
Enabled: false
Style/HashSyntax:
Enabled: false
Style/IfUnlessModifier:
Enabled: false
Style/Lambda:
Enabled: false
Style/MultilineBlockChain:
Enabled: false
Style/NegatedIf:
Enabled: false
Style/Next:
Enabled: false
Style/NumericLiterals:
MinDigits: 11
Style/OptionalBooleanParameter:
Enabled: false
Style/ParallelAssignment:
Enabled: false
Style/PercentLiteralDelimiters:
Enabled: false
Style/PerlBackrefs:
Enabled: false
Style/PreferredHashMethods:
Enabled: false
Style/Proc:
Enabled: false
Style/RedundantRegexpCharacterClass:
Enabled: false
Style/RedundantRegexpEscape:
Enabled: false
Style/RedundantSelf:
Enabled: false
Style/RegexpLiteral:
Enabled: false
Style/RescueModifier:
Enabled: false
Style/SafeNavigation:
Enabled: false
Style/SingleLineMethods:
Enabled: false
Style/SlicingWithRange:
Enabled: false
Style/SpecialGlobalVars:
EnforcedStyle: use_perl_names
Style/StringConcatenation:
Enabled: false
Style/StringLiterals:
Enabled: false
Style/SymbolArray:
EnforcedStyle: brackets
Style/SymbolProc:
Enabled: false
Style/TernaryParentheses:
Enabled: false
Style/TrailingCommaInHashLiteral:
Enabled: false
Style/TrivialAccessors:
Enabled: false
Style/WordArray:
EnforcedStyle: percent
MinSize: 8
Style/ZeroLengthPredicate:
Enabled: false
Layout/LineLength:
Max: 256
carrierwave-3.0.7/CHANGELOG.md 0000664 0000000 0000000 00000042004 14577507162 0015662 0 ustar 00root root 0000000 0000000 # Carrierwave History/Changelog
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
## 3.0.7 - 2024-03-23
### Security
* Fix Content-Type allowlist bypass vulnerability remained (@mshibuya [00676e2](https://github.com/carrierwaveuploader/carrierwave/commit/00676e23d7f4beac12beddee6f2486b686fb7e46), [GHSA-vfmv-jfc5-pjjw](https://github.com/carrierwaveuploader/carrierwave/security/advisories/GHSA-vfmv-jfc5-pjjw))
## 3.0.6 - 2024-03-09
### Fixed
* Fix #derived_versions and #active_sibling_versions returning an Array where Hash is expected (@mshibuya [46e4f20](https://github.com/carrierwaveuploader/carrierwave/commit/46e4f20f8f85f75043cec76aa1f331c55a3ba103))
* Fix incompatibility with Marcel 1.0.3 (@schinery [#2728](https://github.com/carrierwaveuploader/carrierwave/pull/2728), [#2729](https://github.com/carrierwaveuploader/carrierwave/issues/2729))
* Fix assigning a file with the same name not marking the column as changed (@mshibuya [4c65b39](https://github.com/carrierwaveuploader/carrierwave/commit/4c65b393cd85b66bc256d04363cf3e3a97c8fd64), [#2719](https://github.com/carrierwaveuploader/carrierwave/issues/2719))
* Fix failing to remove files with ActiveRecord 7.1 after_commit order change enabled (@mshibuya [63113e9](https://github.com/carrierwaveuploader/carrierwave/commit/63113e96dc172114cb92af239ba73e50ed8a72f2), [#2713](https://github.com/carrierwaveuploader/carrierwave/issues/2713))
## 3.0.5 - 2023-11-29
### Fixed
* Remove unnecessary if clause within #filename left in the uploader template (@rajyan [#2711](https://github.com/carrierwaveuploader/carrierwave/pull/2711))
### Security
* Fix Content-Type allowlist bypass vulnerability, possibly leading to XSS (@mshibuya [863d425](https://github.com/carrierwaveuploader/carrierwave/commit/863d425c76eba12c3294227b39018f6b2dccbbf3), [GHSA-gxhx-g4fq-49hj](https://github.com/carrierwaveuploader/carrierwave/security/advisories/GHSA-gxhx-g4fq-49hj))
## 3.0.4 - 2023-10-08
### Fixed
* Fix model's dirty state remaining after update (@rajyan [#2707](https://github.com/carrierwaveuploader/carrierwave/pull/2707), [#2702](https://github.com/carrierwaveuploader/carrierwave/issues/2702))
* Fix #dup modifying the original object (@rajyan [#2690](https://github.com/carrierwaveuploader/carrierwave/pull/2690), [#2706](https://github.com/carrierwaveuploader/carrierwave/pull/2706), [#2689](https://github.com/carrierwaveuploader/carrierwave/issues/2689), [#2700](https://github.com/carrierwaveuploader/carrierwave/issues/2700))
* Fix #dup not respecting the :mount_on option, causing MissingAttributeError (@marsz [#2691](https://github.com/carrierwaveuploader/carrierwave/pull/2691))
## 3.0.3 - 2023-08-21
### Fixed
* Fix #dup modifying the original object (@mshibuya [37f36f7](https://github.com/carrierwaveuploader/carrierwave/commit/37f36f7ccf035ffb19cbd3964928b3abf2d5e1b1), [#2687](https://github.com/carrierwaveuploader/carrierwave/issues/2687))
* Fix wrongly removing files on transaction rollback (@mshibuya, @rajyan [eb03fe1](https://github.com/carrierwaveuploader/carrierwave/commit/eb03fe124c3a7acf3ffc913c7d432208ba3aa7ca), [#2686](https://github.com/carrierwaveuploader/carrierwave/pull/2686), [#2685](https://github.com/carrierwaveuploader/carrierwave/issues/2685))
## 3.0.2 - 2023-08-01
### Fixed
* Fix deduplicated filename not being persisted (@mshibuya [#2679](https://github.com/carrierwaveuploader/carrierwave/pull/2679), [#2678](https://github.com/carrierwaveuploader/carrierwave/issues/2678), [#2677](https://github.com/carrierwaveuploader/carrierwave/pull/2677))
## 3.0.1 - 2023-07-22
### Fixed
* Fix not respecting the parent's #enable_processing value after reading its own (@mshibuya [2df0f53](https://github.com/carrierwaveuploader/carrierwave/commit/2df0f53f1d5fa30a198aa148ef33f1ab924404e4), [#2676](https://github.com/carrierwaveuploader/carrierwave/issues/2676))
* Fix NoMethodError when a record is rolled back (@y-yagi [#2674](https://github.com/carrierwaveuploader/carrierwave/pull/2674), [#2675](https://github.com/carrierwaveuploader/carrierwave/issues/2675))
* Fix filename suffix being removed due to unnecessary deduplication (@mshibuya [d68a111](https://github.com/carrierwaveuploader/carrierwave/commit/d68a1111cfae4309d703caa19d9c19226bc01686), [#2672](https://github.com/carrierwaveuploader/carrierwave/issues/2672))
* Fix #dup causing unintended name deduplication of copied files (@mshibuya [b732acd](https://github.com/carrierwaveuploader/carrierwave/commit/b732acd63209897e6375a3706330df2c38e3f327), [#2670](https://github.com/carrierwaveuploader/carrierwave/issues/2670))
* Fix initialization failing when active_support/core_ext is not loaded yet (@mshibuya [875d972](https://github.com/carrierwaveuploader/carrierwave/commit/875d972dc78b8416de7768457793baa4d6220a4f))
## 3.0.0 - 2023-07-02
_No changes._
## 3.0.0.rc - 2023-06-11
### Added
* Support adding suffix to filename on store when path collides with the existing ones (@mshibuya [07a5632](https://github.com/carrierwaveuploader/carrierwave/commit/07a5632a3f30ddcb21b10a75f003a7eaeaa072ad), [#1855](https://github.com/carrierwaveuploader/carrierwave/issues/1855))
* Add image dimension validation (@TsubasaYoshida [#2592](https://github.com/carrierwaveuploader/carrierwave/pull/2592), [3b1f8b4](https://github.com/carrierwaveuploader/carrierwave/commit/3b1f8b41f8c0896aa6ebe64bac23622c14a8b8d9))
* Provide validation error details via ActiveModel::Errors#details (@mshibuya [9013999](https://github.com/carrierwaveuploader/carrierwave/commit/90139995fc11978da909db71b1d43c0690c7c9d2), [#2150](https://github.com/carrierwaveuploader/carrierwave/issues/2150))
* Support clearing #remote_urls by assigning nil (@mshibuya [8307f93](https://github.com/carrierwaveuploader/carrierwave/commit/8307f93c29b833d34efaae63c33d36e737d94715), [#2067](https://github.com/carrierwaveuploader/carrierwave/issues/2067))
* Support configuration of download retry wait time (@tricknotes [#2646](https://github.com/carrierwaveuploader/carrierwave/pull/2646))
* Support for ActiveRecord::Base#dup (@mshibuya, @BrianHawley [19b33b8](https://github.com/carrierwaveuploader/carrierwave/commit/19b33b876cd58e7af28dc718fd4f47bb539b78f9), [#2645](https://github.com/carrierwaveuploader/carrierwave/pull/2645), [#1962](https://github.com/carrierwaveuploader/carrierwave/issues/1962))
* Add CarrierWave::Storage::Fog::File#to_file for interface consistency with SanitizedFile (@mshibuya [68ce83a](https://github.com/carrierwaveuploader/carrierwave/commit/68ce83a7b105d52c6af1b410727dd590c3960f7d), [#1960](https://github.com/carrierwaveuploader/carrierwave/issues/1960))
* Allow SanitizedFile to accept read with an optional length and output_buffer arguments (@mshibuya [9096459](https://github.com/carrierwaveuploader/carrierwave/commit/90964596aa3d0b7acea584012f0f5888d622ea1b), [#1959](https://github.com/carrierwaveuploader/carrierwave/issues/1959))
### Changed
* Stop relying on ActiveModel::Dirty change tracking for removal of unnecessary files (@mshibuya [aac25c1](https://github.com/carrierwaveuploader/carrierwave/commit/aac25c10af4218d6e1e70f90154b847b54ce0334))
* Create versions lazily to reflect subclass configurations properly (@mshibuya [1531a67](https://github.com/carrierwaveuploader/carrierwave/commit/1531a67366f0e25e3d298133a72c81b6c9c0dc83), [#1957](https://github.com/carrierwaveuploader/carrierwave/issues/1957), [#2619](https://github.com/carrierwaveuploader/carrierwave/issues/2619))
* [BREAKING CHANGE] Use the resulting file extension on changing format by :convert (@mshibuya [#2659](https://github.com/carrierwaveuploader/carrierwave/pull/2659), [#2125](https://github.com/carrierwaveuploader/carrierwave/issues/2125), [#2126](https://github.com/carrierwaveuploader/carrierwave/issues/2126), [#2254](https://github.com/carrierwaveuploader/carrierwave/issues/2254))
* Prioritize Magic-detected content type for spoof-tolerance (@mshibuya [a2ca59c](https://github.com/carrierwaveuploader/carrierwave/commit/a2ca59cbe67046ba7818c64849a9a4ffa90306db), [#2570](https://github.com/carrierwaveuploader/carrierwave/issues/2570))
* Handle assignments in an ActiveModel::Dirty-friendly way (@mshibuya [#2658](https://github.com/carrierwaveuploader/carrierwave/pull/2658), [#2404](https://github.com/carrierwaveuploader/carrierwave/issues/2404), [#2409](https://github.com/carrierwaveuploader/carrierwave/issues/2409), [#2468](https://github.com/carrierwaveuploader/carrierwave/issues/2468))
* Give a stable name to classes created by the mount_uploader block (@mshibuya [f5b09b8](https://github.com/carrierwaveuploader/carrierwave/commit/f5b09b844d99245a3b4d0ba01efd4972be4ee5be), [#2407](https://github.com/carrierwaveuploader/carrierwave/issues/2407), [#2471](https://github.com/carrierwaveuploader/carrierwave/issues/2471))
* Give a stable name to version classes (@mshibuya [a9de756](https://github.com/carrierwaveuploader/carrierwave/commit/a9de7565eabb4cdca05bb090cdf797eb1720c09c), [#2407](https://github.com/carrierwaveuploader/carrierwave/issues/2407), [#2471](https://github.com/carrierwaveuploader/carrierwave/issues/2471))
### Fixed
* Fix CarrierWave::Storage::Fog::File#read breaking when the file doesn't exist (@mshibuya [246eb01](https://github.com/carrierwaveuploader/carrierwave/commit/246eb012e15a75f7621bf9933f90a0f4742bd6e8), [#2524](https://github.com/carrierwaveuploader/carrierwave/issues/2524))
* Fix to preserve the original URI as much as possible on download (@mshibuya [2f3afaf](https://github.com/carrierwaveuploader/carrierwave/commit/2f3afafb738ae848a8a2d164780571cf9a7eb6ce), [#2631](https://github.com/carrierwaveuploader/carrierwave/issues/2631))
* Fix not to invoke content type detection on #copy_to as it's costly (@mshibuya [6c6e2dc](https://github.com/carrierwaveuploader/carrierwave/commit/6c6e2dc9cf7747c0c1571d315473b246ef582e1f), [#2465](https://github.com/carrierwaveuploader/carrierwave/issues/2465))
* Fix calling #=~ on non-String breaking in Ruby 3.2 (@aubinlrx [#2653](https://github.com/carrierwaveuploader/carrierwave/pull/2653), [fd03ddd](https://github.com/carrierwaveuploader/carrierwave/commit/fd03dddef55025cab83936fc2957e3c8c58772ae))
* Fix #clean_cache! to respect the uploader's root, not the global one (@sawasaki-narumi [#2652](https://github.com/carrierwaveuploader/carrierwave/pull/2652), [3cb9992](https://github.com/carrierwaveuploader/carrierwave/commit/3cb9992cc5fb8b113fe5b050651361f35d94adb4), [#2113](https://github.com/carrierwaveuploader/carrierwave/issues/2113))
* Fix to use helper method #fog_provider instead of checking #fog_credentials (@joshuamsager [#2660](https://github.com/carrierwaveuploader/carrierwave/pull/2660))
* Fix being unable to delete a file by assigning nil (@mshibuya [f8ea354](https://github.com/carrierwaveuploader/carrierwave/commit/f8ea35445e51c438b8cc8baf3e50079b5d423e34), [#2654](https://github.com/carrierwaveuploader/carrierwave/issues/2654), [#2613](https://github.com/carrierwaveuploader/carrierwave/pull/2613))
* Fix to raise exception when ImageMagick is not installed (@mshibuya [d90c399](https://github.com/carrierwaveuploader/carrierwave/commit/d90c399a6d2338203b1382f4ac4269863444d60d), [#2060](https://github.com/carrierwaveuploader/carrierwave/issues/2060))
* Fix to remove unnecessary floodfill in CarrierWave::RMagick#resize_and_pad (@mshibuya [f34a9bd](https://github.com/carrierwaveuploader/carrierwave/commit/f34a9bd26ed3e1006033a783c2ae8d86369993f6))
* Fix `#{column}_cache=` fails to be stored when set as a nested attribute (@mshibuya [e84d11e](https://github.com/carrierwaveuploader/carrierwave/commit/e84d11ec508d286ebab28195da815816abc62e41), [#2206](https://github.com/carrierwaveuploader/carrierwave/issues/2206))
* Fix to use AWS S3 regional endpoints when using virtual-hosted style (@mshibuya [8dace34](https://github.com/carrierwaveuploader/carrierwave/commit/8dace3456b5d1e0c3212ed1dc6c8b47dfd63b8ff), [#2523](https://github.com/carrierwaveuploader/carrierwave/issues/2523))
* Fix to respect condition on processing a derived version (@mshibuya [1fecddc](https://github.com/carrierwaveuploader/carrierwave/commit/1fecddc8ffe43426e9b5044dedfa7ac0b091cad8), [#2516](https://github.com/carrierwaveuploader/carrierwave/issues/2516))
* Fix #recreate_versions! affecting the original file (@mshibuya [a67bfb6](https://github.com/carrierwaveuploader/carrierwave/commit/a67bfb696dcba14c7cdfa2c1b5481f04d3ef8dae), [5f00715](https://github.com/carrierwaveuploader/carrierwave/commit/5f00715747d44dd7f57ee990a6b471ed786ac764), [#2480](https://github.com/carrierwaveuploader/carrierwave/issues/2480), [#2655](https://github.com/carrierwaveuploader/carrierwave/issues/2655))
* Fix `remove_#{column}!` doesn't remove the file immediately (@mshibuya [b719fb3](https://github.com/carrierwaveuploader/carrierwave/commit/b719fb373c48f23e874dfa1a333a954c01967fc1), [#2540](https://github.com/carrierwaveuploader/carrierwave/issues/2540))
* Fix column value populated without a file when using filename override (@mshibuya [f1eff6e](https://github.com/carrierwaveuploader/carrierwave/commit/f1eff6e212fb0c374c9235968bfc4e7580bf1e2a), [#2284](https://github.com/carrierwaveuploader/carrierwave/issues/2284))
* Fix boolean configurations couldn't be set to false on a per-uploader basis (@megane42 [#2642](https://github.com/carrierwaveuploader/carrierwave/pull/2642))
* Fix #clean_cache! breaking with directories that doesn't conform to CarrierWave's cache_id format (@BrianHawley [#2641](https://github.com/carrierwaveuploader/carrierwave/pull/2641))
## 3.0.0.beta - 2022-11-19
### Added
* Add basename and fix extension value for fog file (@leductienttkt [#2587](https://github.com/carrierwaveuploader/carrierwave/pull/2587))
* Allow uploaders to accept unless conditions (@Vpatel1093 [#2588](https://github.com/carrierwaveuploader/carrierwave/pull/2588))
* Add retry option to download from remote url (@tashirosota [#2577](https://github.com/carrierwaveuploader/carrierwave/pull/2577))
### Deprecated
* #denylist was deprecated to prefer explicitly opting-in (@mshibuya [7a40ef7](https://github.com/carrierwaveuploader/carrierwave/commit/7a40ef7c4d5f3033f0f8401323f80bde14ca72b9), [#2536](https://github.com/carrierwaveuploader/carrierwave/issues/2536))
### Changed
* Completely migrate to allowlist/denylist terminology (@mshibuya [7a40ef7](https://github.com/carrierwaveuploader/carrierwave/commit/7a40ef7c4d5f3033f0f8401323f80bde14ca72b9), [#2536](https://github.com/carrierwaveuploader/carrierwave/issues/2536))
* Remove implementation-dependent information from an error message (@akihikodaki [#2499](https://github.com/carrierwaveuploader/carrierwave/pull/2499))
* Replace mini_mime with marcel (@pjmartorell [#2552](https://github.com/carrierwaveuploader/carrierwave/pull/2552))
* [BREAKING CHANGE] Change to store files on after_save hook instead of after_commit, with performing cleanup when transaction is rolled back (@fsateler [#2546](https://github.com/carrierwaveuploader/carrierwave/pull/2546))
### Removed
* Drop support for Ruby < 2.5 and Rails 5.x (@mshibuya [229594f](https://github.com/carrierwaveuploader/carrierwave/commit/229594fb2ac7cfa59586162c0b3fc3d0b5bab978))
* Remove support for Merb (@seuros [#2566](https://github.com/carrierwaveuploader/carrierwave/pull/2566))
### Fixed
* Add Workaround for 'undefined method closed?' error caused by ssrf_filter 1.1 (@mshibuya [65bf0d9](https://github.com/carrierwaveuploader/carrierwave/commit/65bf0d94759f7d522a36698d4b81e3635b8ca572), [#2628](https://github.com/carrierwaveuploader/carrierwave/issues/2628))
* Fix Ruby 2.7 keyword argument warning in uploader process (@nachiket87 [#2636](https://github.com/carrierwaveuploader/carrierwave/pull/2636), [#2635](https://github.com/carrierwaveuploader/carrierwave/issues/2635))
* Raise DownloadError when no content is returned (@BrianHawley [#2633](https://github.com/carrierwaveuploader/carrierwave/pull/2633), [#2632](https://github.com/carrierwaveuploader/carrierwave/issues/2632))
* Add workaround for the API change in ssrf_filter 1.1 (@BrianHawley [#2629](https://github.com/carrierwaveuploader/carrierwave/pull/2629), [#2625](https://github.com/carrierwaveuploader/carrierwave/issues/2625))
* Fix Content-Type not being copied when using fog-google (@smnscp [#2614](https://github.com/carrierwaveuploader/carrierwave/pull/2614))
* Fix failing to save after limiting the columns with ActiveRecord's #select (@wonda-tea-coffee [#2613](https://github.com/carrierwaveuploader/carrierwave/pull/2613), [#2608](https://github.com/carrierwaveuploader/carrierwave/issues/2608))
* Fix content type detection for JSON files (@smnscp [#2618](https://github.com/carrierwaveuploader/carrierwave/pull/2618))
* Remove invalid byte sequences from the sanitized filename (@alexdunae [#2606](https://github.com/carrierwaveuploader/carrierwave/pull/2606))
* Fix issue with copying a fog file larger than 5GB (@slonopotamus [#2583](https://github.com/carrierwaveuploader/carrierwave/pull/2583))
* Stop closing StringIO-based file after CarrierWave::SanitizedFile#read (@aleksandrs-ledovskis [#2571](https://github.com/carrierwaveuploader/carrierwave/pull/2571))
Please check [2.x-stable](https://github.com/carrierwaveuploader/carrierwave/blob/2.x-stable/CHANGELOG.md) for previous changes.
carrierwave-3.0.7/CONTRIBUTING.md 0000664 0000000 0000000 00000002004 14577507162 0016276 0 ustar 00root root 0000000 0000000 # Contributing to CarrierWave
CarrierWave thrives on a large number of [contributors](https://github.com/carrierwaveuploader/carrierwave/contributors),
and pull requests are very welcome. Before submitting a pull request, please make sure that your changes are well tested.
First, make sure you have `imagemagick`, `ghostscript` and `libvips` installed. You may need `libmagic` as well.
Then, you'll need to install the gem dependencies:
bundle install
You should now be able to run the local tests:
bundle exec rake
You can also run the remote specs by creating a ~/.fog file:
```yaml
:carrierwave:
:aws_access_key_id: xxx
:aws_secret_access_key: yyy
:rackspace_username: xxx
:rackspace_api_key: yyy
:google_storage_access_key_id: xxx
:google_storage_secret_access_key: yyy
```
You should now be able to run the remote tests:
REMOTE=true bundle exec rake
Please test with the latest Ruby version using RVM if possible.
Don't forget to run the RuboCop check as well:
bundle exec rubocop
carrierwave-3.0.7/Gemfile 0000664 0000000 0000000 00000000515 14577507162 0015345 0 ustar 00root root 0000000 0000000 source "https://rubygems.org"
gem "rails", ">= 6.0.0"
gem "activemodel-serializers-xml"
gem 'sqlite3', platforms: :ruby
gem "activerecord-jdbcsqlite3-adapter", platform: [:jruby, :truffleruby]
# See https://github.com/fog/fog-google/issues/535 for this restriction.
gem "fog-google", "~> 1.13.0" if RUBY_VERSION.to_f < 2.7
gemspec
carrierwave-3.0.7/README.md 0000664 0000000 0000000 00000112201 14577507162 0015325 0 ustar 00root root 0000000 0000000 # CarrierWave
This gem provides a simple and extremely flexible way to upload files from Ruby applications.
It works well with Rack based web applications, such as Ruby on Rails.
[](https://github.com/carrierwaveuploader/carrierwave/actions)
[](https://codeclimate.com/github/carrierwaveuploader/carrierwave)
[](https://dependabot.com/compatibility-score.html?dependency-name=carrierwave&package-manager=bundler&version-scheme=semver)
## Information
* RDoc documentation [available on RubyDoc.info](https://rubydoc.info/gems/carrierwave)
* Source code [available on GitHub](http://github.com/carrierwaveuploader/carrierwave)
* More information, known limitations, and how-tos [available on the wiki](https://github.com/carrierwaveuploader/carrierwave/wiki)
## Getting Help
* Please ask the community on [Stack Overflow](https://stackoverflow.com/questions/tagged/carrierwave) for help if you have any questions. Please do not post usage questions on the issue tracker.
* Please report bugs on the [issue tracker](http://github.com/carrierwaveuploader/carrierwave/issues) but read the "getting help" section in the wiki first.
## Installation
Install the latest release:
```
$ gem install carrierwave
```
In Rails, add it to your Gemfile:
```ruby
gem 'carrierwave', '~> 3.0'
```
Finally, restart the server to apply the changes.
## Upgrading from 2.x or earlier
CarrierWave 3.0 comes with a change in the way of handling the file extension on conversion. This results in following issues if you use `process convert: :format` to change the file format:
- If you have it on the uploader itself (not within a version), the file extension of the cached file will change. That means if you serve both CarrierWave 2.x and 3.x simultaneously on the same workload (e.g. using blue-green deployment), a cache file stored by 2.x can't be retrieved by 3.x and vice versa.
- If you have it within a version, the file extension of the stored file will change. You need to perform `#recreate_versions!` to make it usable again.
To preserve the 2.x behavior, you can set `force_extension false` right after calling `process convert: :format`. See [#2659](https://github.com/carrierwaveuploader/carrierwave/pull/2659) for the detail.
## Getting Started
Start off by generating an uploader:
rails generate uploader Avatar
this should give you a file in:
app/uploaders/avatar_uploader.rb
Check out this file for some hints on how you can customize your uploader. It
should look something like this:
```ruby
class AvatarUploader < CarrierWave::Uploader::Base
storage :file
end
```
You can use your uploader class to store and retrieve files like this:
```ruby
uploader = AvatarUploader.new
uploader.store!(my_file)
uploader.retrieve_from_store!('my_file.png')
```
CarrierWave gives you a `store` for permanent storage, and a `cache` for
temporary storage. You can use different stores, including filesystem
and cloud storage.
Most of the time you are going to want to use CarrierWave together with an ORM.
It is quite simple to mount uploaders on columns in your model, so you can
simply assign files and get going:
### ActiveRecord
Make sure you are loading CarrierWave after loading your ORM, otherwise you'll
need to require the relevant extension manually, e.g.:
```ruby
require 'carrierwave/orm/activerecord'
```
Add a string column to the model you want to mount the uploader by creating
a migration:
rails g migration add_avatar_to_users avatar:string
rails db:migrate
Open your model file and mount the uploader:
```ruby
class User < ApplicationRecord
mount_uploader :avatar, AvatarUploader
end
```
Now you can cache files by assigning them to the attribute, they will
automatically be stored when the record is saved.
```ruby
u = User.new
u.avatar = params[:file] # Assign a file like this, or
# like this
File.open('somewhere') do |f|
u.avatar = f
end
u.save!
u.avatar.url # => '/url/to/file.png'
u.avatar.current_path # => 'path/to/file.png'
u.avatar_identifier # => 'file.png'
```
**Note**: `u.avatar` will never return nil, even if there is no photo associated to it.
To check if a photo was saved to the model, use `u.avatar.file.nil?` instead.
### DataMapper, Mongoid, Sequel
Other ORM support has been extracted into separate gems:
* [carrierwave-datamapper](https://github.com/carrierwaveuploader/carrierwave-datamapper)
* [carrierwave-mongoid](https://github.com/carrierwaveuploader/carrierwave-mongoid)
* [carrierwave-sequel](https://github.com/carrierwaveuploader/carrierwave-sequel)
There are more extensions listed in [the wiki](https://github.com/carrierwaveuploader/carrierwave/wiki)
## Multiple file uploads
CarrierWave also has convenient support for multiple file upload fields.
### ActiveRecord
Add a column which can store an array. This could be an array column or a JSON
column for example. Your choice depends on what your database supports. For
example, create a migration like this:
#### For databases with ActiveRecord json data type support (e.g. PostgreSQL, MySQL)
rails g migration add_avatars_to_users avatars:json
rails db:migrate
#### For database without ActiveRecord json data type support (e.g. SQLite)
rails g migration add_avatars_to_users avatars:string
rails db:migrate
__Note__: JSON datatype doesn't exists in SQLite adapter, that's why you can use a string datatype which will be serialized in model.
Open your model file and mount the uploader:
```ruby
class User < ApplicationRecord
mount_uploaders :avatars, AvatarUploader
serialize :avatars, JSON # If you use SQLite, add this line.
end
```
Make sure that you mount the uploader with write (mount_uploaders) with `s` not (mount_uploader)
in order to avoid errors when uploading multiple files
Make sure your file input fields are set up as multiple file fields. For
example in Rails you'll want to do something like this:
```erb
<%= form.file_field :avatars, multiple: true %>
```
Also, make sure your upload controller permits the multiple file upload attribute, *pointing to an empty array in a hash*. For example:
```ruby
params.require(:user).permit(:email, :first_name, :last_name, {avatars: []})
```
Now you can select multiple files in the upload dialog (e.g. SHIFT+SELECT), and they will
automatically be stored when the record is saved.
```ruby
u = User.new(params[:user])
u.save!
u.avatars[0].url # => '/url/to/file.png'
u.avatars[0].current_path # => 'path/to/file.png'
u.avatars[0].identifier # => 'file.png'
```
If you want to preserve existing files on uploading new one, you can go like:
```erb
<% user.avatars.each do |avatar| %>
<%= hidden_field :user, :avatars, multiple: true, value: avatar.identifier %>
<% end %>
<%= form.file_field :avatars, multiple: true %>
```
Sorting avatars is supported as well by reordering `hidden_field`, an example using jQuery UI Sortable is available [here](https://github.com/carrierwaveuploader/carrierwave/wiki/How-to%3A-Add%2C-remove-and-reorder-images-using-multiple-file-upload).
## Changing the storage directory
In order to change where uploaded files are put, just override the `store_dir`
method:
```ruby
class MyUploader < CarrierWave::Uploader::Base
def store_dir
'public/my/upload/directory'
end
end
```
This works for the file storage as well as Amazon S3 and Rackspace Cloud Files.
Define `store_dir` as `nil` if you'd like to store files at the root level.
If you store files outside the project root folder, you may want to define `cache_dir` in the same way:
```ruby
class MyUploader < CarrierWave::Uploader::Base
def cache_dir
'/tmp/projectname-cache'
end
end
```
## Changing the filename
To change the filename of uploaded files, you can override `#filename` method in the uploader.
```ruby
class MyUploader < CarrierWave::Uploader::Base
def filename
"image.#{file.extension}" # If you upload 'file.jpg', you'll get 'image.jpg'
end
end
```
Some old documentations (like [this](https://stackoverflow.com/a/5865117)) may instruct you to safeguard the filename value with `if original_filename`, but it's no longer necessary with CarrierWave 3.0 or later.
## Securing uploads
Certain files might be dangerous if uploaded to the wrong location, such as PHP
files or other script files. CarrierWave allows you to specify an allowlist of
allowed extensions or content types.
If you're mounting the uploader, uploading a file with the wrong extension will
make the record invalid instead. Otherwise, an error is raised.
```ruby
class MyUploader < CarrierWave::Uploader::Base
def extension_allowlist
%w(jpg jpeg gif png)
end
end
```
The same thing could be done using content types.
Let's say we need an uploader that accepts only images. This can be done like this
```ruby
class MyUploader < CarrierWave::Uploader::Base
def content_type_allowlist
/image\//
end
end
```
You can use a denylist to reject content types.
Let's say we need an uploader that reject JSON files. This can be done like this
```ruby
class NoJsonUploader < CarrierWave::Uploader::Base
def content_type_denylist
['application/text', 'application/json']
end
end
```
### CVE-2016-3714 (ImageTragick)
This version of CarrierWave has the ability to mitigate CVE-2016-3714. However, you **MUST** set a content_type_allowlist in your uploaders for this protection to be effective, and you **MUST** either disable ImageMagick's default SVG delegate or use the RSVG delegate for SVG processing.
A valid allowlist that will restrict your uploader to images only, and mitigate the CVE is:
```ruby
class MyUploader < CarrierWave::Uploader::Base
def content_type_allowlist
[/image\//]
end
end
```
**WARNING**: A `content_type_allowlist` is the only form of allowlist or denylist supported by CarrierWave that can effectively mitigate against CVE-2016-3714. Use of `extension_allowlist` will not inspect the file headers, and thus still leaves your application open to the vulnerability.
### Filenames and unicode chars
Another security issue you should care for is the file names (see
[Ruby On Rails Security Guide](http://guides.rubyonrails.org/security.html#file-uploads)).
By default, CarrierWave provides only English letters, arabic numerals and some symbols as
allowlisted characters in the file name. If you want to support local scripts (Cyrillic letters, letters with diacritics and so on), you
have to override `sanitize_regexp` method. It should return regular expression which would match
all *non*-allowed symbols.
```ruby
CarrierWave::SanitizedFile.sanitize_regexp = /[^[:word:]\.\-\+]/
```
Also make sure that allowing non-latin characters won't cause a compatibility issue with a third-party
plugins or client-side software.
## Setting the content type
As of v0.11.0, the `mime-types` gem is a runtime dependency and the content type is set automatically.
You no longer need to do this manually.
## Adding versions
Often you'll want to add different versions of the same file. The classic example is generating image thumbnails while preserving the original file to be used for high-quality representation.
In this section we'll explore how CarrierWave supports working with multiple versions. The image manipulation itself is covered in [another section](#manipulating-images).
```ruby
class MyUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
process resize_to_fit: [800, 800]
version :thumb do
process resize_to_fill: [200,200]
end
end
```
When this uploader is used, an uploaded image would be scaled to be no larger
than 800 by 800 pixels. The original aspect ratio will be kept.
A version called `:thumb` is then created, which is scaled
to exactly 200 by 200 pixels. The thumbnail uses `resize_to_fill` which makes sure
that the width and height specified are filled, only cropping
if the aspect ratio requires it.
The above uploader could be used like this:
```ruby
uploader = AvatarUploader.new
uploader.store!(my_file) # size: 1024x768
uploader.url # => '/url/to/my_file.png' # size: 800x800
uploader.thumb.url # => '/url/to/thumb_my_file.png' # size: 200x200
```
One important thing to remember is that process is called *before* versions are
created. This can cut down on processing cost.
### Conditional processing
If you want to use conditional process, you can only use `if` statement.
See `carrierwave/uploader/processing.rb` for details.
```ruby
class MyUploader < CarrierWave::Uploader::Base
process :scale => [200, 200], :if => :image?
def image?(carrier_wave_sanitized_file)
true
end
end
```
### Nested versions
It is possible to nest versions within versions:
```ruby
class MyUploader < CarrierWave::Uploader::Base
version :animal do
version :human
version :monkey
version :llama
end
end
```
### Conditional versions
Occasionally you want to restrict the creation of versions on certain
properties within the model or based on the picture itself.
```ruby
class MyUploader < CarrierWave::Uploader::Base
version :human, if: :is_human?
version :monkey, if: :is_monkey?
version :banner, if: :is_landscape?
private
def is_human? picture
model.can_program?(:ruby)
end
def is_monkey? picture
model.favorite_food == 'banana'
end
def is_landscape? picture
image = MiniMagick::Image.new(picture.path)
image[:width] > image[:height]
end
end
```
The `model` variable points to the instance object the uploader is attached to.
### Create versions from existing versions
For performance reasons, it is often useful to create versions from existing ones
instead of using the original file. If your uploader generates several versions
where the next is smaller than the last, it will take less time to generate from
a smaller, already processed image.
```ruby
class MyUploader < CarrierWave::Uploader::Base
version :thumb do
process resize_to_fill: [280, 280]
end
version :small_thumb, from_version: :thumb do
process resize_to_fill: [20, 20]
end
end
```
### Customizing version filenames
CarrierWave supports [customization of filename](#changing-the-filename) by overriding an uploader's
#filename method, but this doesn't work for versions because of the limitation on how CarrierWave
re-constructs the filename on retrieval of the stored file.
Instead, you can override `#full_filename` with providing a version-aware name.
```ruby
class MyUploader < CarrierWave::Uploader::Base
version :thumb do
def full_filename(for_file)
'thumb.png'
end
process convert: 'png'
end
end
```
Please note that `#full_filename` mustn't be constructed based on a dynamic value
that can change from the time of store and time of retrieval, since it will result in
being unable to retrieve a file previously stored.
## Making uploads work across form redisplays
Often you'll notice that uploaded files disappear when a validation fails.
CarrierWave has a feature that makes it easy to remember the uploaded file even
in that case. Suppose your `user` model has an uploader mounted on `avatar`
file, just add a hidden field called `avatar_cache` (don't forget to add it to
the attr_accessible list as necessary). In Rails, this would look like this:
```erb
<%= form_for @user, html: { multipart: true } do |f| %>
<% end %>
````
It might be a good idea to show the user that a file has been uploaded, in the
case of images, a small thumbnail would be a good indicator:
```erb
<%= form_for @user, html: { multipart: true } do |f| %>
<% end %>
```
## Removing uploaded files
If you want to remove a previously uploaded file on a mounted uploader, you can
easily add a checkbox to the form which will remove the file when checked.
```erb
<%= form_for @user, html: { multipart: true } do |f| %>
<%= image_tag(@user.avatar_url) if @user.avatar? %>
<%= f.file_field :avatar %>
<% end %>
```
If you want to remove the file manually, you can call remove_avatar!, then save the object.
```erb
@user.remove_avatar!
@user.save
#=> true
```
## Uploading files from a remote location
Your users may find it convenient to upload a file from a location on the Internet
via a URL. CarrierWave makes this simple, just add the appropriate attribute to your
form and you're good to go:
```erb
<%= form_for @user, html: { multipart: true } do |f| %>
<%= image_tag(@user.avatar_url) if @user.avatar? %>
<%= f.text_field :remote_avatar_url %>
<% end %>
```
If you're using ActiveRecord, CarrierWave will indicate invalid URLs and download
failures automatically with attribute validation errors. If you aren't, or you
disable CarrierWave's `validate_download` option, you'll need to handle those
errors yourself.
### Retry option for download from remote location
If you want to retry the download from the Remote URL, enable the download_retry_count option, an error occurs during download, it will try to execute the specified number of times.
This option is effective when the remote destination is unstable.
```rb
CarrierWave.configure do |config|
config.download_retry_count = 3 # Default 0
config.download_retry_wait_time = 3 # Default 5
end
```
## Providing a default URL
In many cases, especially when working with images, it might be a good idea to
provide a default url, a fallback in case no file has been uploaded. You can do
this easily by overriding the `default_url` method in your uploader:
```ruby
class MyUploader < CarrierWave::Uploader::Base
def default_url(*args)
"/images/fallback/" + [version_name, "default.png"].compact.join('_')
end
end
```
Or if you are using the Rails asset pipeline:
```ruby
class MyUploader < CarrierWave::Uploader::Base
def default_url(*args)
ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
end
end
```
## Recreating versions
You might come to a situation where you want to retroactively change a version
or add a new one. You can use the `recreate_versions!` method to recreate the
versions from the base file. This uses a naive approach which will re-upload and
process the specified version or all versions, if none is passed as an argument.
When you are generating random unique filenames you have to call `save!` on
the model after using `recreate_versions!`. This is necessary because
`recreate_versions!` doesn't save the new filename to the database. Calling
`save!` yourself will prevent that the database and file system are running
out of sync.
```ruby
instance = MyUploader.new
instance.recreate_versions!(:thumb, :large)
```
Or on a mounted uploader:
```ruby
User.find_each do |user|
user.avatar.recreate_versions!
end
```
Note: `recreate_versions!` will throw an exception on records without an image. To avoid this, scope the records to those with images or check if an image exists within the block. If you're using ActiveRecord, recreating versions for a user avatar might look like this:
```ruby
User.find_each do |user|
user.avatar.recreate_versions! if user.avatar?
end
```
## Configuring CarrierWave
CarrierWave has a broad range of configuration options, which you can configure,
both globally and on a per-uploader basis:
```ruby
CarrierWave.configure do |config|
config.permissions = 0666
config.directory_permissions = 0777
config.storage = :file
end
```
Or alternatively:
```ruby
class AvatarUploader < CarrierWave::Uploader::Base
permissions 0777
end
```
If you're using Rails, create an initializer for this:
config/initializers/carrierwave.rb
If you want CarrierWave to fail noisily in development, you can change these configs in your environment file:
```ruby
CarrierWave.configure do |config|
config.ignore_integrity_errors = false
config.ignore_processing_errors = false
config.ignore_download_errors = false
end
```
## Testing with CarrierWave
It's a good idea to test your uploaders in isolation. In order to speed up your
tests, it's recommended to switch off processing in your tests, and to use the
file storage. In Rails you could do that by adding an initializer with:
```ruby
if Rails.env.test? or Rails.env.cucumber?
CarrierWave.configure do |config|
config.storage = :file
config.enable_processing = false
end
end
```
Remember, if you have already set `storage :something` in your uploader, the `storage`
setting from this initializer will be ignored.
If you need to test your processing, you should test it in isolation, and enable
processing only for those tests that need it.
CarrierWave comes with some RSpec matchers which you may find useful:
```ruby
require 'carrierwave/test/matchers'
describe MyUploader do
include CarrierWave::Test::Matchers
let(:user) { double('user') }
let(:uploader) { MyUploader.new(user, :avatar) }
before do
MyUploader.enable_processing = true
File.open(path_to_file) { |f| uploader.store!(f) }
end
after do
MyUploader.enable_processing = false
uploader.remove!
end
context 'the thumb version' do
it "scales down a landscape image to be exactly 64 by 64 pixels" do
expect(uploader.thumb).to have_dimensions(64, 64)
end
end
context 'the small version' do
it "scales down a landscape image to fit within 200 by 200 pixels" do
expect(uploader.small).to be_no_larger_than(200, 200)
end
end
it "makes the image readable only to the owner and not executable" do
expect(uploader).to have_permissions(0600)
end
it "has the correct format" do
expect(uploader).to be_format('png')
end
end
```
If you're looking for minitest asserts, checkout [carrierwave_asserts](https://github.com/hcfairbanks/carrierwave_asserts).
Setting the enable_processing flag on an uploader will prevent any of the versions from processing as well.
Processing can be enabled for a single version by setting the processing flag on the version like so:
```ruby
@uploader.thumb.enable_processing = true
```
## Fog
If you want to use fog you must add in your CarrierWave initializer the
following lines
```ruby
config.fog_credentials = { ... } # Provider specific credentials
```
## Using Amazon S3
[Fog AWS](http://github.com/fog/fog-aws) is used to support Amazon S3. Ensure you have it in your Gemfile:
```ruby
gem "fog-aws"
```
You'll need to provide your fog_credentials and a fog_directory (also known as a bucket) in an initializer.
For the sake of performance it is assumed that the directory already exists, so please create it if it needs to be.
You can also pass in additional options, as documented fully in lib/carrierwave/storage/fog.rb. Here's a full example:
```ruby
CarrierWave.configure do |config|
config.fog_credentials = {
provider: 'AWS', # required
aws_access_key_id: 'xxx', # required unless using use_iam_profile
aws_secret_access_key: 'yyy', # required unless using use_iam_profile
use_iam_profile: true, # optional, defaults to false
region: 'eu-west-1', # optional, defaults to 'us-east-1'
host: 's3.example.com', # optional, defaults to nil
endpoint: 'https://s3.example.com:8080' # optional, defaults to nil
}
config.fog_directory = 'name_of_bucket' # required
config.fog_public = false # optional, defaults to true
config.fog_attributes = { cache_control: "public, max-age=#{365.days.to_i}" } # optional, defaults to {}
# For an application which utilizes multiple servers but does not need caches persisted across requests,
# uncomment the line :file instead of the default :storage. Otherwise, it will use AWS as the temp cache store.
# config.cache_storage = :file
end
```
In your uploader, set the storage to :fog
```ruby
class AvatarUploader < CarrierWave::Uploader::Base
storage :fog
end
```
That's it! You can still use the `CarrierWave::Uploader#url` method to return the url to the file on Amazon S3.
**Note**: for Carrierwave to work properly it needs credentials with the following permissions:
* `s3:ListBucket`
* `s3:PutObject`
* `s3:GetObject`
* `s3:DeleteObject`
* `s3:PutObjectAcl`
## Using Rackspace Cloud Files
[Fog](http://github.com/fog/fog) is used to support Rackspace Cloud Files. Ensure you have it in your Gemfile:
```ruby
gem "fog"
```
You'll need to configure a directory (also known as a container), username and API key in the initializer.
For the sake of performance it is assumed that the directory already exists, so please create it if need be.
Using a US-based account:
```ruby
CarrierWave.configure do |config|
config.fog_credentials = {
provider: 'Rackspace',
rackspace_username: 'xxxxxx',
rackspace_api_key: 'yyyyyy',
rackspace_region: :ord # optional, defaults to :dfw
}
config.fog_directory = 'name_of_directory'
end
```
Using a UK-based account:
```ruby
CarrierWave.configure do |config|
config.fog_credentials = {
provider: 'Rackspace',
rackspace_username: 'xxxxxx',
rackspace_api_key: 'yyyyyy',
rackspace_auth_url: Fog::Rackspace::UK_AUTH_ENDPOINT,
rackspace_region: :lon
}
config.fog_directory = 'name_of_directory'
end
```
You can optionally include your CDN host name in the configuration.
This is *highly* recommended, as without it every request requires a lookup
of this information.
```ruby
config.asset_host = "http://c000000.cdn.rackspacecloud.com"
```
In your uploader, set the storage to :fog
```ruby
class AvatarUploader < CarrierWave::Uploader::Base
storage :fog
end
```
That's it! You can still use the `CarrierWave::Uploader#url` method to return
the url to the file on Rackspace Cloud Files.
## Using Google Cloud Storage
[Fog](http://github.com/fog/fog-google) is used to support Google Cloud Storage. Ensure you have it in your Gemfile:
```ruby
gem "fog-google"
```
You'll need to configure a directory (also known as a bucket) and the credentials in the initializer.
For the sake of performance it is assumed that the directory already exists, so please create it if need be.
Please read the [fog-google README](https://github.com/fog/fog-google/blob/master/README.md) on how to get credentials.
For Google Storage JSON API (recommended):
```ruby
CarrierWave.configure do |config|
config.fog_credentials = {
provider: 'Google',
google_project: 'my-project',
google_json_key_string: 'xxxxxx'
# or use google_json_key_location if using an actual file
}
config.fog_directory = 'google_cloud_storage_bucket_name'
end
```
For Google Storage XML API:
```ruby
CarrierWave.configure do |config|
config.fog_credentials = {
provider: 'Google',
google_storage_access_key_id: 'xxxxxx',
google_storage_secret_access_key: 'yyyyyy'
}
config.fog_directory = 'google_cloud_storage_bucket_name'
end
```
In your uploader, set the storage to :fog
```ruby
class AvatarUploader < CarrierWave::Uploader::Base
storage :fog
end
```
That's it! You can still use the `CarrierWave::Uploader#url` method to return
the url to the file on Google.
## Optimized Loading of Fog
Since Carrierwave doesn't know which parts of Fog you intend to use, it will just load the entire library (unless you use e.g. [`fog-aws`, `fog-google`] instead of fog proper). If you prefer to load fewer classes into your application, you need to load those parts of Fog yourself *before* loading CarrierWave in your Gemfile. Ex:
```ruby
gem "fog", "~> 1.27", require: "fog/rackspace/storage"
gem "carrierwave"
```
A couple of notes about versions:
* This functionality was introduced in Fog v1.20.
* This functionality is slated for CarrierWave v1.0.0.
If you're not relying on Gemfile entries alone and are requiring "carrierwave" anywhere, ensure you require "fog/rackspace/storage" before it. Ex:
```ruby
require "fog/rackspace/storage"
require "carrierwave"
```
Beware that this specific require is only needed when working with a fog provider that was not extracted to its own gem yet.
A list of the extracted providers can be found in the page of the `fog` organizations [here](https://github.com/fog).
When in doubt, inspect `Fog.constants` to see what has been loaded.
## Dynamic Asset Host
The `asset_host` config property can be assigned a proc (or anything that responds to `call`) for generating the host dynamically. The proc-compliant object gets an instance of the current `CarrierWave::Storage::Fog::File` or `CarrierWave::SanitizedFile` as its only argument.
```ruby
CarrierWave.configure do |config|
config.asset_host = proc do |file|
identifier = # some logic
"http://#{identifier}.cdn.rackspacecloud.com"
end
end
```
## Manipulating images
If you're uploading images, you'll probably want to manipulate them in some way,
you might want to create thumbnail images for example.
### Using MiniMagick
MiniMagick performs all the operations using the 'convert' CLI which is part of the standard ImageMagick kit.
This allows you to have the power of ImageMagick without having to worry about installing
all the RMagick libraries, it often results in higher memory footprint.
See the MiniMagick site for more details:
https://github.com/minimagick/minimagick
To install Imagemagick on OSX with homebrew type the following:
```
$ brew install imagemagick
```
And the ImageMagick command line options for more for what's on offer:
http://www.imagemagick.org/script/command-line-options.php
Currently, the MiniMagick carrierwave processor provides exactly the same methods as
for the RMagick processor.
```ruby
class AvatarUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
process resize_to_fill: [200, 200]
end
```
#### List of available processing methods:
- `convert` - Changes the image encoding format to the given format(eg. jpg). This operation is treated specially to trigger the change of the file extension, so it matches with the format of the resulting file.
- `resize_to_limit` - Resize the image to fit within the specified dimensions while retaining the original aspect ratio. Will only resize the image if it is larger than the specified dimensions. The resulting image may be shorter or narrower than specified in the smaller dimension but will not be larger than the specified values.
- `resize_to_fit` - Resize the image to fit within the specified dimensions while retaining the original aspect ratio. The image may be shorter or narrower than specified in the smaller dimension but will not be larger than the specified values.
- `resize_to_fill` - Resize the image to fit within the specified dimensions while retaining the aspect ratio of the original image. If necessary, crop the image in the larger dimension. Optionally, a "gravity" may be specified, for example "Center", or "NorthEast".
- `resize_and_pad` - Resize the image to fit within the specified dimensions while retaining the original aspect ratio. If necessary, will pad the remaining area with the given color, which defaults to transparent (for gif and png, white for jpeg). Optionally, a "gravity" may be specified, as above.
See `carrierwave/processing/mini_magick.rb` for details.
### Using RMagick
CarrierWave also comes with support for RMagick, a well-known image processing library.
To use it, you'll need to include this in your Uploader:
```ruby
class AvatarUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
end
```
The RMagick module gives you a few methods, like
`CarrierWave::RMagick#resize_to_fill` which manipulate the image file in some
way. You can set a `process` callback, which will call that method any time a
file is uploaded.
There is a demonstration of convert here.
```ruby
class AvatarUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
process resize_to_fill: [200, 200]
process convert: 'png'
end
```
Check out the manipulate! method, which makes it easy for you to write your own
manipulation methods.
## Migrating from Paperclip
If you are using Paperclip, you can use the provided compatibility module:
```ruby
class AvatarUploader < CarrierWave::Uploader::Base
include CarrierWave::Compatibility::Paperclip
end
```
See the documentation for `CarrierWave::Compatibility::Paperclip` for more
details.
Be sure to use mount_on to specify the correct column:
```ruby
mount_uploader :avatar, AvatarUploader, mount_on: :avatar_file_name
```
## I18n
The Active Record validations use the Rails `i18n` framework. Add these keys to
your translations file:
```yaml
errors:
messages:
carrierwave_processing_error: failed to be processed
carrierwave_integrity_error: is not of an allowed file type
carrierwave_download_error: could not be downloaded
extension_allowlist_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
extension_denylist_error: "You are not allowed to upload %{extension} files, prohibited types: %{prohibited_types}"
content_type_allowlist_error: "You are not allowed to upload %{content_type} files, allowed types: %{allowed_types}"
content_type_denylist_error: "You are not allowed to upload %{content_type} files"
processing_error: "Failed to manipulate, maybe it is not an image?"
min_size_error: "File size should be greater than %{min_size}"
max_size_error: "File size should be less than %{max_size}"
min_width_error: "Image width should be greater than %{min_width}px"
max_width_error: "Image width should be less than %{max_width}px"
min_height_error: "Image height should be greater than %{min_height}px"
max_height_error: "Image height should be less than %{max_height}px"
```
The [`carrierwave-i18n`](https://github.com/carrierwaveuploader/carrierwave-i18n)
library adds support for additional locales.
## Large files
By default, CarrierWave copies an uploaded file twice, first copying the file into the cache, then
copying the file into the store. For large files, this can be prohibitively time consuming.
You may change this behavior by overriding either or both of the `move_to_cache` and
`move_to_store` methods:
```ruby
class MyUploader < CarrierWave::Uploader::Base
def move_to_cache
true
end
def move_to_store
true
end
end
```
When the `move_to_cache` and/or `move_to_store` methods return true, files will be moved (instead of copied) to the cache and store respectively.
This has only been tested with the local filesystem store.
## Skipping ActiveRecord callbacks
By default, mounting an uploader into an ActiveRecord model will add a few
callbacks. For example, this code:
```ruby
class User
mount_uploader :avatar, AvatarUploader
end
```
Will add these callbacks:
```ruby
before_save :write_avatar_identifier
after_save :store_previous_changes_for_avatar
after_commit :remove_avatar!, on: :destroy
after_commit :mark_remove_avatar_false, on: :update
after_commit :remove_previously_stored_avatar, on: :update
after_commit :store_avatar!, on: [:create, :update]
```
If you want to skip any of these callbacks (eg. you want to keep the existing
avatar, even after uploading a new one), you can use ActiveRecord’s
`skip_callback` method.
```ruby
class User
mount_uploader :avatar, AvatarUploader
skip_callback :commit, :after, :remove_previously_stored_avatar
end
```
## Uploader Callbacks
In addition to the ActiveRecord callbacks described above, uploaders also have callbacks.
```ruby
class MyUploader < ::CarrierWave::Uploader::Base
before :remove, :log_removal
private
def log_removal
::Rails.logger.info(format('Deleting file on S3: %s', @file))
end
end
```
Uploader callbacks can be `before` or `after` the following events:
```
cache
process
remove
retrieve_from_cache
store
```
## Contributing to CarrierWave
See [CONTRIBUTING.md](https://github.com/carrierwaveuploader/carrierwave/blob/master/CONTRIBUTING.md)
## License
The MIT License (MIT)
Copyright (c) 2008 Jonas Nicklas
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.
carrierwave-3.0.7/Rakefile 0000664 0000000 0000000 00000001057 14577507162 0015521 0 ustar 00root root 0000000 0000000 require 'rubygems'
begin
require 'bundler/setup'
rescue LoadError
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
end
require 'bundler'
Bundler::GemHelper.install_tasks
require 'rake'
require 'rspec/core/rake_task'
require 'cucumber'
require 'cucumber/rake/task'
desc "Run all examples"
RSpec::Core::RakeTask.new(:spec) do |t|
t.rspec_opts = %w[--color]
end
desc "Run cucumber features"
Cucumber::Rake::Task.new(:features) do |t|
t.cucumber_opts = "features --format progress"
end
task :default => [:spec, :features]
carrierwave-3.0.7/carrierwave.gemspec 0000664 0000000 0000000 00000003446 14577507162 0017737 0 ustar 00root root 0000000 0000000 lib = File.expand_path('lib', __dir__)
$:.unshift lib unless $:.include?(lib)
require 'carrierwave/version'
Gem::Specification.new do |s|
s.name = "carrierwave"
s.version = CarrierWave::VERSION
s.authors = ["Jonas Nicklas"]
s.description = "Upload files in your Ruby applications, map them to a range of ORMs, store them on different backends."
s.summary = "Ruby file upload library"
s.email = ["jonas.nicklas@gmail.com"]
s.extra_rdoc_files = ["README.md"]
s.files = Dir["{bin,lib}/**/*", "README.md"]
s.homepage = 'https://github.com/carrierwaveuploader/carrierwave'
s.rdoc_options = ["--main"]
s.require_paths = ["lib"]
s.licenses = ["MIT"]
s.required_ruby_version = ">= 2.5.0"
s.add_dependency "activesupport", ">= 6.0.0"
s.add_dependency "activemodel", ">= 6.0.0"
s.add_dependency "image_processing", "~> 1.1"
s.add_dependency "marcel", "~> 1.0.0"
s.add_dependency "addressable", "~> 2.6"
s.add_dependency "ssrf_filter", "~> 1.0"
s.add_development_dependency "csv", "~> 3.0"
s.add_development_dependency "cucumber", "~> 2.3"
s.add_development_dependency "rspec", "~> 3.4"
s.add_development_dependency "rspec-retry"
s.add_development_dependency "rubocop", "~> 1.28"
s.add_development_dependency "webmock"
s.add_development_dependency "fog-aws"
s.add_development_dependency "fog-google", ["~> 1.7", "!= 1.12.1"]
s.add_development_dependency "fog-local"
s.add_development_dependency "fog-rackspace"
s.add_development_dependency "mini_magick", ">= 3.6.0"
if RUBY_ENGINE != 'jruby'
s.add_development_dependency "rmagick", ">= 2.16"
end
s.add_development_dependency "timecop"
s.add_development_dependency "generator_spec", ">= 0.9.1"
s.add_development_dependency "pry"
if RUBY_ENGINE != 'jruby'
s.add_development_dependency "pry-byebug"
end
end
carrierwave-3.0.7/cucumber.yml 0000664 0000000 0000000 00000000114 14577507162 0016375 0 ustar 00root root 0000000 0000000 default: --format pretty --no-source
html: --format html --out features.html carrierwave-3.0.7/features/ 0000775 0000000 0000000 00000000000 14577507162 0015667 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/features/caching.feature 0000664 0000000 0000000 00000003231 14577507162 0020637 0 ustar 00root root 0000000 0000000 Feature: uploader with file storage
In order to be able to temporarily store files to disk
As a developer using CarrierWave
I want to cache files
Scenario: cache a file
Given an uploader class that uses the 'file' storage
And an instance of that class
When I cache the file 'fixtures/bork.txt'
Then there should be a file called 'bork.txt' somewhere in a subdirectory of 'public/uploads/tmp'
And the file called 'bork.txt' in a subdirectory of 'public/uploads/tmp' should be identical to the file at 'fixtures/bork.txt'
Scenario: cache two files in succession
Given an uploader class that uses the 'file' storage
And an instance of that class
When I cache the file 'fixtures/bork.txt'
Then there should be a file called 'bork.txt' somewhere in a subdirectory of 'public/uploads/tmp'
And the file called 'bork.txt' in a subdirectory of 'public/uploads/tmp' should be identical to the file at 'fixtures/bork.txt'
When I cache the file 'fixtures/monkey.txt'
Then there should be a file called 'monkey.txt' somewhere in a subdirectory of 'public/uploads/tmp'
And the file called 'monkey.txt' in a subdirectory of 'public/uploads/tmp' should be identical to the file at 'fixtures/monkey.txt'
Scenario: retrieving a file from cache
Given an uploader class that uses the 'file' storage
And an instance of that class
And the file 'fixtures/bork.txt' is cached file at 'public/uploads/tmp/1369894322-345-1234-2255/bork.txt'
When I retrieve the cache name '1369894322-345-1234-2255/bork.txt' from the cache
Then the uploader should have 'public/uploads/tmp/1369894322-345-1234-2255/bork.txt' as its current path
carrierwave-3.0.7/features/download.feature 0000664 0000000 0000000 00000001567 14577507162 0021064 0 ustar 00root root 0000000 0000000 Feature: downloading files
In order to allow users to upload remote files
As a developer using CarrierWave
I want to download files to the filesystem via HTTP
Background:
Given an uploader class that uses the 'file' storage
And an instance of that class
Scenario: download a file
When I download the file 'http://s3.amazonaws.com/Monkey/testfile.txt'
Then there should be a file called 'testfile.txt' somewhere in a subdirectory of 'public/uploads/tmp'
And the file called 'testfile.txt' in a subdirectory of 'public/uploads/tmp' should contain 'S3 Remote File'
Scenario: downloading a file then storing
When I download the file 'http://s3.amazonaws.com/Monkey/testfile.txt'
And I store the file
Then there should be a file at 'public/uploads/testfile.txt'
And the file at 'public/uploads/testfile.txt' should contain 'S3 Remote File'
carrierwave-3.0.7/features/file_storage.feature 0000664 0000000 0000000 00000003652 14577507162 0021715 0 ustar 00root root 0000000 0000000 Feature: uploader with file storage
In order to be awesome
As a developer using CarrierWave
I want to upload files to the filesystem
Background:
Given an uploader class that uses the 'file' storage
And an instance of that class
Scenario: store a file
When I store the file 'fixtures/bork.txt'
Then there should be a file at 'public/uploads/bork.txt'
And the file at 'public/uploads/bork.txt' should be identical to the file at 'fixtures/bork.txt'
Scenario: store two files in succession
When I store the file 'fixtures/bork.txt'
Then there should be a file at 'public/uploads/bork.txt'
And the file at 'public/uploads/bork.txt' should be identical to the file at 'fixtures/bork.txt'
When I store the file 'fixtures/monkey.txt'
Then there should be a file at 'public/uploads/monkey.txt'
And the file at 'public/uploads/monkey.txt' should be identical to the file at 'fixtures/monkey.txt'
Scenario: cache a file and then store it
When I cache the file 'fixtures/bork.txt'
Then there should be a file called 'bork.txt' somewhere in a subdirectory of 'public/uploads/tmp'
And the file called 'bork.txt' in a subdirectory of 'public/uploads/tmp' should be identical to the file at 'fixtures/bork.txt'
And there should not be a file at 'public/uploads/bork.txt'
When I store the file
Then there should be a file at 'public/uploads/bork.txt'
And the file at 'public/uploads/bork.txt' should be identical to the file at 'fixtures/bork.txt'
Scenario: retrieving a file from cache then storing
Given the file 'fixtures/bork.txt' is cached file at 'public/uploads/tmp/1369894322-345-1234-2255/bork.txt'
When I retrieve the cache name '1369894322-345-1234-2255/bork.txt' from the cache
And I store the file
Then there should be a file at 'public/uploads/bork.txt'
And the file at 'public/uploads/bork.txt' should be identical to the file at 'fixtures/bork.txt'
carrierwave-3.0.7/features/file_storage_overridden_filename.feature 0000664 0000000 0000000 00000004016 14577507162 0025771 0 ustar 00root root 0000000 0000000 Feature: uploader with file storage and overridden filename
In order to be awesome
As a developer using CarrierWave
I want to upload files to the filesystem with an overridden filename
Background:
Given an uploader class that uses the 'file' storage
And that the uploader reverses the filename
And an instance of that class
Scenario: store a file
When I store the file 'fixtures/bork.txt'
Then there should be a file at 'public/uploads/txt.krob'
And the file at 'public/uploads/txt.krob' should be identical to the file at 'fixtures/bork.txt'
Scenario: store two files in succession
When I store the file 'fixtures/bork.txt'
Then there should be a file at 'public/uploads/txt.krob'
And the file at 'public/uploads/txt.krob' should be identical to the file at 'fixtures/bork.txt'
When I store the file 'fixtures/monkey.txt'
Then there should be a file at 'public/uploads/txt.yeknom'
And the file at 'public/uploads/txt.yeknom' should be identical to the file at 'fixtures/monkey.txt'
Scenario: cache a file and then store it
When I cache the file 'fixtures/bork.txt'
Then there should be a file called 'bork.txt' somewhere in a subdirectory of 'public/uploads/tmp'
And the file called 'bork.txt' in a subdirectory of 'public/uploads/tmp' should be identical to the file at 'fixtures/bork.txt'
And there should not be a file at 'public/uploads/txt.krob'
When I store the file
Then there should be a file at 'public/uploads/txt.krob'
And the file at 'public/uploads/txt.krob' should be identical to the file at 'fixtures/bork.txt'
Scenario: retrieving a file from cache then storing
Given the file 'fixtures/bork.txt' is cached file at 'public/uploads/tmp/1369894322-345-1234-2255/bork.txt'
When I retrieve the cache name '1369894322-345-1234-2255/bork.txt' from the cache
And I store the file
Then there should be a file at 'public/uploads/txt.krob'
And the file at 'public/uploads/txt.krob' should be identical to the file at 'fixtures/bork.txt'
carrierwave-3.0.7/features/file_storage_overridden_store_dir.feature 0000664 0000000 0000000 00000004112 14577507162 0026200 0 ustar 00root root 0000000 0000000 Feature: uploader with file storage and overridden store dir
In order to be awesome
As a developer using CarrierWave
I want to upload files to the filesystem
Background:
Given an uploader class that uses the 'file' storage
And that the uploader has the store_dir overridden to 'public/monkey/llama'
And an instance of that class
Scenario: store a file
When I store the file 'fixtures/bork.txt'
Then there should be a file at 'public/monkey/llama/bork.txt'
And the file at 'public/monkey/llama/bork.txt' should be identical to the file at 'fixtures/bork.txt'
Scenario: store two files in succession
When I store the file 'fixtures/bork.txt'
Then there should be a file at 'public/monkey/llama/bork.txt'
And the file at 'public/monkey/llama/bork.txt' should be identical to the file at 'fixtures/bork.txt'
When I store the file 'fixtures/monkey.txt'
Then there should be a file at 'public/monkey/llama/monkey.txt'
And the file at 'public/monkey/llama/monkey.txt' should be identical to the file at 'fixtures/monkey.txt'
Scenario: cache a file and then store it
When I cache the file 'fixtures/bork.txt'
Then there should be a file called 'bork.txt' somewhere in a subdirectory of 'public/uploads/tmp'
And the file called 'bork.txt' in a subdirectory of 'public/uploads/tmp' should be identical to the file at 'fixtures/bork.txt'
And there should not be a file at 'public/monkey/llama/bork.txt'
When I store the file
Then there should be a file at 'public/monkey/llama/bork.txt'
And the file at 'public/monkey/llama/bork.txt' should be identical to the file at 'fixtures/bork.txt'
Scenario: retrieving a file from cache then storing
Given the file 'fixtures/bork.txt' is cached file at 'public/uploads/tmp/1369894322-345-1234-2255/bork.txt'
When I retrieve the cache name '1369894322-345-1234-2255/bork.txt' from the cache
And I store the file
Then there should be a file at 'public/monkey/llama/bork.txt'
And the file at 'public/monkey/llama/bork.txt' should be identical to the file at 'fixtures/bork.txt'
carrierwave-3.0.7/features/file_storage_reversing_processor.feature 0000664 0000000 0000000 00000005003 14577507162 0026070 0 ustar 00root root 0000000 0000000 Feature: uploader with file storage and a processor that reverses the file
In order to be awesome
As a developer using CarrierWave
I want to upload files to the filesystem
Background:
Given an uploader class that uses the 'file' storage
And an instance of that class
And the class has a method called 'reverse' that reverses the contents of a file
And the class will process 'reverse'
Scenario: store a file
When I store the file 'fixtures/bork.txt'
Then there should be a file at 'public/uploads/bork.txt'
And the file at 'public/uploads/bork.txt' should not be identical to the file at 'fixtures/bork.txt'
And the file at 'public/uploads/bork.txt' should be the reverse of the file at 'fixtures/bork.txt'
Scenario: store two files in succession
When I store the file 'fixtures/bork.txt'
Then there should be a file at 'public/uploads/bork.txt'
And the file at 'public/uploads/bork.txt' should not be identical to the file at 'fixtures/bork.txt'
And the file at 'public/uploads/bork.txt' should be the reverse of the file at 'fixtures/bork.txt'
When I store the file 'fixtures/monkey.txt'
Then there should be a file at 'public/uploads/monkey.txt'
And the file at 'public/uploads/monkey.txt' should not be identical to the file at 'fixtures/monkey.txt'
And the file at 'public/uploads/monkey.txt' should be the reverse of the file at 'fixtures/monkey.txt'
Scenario: cache a file and then store it
When I cache the file 'fixtures/bork.txt'
Then there should be a file called 'bork.txt' somewhere in a subdirectory of 'public/uploads/tmp'
And the file called 'bork.txt' in a subdirectory of 'public/uploads/tmp' should not be identical to the file at 'fixtures/bork.txt'
And there should not be a file at 'public/uploads/bork.txt'
When I store the file
Then there should be a file at 'public/uploads/bork.txt'
And the file at 'public/uploads/bork.txt' should not be identical to the file at 'fixtures/bork.txt'
And the file at 'public/uploads/bork.txt' should be the reverse of the file at 'fixtures/bork.txt'
Scenario: retrieving a file from cache then storing
Given the file 'fixtures/bork.txt' is cached file at 'public/uploads/tmp/1369894322-345-1234-2255/bork.txt'
When I retrieve the cache name '1369894322-345-1234-2255/bork.txt' from the cache
And I store the file
Then there should be a file at 'public/uploads/bork.txt'
And the file at 'public/uploads/bork.txt' should be identical to the file at 'fixtures/bork.txt'
carrierwave-3.0.7/features/fixtures/ 0000775 0000000 0000000 00000000000 14577507162 0017540 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/features/fixtures/bork.txt 0000664 0000000 0000000 00000000016 14577507162 0021233 0 ustar 00root root 0000000 0000000 this is a file carrierwave-3.0.7/features/fixtures/monkey.txt 0000664 0000000 0000000 00000000024 14577507162 0021577 0 ustar 00root root 0000000 0000000 this is another file carrierwave-3.0.7/features/fixtures/upcased_bork.txt 0000664 0000000 0000000 00000000016 14577507162 0022737 0 ustar 00root root 0000000 0000000 THIS IS A FILE carrierwave-3.0.7/features/mount_activerecord.feature 0000664 0000000 0000000 00000004530 14577507162 0023142 0 ustar 00root root 0000000 0000000 Feature: Mount an Uploader on ActiveRecord class
In order to easily attach files to a form
As a web developer using CarrierWave
I want to mount an uploader on an ActiveRecord class
Background:
Given an uploader class that uses the 'file' storage
And an activerecord class that uses the 'users' table
And the uploader class is mounted on the 'avatar' column
And an instance of the activerecord class
Scenario: assign a file
When I assign the file 'fixtures/bork.txt' to the 'avatar' column
Then there should be a file called 'bork.txt' somewhere in a subdirectory of 'public/uploads/tmp'
And the file called 'bork.txt' in a subdirectory of 'public/uploads/tmp' should be identical to the file at 'fixtures/bork.txt'
Scenario: assign a file and save the record
When I assign the file 'fixtures/bork.txt' to the 'avatar' column
And I save the active record
Then there should be a file at 'public/uploads/bork.txt'
And the file at 'public/uploads/bork.txt' should be identical to the file at 'fixtures/bork.txt'
And the url for the column 'avatar' should be '/uploads/bork.txt'
Scenario: assign a file and retrieve it from cache
When I assign the file 'fixtures/bork.txt' to the 'avatar' column
And I retrieve the file later from the cache name for the column 'avatar'
And I save the active record
Then there should be a file at 'public/uploads/bork.txt'
And the file at 'public/uploads/bork.txt' should be identical to the file at 'fixtures/bork.txt'
And the url for the column 'avatar' should be '/uploads/bork.txt'
Scenario: store a file and retrieve it later
When I assign the file 'fixtures/bork.txt' to the 'avatar' column
And I retrieve the file later from the cache name for the column 'avatar'
And I save the active record
Then there should be a file at 'public/uploads/bork.txt'
When I reload the active record
Then the url for the column 'avatar' should be '/uploads/bork.txt'
Scenario: store a file and delete the record
When I assign the file 'fixtures/bork.txt' to the 'avatar' column
And I retrieve the file later from the cache name for the column 'avatar'
And I save the active record
Then there should be a file at 'public/uploads/bork.txt'
When I delete the active record
Then there should not be a file at 'public/uploads/bork.txt'
carrierwave-3.0.7/features/step_definitions/ 0000775 0000000 0000000 00000000000 14577507162 0021235 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/features/step_definitions/activerecord_steps.rb 0000664 0000000 0000000 00000000723 14577507162 0025454 0 ustar 00root root 0000000 0000000 Given /^an activerecord class that uses the '([^\']*)' table$/ do |name|
@mountee_klass = Class.new(ActiveRecord::Base)
@mountee_klass.table_name = name
end
Given /^an instance of the activerecord class$/ do
@instance = @mountee_klass.new
end
When /^I save the active record$/ do
@instance.save!
end
When /^I reload the active record$/ do
@instance = @instance.class.find(@instance.id)
end
When /^I delete the active record$/ do
@instance.destroy
end
carrierwave-3.0.7/features/step_definitions/caching_steps.rb 0000664 0000000 0000000 00000000603 14577507162 0024373 0 ustar 00root root 0000000 0000000 Given /^the file '(.*?)' is cached file at '(.*?)'$/ do |file, cached|
FileUtils.mkdir_p(File.dirname(file_path(cached)))
FileUtils.cp(file_path(file), file_path(cached))
end
When /^I cache the file '(.*?)'$/ do |file|
@uploader.cache!(File.open(file_path(file)))
end
When /^I retrieve the cache name '(.*?)' from the cache$/ do |name|
@uploader.retrieve_from_cache!(name)
end
carrierwave-3.0.7/features/step_definitions/datamapper_steps.rb 0000664 0000000 0000000 00000001153 14577507162 0025116 0 ustar 00root root 0000000 0000000 Given /^a datamapper class that has a '([^\']*)' column$/ do |column|
@mountee_klass = Class.new do
include DataMapper::Resource
storage_names[:default] = 'users'
property :id, DataMapper::Types::Serial
property column.to_sym, String
end
@mountee_klass.auto_migrate!
end
Given /^an instance of the datamapper class$/ do
@instance = @mountee_klass.new
end
When /^I save the datamapper record$/ do
@instance.save
end
When /^I reload the datamapper record$/ do
@instance = @instance.class.first(:id => @instance.key)
end
When /^I delete the datamapper record$/ do
@instance.destroy
end
carrierwave-3.0.7/features/step_definitions/download_steps.rb 0000664 0000000 0000000 00000000375 14577507162 0024614 0 ustar 00root root 0000000 0000000 When /^I download the file '([^']+)'/ do |url|
unless ENV['REMOTE'] == 'true'
stub_request(:get, %r{/Monkey/testfile.txt}).
to_return(body: "S3 Remote File", headers: { "Content-Type" => "text/plain" })
end
@uploader.download!(url)
end
carrierwave-3.0.7/features/step_definitions/file_steps.rb 0000664 0000000 0000000 00000003430 14577507162 0023717 0 ustar 00root root 0000000 0000000 ###
# EXISTENCE
Then /^there should be a file at '(.*?)'$/ do |file|
File.exist?(file_path(file)).should be_truthy
end
Then /^there should not be a file at '(.*?)'$/ do |file|
File.exist?(file_path(file)).should be_falsey
end
Then /^there should be a file called '(.*?)' somewhere in a subdirectory of '(.*?)'$/ do |file, directory|
Dir.glob(File.join(file_path(directory), '**', file)).any?.should be_truthy
end
###
# IDENTICAL
Then /^the file at '(.*?)' should be identical to the file at '(.*?)'$/ do |one, two|
File.read(file_path(one)).should == File.read(file_path(two))
end
Then /^the file at '(.*?)' should not be identical to the file at '(.*?)'$/ do |one, two|
File.read(file_path(one)).should_not == File.read(file_path(two))
end
Then /^the file called '(.*?)' in a subdirectory of '(.*?)' should be identical to the file at '(.*?)'$/ do |file, directory, other|
File.read(Dir.glob(File.join(file_path(directory), '**', file)).first).should == File.read(file_path(other))
end
Then /^the file called '(.*?)' in a subdirectory of '(.*?)' should not be identical to the file at '(.*?)'$/ do |file, directory, other|
File.read(Dir.glob(File.join(file_path(directory), '**', file)).first).should_not == File.read(file_path(other))
end
###
# CONTENT
Then /^the file called '([^']+)' in a subdirectory of '([^']+)' should contain '([^']+)'$/ do |file, directory, content|
File.read(Dir.glob(File.join(file_path(directory), '**', file)).first).should include(content)
end
Then /^the file at '([^']+)' should contain '([^']+)'$/ do |path, content|
File.read(file_path(path)).should include(content)
end
###
# REVERSING
Then /^the file at '(.*?)' should be the reverse of the file at '(.*?)'$/ do |one, two|
File.read(file_path(one)).should == File.read(file_path(two)).reverse
end
carrierwave-3.0.7/features/step_definitions/general_steps.rb 0000664 0000000 0000000 00000005416 14577507162 0024423 0 ustar 00root root 0000000 0000000 Given /^an uploader class that uses the '(.*?)' storage$/ do |kind|
@klass = Class.new(CarrierWave::Uploader::Base)
@klass.storage = kind.to_sym
end
Given /^an instance of that class$/ do
@uploader = @klass.new
end
Given /^a processor method named :upcase$/ do
@klass.class_eval do
define_method(:upcase) do
content = File.read(current_path)
File.open(current_path, 'w') { |f| f.write content.upcase }
end
end
end
Then /^the contents of the file should be '(.*?)'$/ do |contents|
@uploader.read.chomp.should == contents
end
Given /^that the uploader reverses the filename$/ do
@klass.class_eval do
def filename
super.reverse unless super.blank?
end
end
end
Given /^that the uploader has the filename overridden to '(.*?)'$/ do |filename|
@klass.class_eval do
define_method(:filename) do
filename
end
end
end
Given /^that the uploader has the store_dir overridden to '(.*?)'$/ do |store_dir|
@klass.class_eval do
define_method(:store_dir) do
file_path(store_dir)
end
end
end
Given /^that the version '(.*?)' has the store_dir overridden to '(.*?)'$/ do |version, store_dir|
@klass.version(version.to_sym) do
define_method(:store_dir) do
file_path(store_dir)
end
end
end
Given /^that the uploader class has a version named '([^\']+)'$/ do |name|
@klass.version(name)
end
Given /^that the uploader class has a version named '([^\']+)' which process '([a-zA-Z0-9\_\?!]*)'$/ do |name, processor_name|
@klass.version(name) do
process processor_name.to_sym
end
end
Given /^that the uploader class has a version named '([^\']+)' which is based on version '(.*?)'$/ do |name, based_version_name|
@klass.version(name, {:from_version => based_version_name.to_sym})
end
Given /^yo dawg, I put a version called '(.*?)' in your version called '(.*?)'$/ do |v2, v1|
@klass.version(v1) do
version(v2)
end
end
Given /^the class has a method called 'reverse' that reverses the contents of a file$/ do
@klass.class_eval do
def reverse
text = File.read(current_path)
File.open(current_path, 'w') { |f| f.write(text.reverse) }
end
end
end
Given /^the class will process '([a-zA-Z0-9\_\?!]*)'$/ do |name|
@klass.process name.to_sym
end
Then /^the uploader should have '(.*?)' as its current path$/ do |path|
@uploader.current_path.should == file_path(path)
end
Then /^the uploader should have the url '(.*?)'$/ do |url|
@uploader.url.should == url
end
Then /^the uploader's version '(.*?)' should have the url '(.*?)'$/ do |version, url|
@uploader.versions[version.to_sym].url.should == url
end
Then /^the uploader's nested version '(.*?)' nested in '(.*?)' should have the url '(.*?)'$/ do |v2, v1, url|
@uploader.versions[v1.to_sym].versions[v2.to_sym].url.should == url
end
carrierwave-3.0.7/features/step_definitions/mount_steps.rb 0000664 0000000 0000000 00000001213 14577507162 0024137 0 ustar 00root root 0000000 0000000 When /^I assign the file '([^\']*)' to the '([^\']*)' column$/ do |path, column|
@instance.send("#{column}=", File.open(file_path(path)))
end
Given /^the uploader class is mounted on the '([^\']*)' column$/ do |column|
@mountee_klass.mount_uploader column.to_sym, @klass
end
When /^I retrieve the file later from the cache name for the column '([^\']*)'$/ do |column|
new_instance = @instance.class.new
new_instance.send("#{column}_cache=", @instance.send("#{column}_cache"))
@instance = new_instance
end
Then /^the url for the column '([^\']*)' should be '([^\']*)'$/ do |column, url|
@instance.send("#{column}_url").should == url
end
carrierwave-3.0.7/features/step_definitions/store_steps.rb 0000664 0000000 0000000 00000000671 14577507162 0024140 0 ustar 00root root 0000000 0000000 Given /^the file '(.*?)' is stored at '(.*?)'$/ do |file, stored|
FileUtils.mkdir_p(File.dirname(file_path(stored)))
FileUtils.cp(file_path(file), file_path(stored))
end
When /^I store the file$/ do
@uploader.store!
end
When /^I store the file '(.*?)'$/ do |file|
@uploader.store!(File.open(file_path(file)))
end
When /^I retrieve the file '(.*?)' from the store$/ do |identifier|
@uploader.retrieve_from_store!(identifier)
end
carrierwave-3.0.7/features/support/ 0000775 0000000 0000000 00000000000 14577507162 0017403 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/features/support/activerecord.rb 0000664 0000000 0000000 00000000646 14577507162 0022410 0 ustar 00root root 0000000 0000000 require 'carrierwave/mount'
require File.join(File.dirname(__FILE__), '..', '..', 'spec', 'support', 'activerecord')
class TestMigration < ActiveRecord.version.to_s >= '5.0' ? ActiveRecord::Migration[5.0] : ActiveRecord::Migration
def self.up
create_table :users, :force => true do |t|
t.column :avatar, :string
end
end
def self.down
drop_table :users
end
end
Before do
TestMigration.up
end
carrierwave-3.0.7/features/support/env.rb 0000664 0000000 0000000 00000000615 14577507162 0020522 0 ustar 00root root 0000000 0000000 $:.unshift File.expand_path(File.join('..', '..', 'lib'), File.dirname(__FILE__))
require File.join(File.dirname(__FILE__), 'activerecord')
require 'rspec'
require 'carrierwave'
require "webmock/cucumber"
def file_path( *paths )
File.expand_path(File.join('..', *paths), File.dirname(__FILE__))
end
CarrierWave.root = file_path('public')
After do
FileUtils.rm_rf(file_path("public"))
end
carrierwave-3.0.7/features/versions_basics.feature 0000664 0000000 0000000 00000006021 14577507162 0022437 0 ustar 00root root 0000000 0000000 Feature: uploader with file storage and versions
In order to be awesome
As a developer using CarrierWave
I want to upload files to the filesystem
Background:
Given an uploader class that uses the 'file' storage
And that the uploader class has a version named 'thumb'
And an instance of that class
Scenario: store a file
When I store the file 'fixtures/bork.txt'
Then there should be a file at 'public/uploads/bork.txt'
Then there should be a file at 'public/uploads/thumb_bork.txt'
And the file at 'public/uploads/bork.txt' should be identical to the file at 'fixtures/bork.txt'
And the file at 'public/uploads/thumb_bork.txt' should be identical to the file at 'fixtures/bork.txt'
And the uploader should have the url '/uploads/bork.txt'
And the uploader's version 'thumb' should have the url '/uploads/thumb_bork.txt'
Scenario: cache a file and then store it
When I cache the file 'fixtures/bork.txt'
Then there should be a file called 'bork.txt' somewhere in a subdirectory of 'public/uploads/tmp'
Then there should be a file called 'thumb_bork.txt' somewhere in a subdirectory of 'public/uploads/tmp'
And the file called 'bork.txt' in a subdirectory of 'public/uploads/tmp' should be identical to the file at 'fixtures/bork.txt'
And there should not be a file at 'public/uploads/bork.txt'
And there should not be a file at 'public/uploads/thumb_bork.txt'
When I store the file
Then there should be a file at 'public/uploads/bork.txt'
And there should be a file at 'public/uploads/thumb_bork.txt'
And the file at 'public/uploads/bork.txt' should be identical to the file at 'fixtures/bork.txt'
And the file at 'public/uploads/thumb_bork.txt' should be identical to the file at 'fixtures/bork.txt'
And the uploader should have the url '/uploads/bork.txt'
And the uploader's version 'thumb' should have the url '/uploads/thumb_bork.txt'
Scenario: retrieving a file from cache then storing
Given the file 'fixtures/bork.txt' is cached file at 'public/uploads/tmp/1369894322-345-1234-2255/bork.txt'
Given the file 'fixtures/monkey.txt' is cached file at 'public/uploads/tmp/1369894322-345-1234-2255/thumb_bork.txt'
When I retrieve the cache name '1369894322-345-1234-2255/bork.txt' from the cache
And I store the file
Then there should be a file at 'public/uploads/bork.txt'
Then there should be a file at 'public/uploads/thumb_bork.txt'
And the file at 'public/uploads/bork.txt' should be identical to the file at 'fixtures/bork.txt'
And the file at 'public/uploads/thumb_bork.txt' should be identical to the file at 'fixtures/monkey.txt'
Scenario: retrieving a file from store
Given the file 'fixtures/bork.txt' is stored at 'public/uploads/bork.txt'
Given the file 'fixtures/monkey.txt' is stored at 'public/uploads/thumb_bork.txt'
When I retrieve the file 'bork.txt' from the store
Then the uploader should have the url '/uploads/bork.txt'
And the uploader's version 'thumb' should have the url '/uploads/thumb_bork.txt'
carrierwave-3.0.7/features/versions_caching_from_versions.feature 0000664 0000000 0000000 00000004435 14577507162 0025551 0 ustar 00root root 0000000 0000000 Feature: uploader with file storage and versions with overridden store dir
In order to be awesome
As a developer using CarrierWave
I want to upload files to the filesystem
Background:
Given an uploader class that uses the 'file' storage
Given a processor method named :upcase
And that the uploader class has a version named 'thumb' which process 'upcase'
And that the version 'thumb' has the store_dir overridden to 'public/monkey/llama'
And that the uploader class has a version named 'small_thumb' which is based on version 'thumb'
And that the version 'small_thumb' has the store_dir overridden to 'public/monkey/toro'
And an instance of that class
Scenario: cache a file and then store it
When I cache the file 'fixtures/bork.txt'
Then there should be a file called 'bork.txt' somewhere in a subdirectory of 'public/uploads/tmp'
Then there should be a file called 'thumb_bork.txt' somewhere in a subdirectory of 'public/uploads/tmp'
Then there should be a file called 'small_thumb_bork.txt' somewhere in a subdirectory of 'public/uploads/tmp'
And the file called 'bork.txt' in a subdirectory of 'public/uploads/tmp' should be identical to the file at 'fixtures/bork.txt'
And the file called 'thumb_bork.txt' in a subdirectory of 'public/uploads/tmp' should be identical to the file at 'fixtures/upcased_bork.txt'
And the file called 'small_thumb_bork.txt' in a subdirectory of 'public/uploads/tmp' should be identical to the file at 'fixtures/upcased_bork.txt'
And there should not be a file at 'public/uploads/bork.txt'
And there should not be a file at 'public/monkey/llama/thumb_bork.txt'
And there should not be a file at 'public/monkey/toro/small_thumb_bork.txt'
When I store the file
Then there should be a file at 'public/uploads/bork.txt'
Then there should be a file at 'public/monkey/llama/thumb_bork.txt'
Then there should be a file at 'public/monkey/toro/small_thumb_bork.txt'
And the file at 'public/uploads/bork.txt' should be identical to the file at 'fixtures/bork.txt'
And the file at 'public/monkey/llama/thumb_bork.txt' should be identical to the file at 'fixtures/upcased_bork.txt'
And the file at 'public/monkey/toro/small_thumb_bork.txt' should be identical to the file at 'fixtures/upcased_bork.txt' carrierwave-3.0.7/features/versions_nested_versions.feature 0000664 0000000 0000000 00000011772 14577507162 0024416 0 ustar 00root root 0000000 0000000 Feature: uploader with nested versions
In order to optimize performance for processing
As a developer using CarrierWave
I want to set nested versions
Background:
Given an uploader class that uses the 'file' storage
And that the uploader class has a version named 'thumb'
And yo dawg, I put a version called 'mini' in your version called 'thumb'
And yo dawg, I put a version called 'micro' in your version called 'thumb'
And an instance of that class
Scenario: store a file
When I store the file 'fixtures/bork.txt'
Then there should be a file at 'public/uploads/bork.txt'
Then there should be a file at 'public/uploads/thumb_bork.txt'
Then there should be a file at 'public/uploads/thumb_mini_bork.txt'
Then there should be a file at 'public/uploads/thumb_micro_bork.txt'
And the file at 'public/uploads/bork.txt' should be identical to the file at 'fixtures/bork.txt'
And the file at 'public/uploads/thumb_bork.txt' should be identical to the file at 'fixtures/bork.txt'
And the file at 'public/uploads/thumb_mini_bork.txt' should be identical to the file at 'fixtures/bork.txt'
And the file at 'public/uploads/thumb_micro_bork.txt' should be identical to the file at 'fixtures/bork.txt'
And the uploader should have the url '/uploads/bork.txt'
And the uploader's version 'thumb' should have the url '/uploads/thumb_bork.txt'
And the uploader's nested version 'mini' nested in 'thumb' should have the url '/uploads/thumb_mini_bork.txt'
And the uploader's nested version 'micro' nested in 'thumb' should have the url '/uploads/thumb_micro_bork.txt'
Scenario: cache a file and then store it
When I cache the file 'fixtures/bork.txt'
Then there should be a file called 'bork.txt' somewhere in a subdirectory of 'public/uploads/tmp'
Then there should be a file called 'thumb_bork.txt' somewhere in a subdirectory of 'public/uploads/tmp'
And the file called 'bork.txt' in a subdirectory of 'public/uploads/tmp' should be identical to the file at 'fixtures/bork.txt'
And there should not be a file at 'public/uploads/bork.txt'
And there should not be a file at 'public/uploads/thumb_bork.txt'
When I store the file
Then there should be a file at 'public/uploads/bork.txt'
And there should be a file at 'public/uploads/thumb_bork.txt'
And the file at 'public/uploads/bork.txt' should be identical to the file at 'fixtures/bork.txt'
And the file at 'public/uploads/thumb_bork.txt' should be identical to the file at 'fixtures/bork.txt'
And the uploader should have the url '/uploads/bork.txt'
And the uploader's version 'thumb' should have the url '/uploads/thumb_bork.txt'
And the uploader's nested version 'mini' nested in 'thumb' should have the url '/uploads/thumb_mini_bork.txt'
And the uploader's nested version 'micro' nested in 'thumb' should have the url '/uploads/thumb_micro_bork.txt'
Scenario: retrieving a file from cache then storing
Given the file 'fixtures/bork.txt' is cached file at 'public/uploads/tmp/1369894322-345-1234-2255/bork.txt'
Given the file 'fixtures/monkey.txt' is cached file at 'public/uploads/tmp/1369894322-345-1234-2255/thumb_bork.txt'
Given the file 'fixtures/bork.txt' is cached file at 'public/uploads/tmp/1369894322-345-1234-2255/thumb_mini_bork.txt'
Given the file 'fixtures/monkey.txt' is cached file at 'public/uploads/tmp/1369894322-345-1234-2255/thumb_micro_bork.txt'
When I retrieve the cache name '1369894322-345-1234-2255/bork.txt' from the cache
And I store the file
Then there should be a file at 'public/uploads/bork.txt'
Then there should be a file at 'public/uploads/thumb_bork.txt'
Then there should be a file at 'public/uploads/thumb_mini_bork.txt'
Then there should be a file at 'public/uploads/thumb_micro_bork.txt'
And the file at 'public/uploads/bork.txt' should be identical to the file at 'fixtures/bork.txt'
And the file at 'public/uploads/thumb_bork.txt' should be identical to the file at 'fixtures/monkey.txt'
And the file at 'public/uploads/thumb_mini_bork.txt' should be identical to the file at 'fixtures/bork.txt'
And the file at 'public/uploads/thumb_micro_bork.txt' should be identical to the file at 'fixtures/monkey.txt'
Scenario: retrieving a file from store
Given the file 'fixtures/bork.txt' is stored at 'public/uploads/bork.txt'
Given the file 'fixtures/monkey.txt' is stored at 'public/uploads/thumb_bork.txt'
Given the file 'fixtures/monkey.txt' is stored at 'public/uploads/thumb_mini_bork.txt'
Given the file 'fixtures/monkey.txt' is stored at 'public/uploads/thumb_micro_bork.txt'
When I retrieve the file 'bork.txt' from the store
Then the uploader should have the url '/uploads/bork.txt'
And the uploader's version 'thumb' should have the url '/uploads/thumb_bork.txt'
And the uploader's nested version 'mini' nested in 'thumb' should have the url '/uploads/thumb_mini_bork.txt'
And the uploader's nested version 'micro' nested in 'thumb' should have the url '/uploads/thumb_micro_bork.txt'
carrierwave-3.0.7/features/versions_overridden_filename.feature 0000664 0000000 0000000 00000006416 14577507162 0025204 0 ustar 00root root 0000000 0000000 Feature: uploader with file storage and overridden filename
In order to customize the filename of uploaded files
As a developer using CarrierWave
I want to upload files to the filesystem with an overridden filename and different versions
Background:
Given an uploader class that uses the 'file' storage
And that the uploader class has a version named 'thumb'
And that the uploader has the filename overridden to 'grark.png'
And an instance of that class
Scenario: store a file
When I store the file 'fixtures/bork.txt'
Then there should be a file at 'public/uploads/grark.png'
Then there should be a file at 'public/uploads/thumb_grark.png'
And the file at 'public/uploads/grark.png' should be identical to the file at 'fixtures/bork.txt'
And the file at 'public/uploads/thumb_grark.png' should be identical to the file at 'fixtures/bork.txt'
And the uploader should have the url '/uploads/grark.png'
And the uploader's version 'thumb' should have the url '/uploads/thumb_grark.png'
Scenario: cache a file and then store it
When I cache the file 'fixtures/bork.txt'
Then there should be a file called 'bork.txt' somewhere in a subdirectory of 'public/uploads/tmp'
Then there should be a file called 'thumb_bork.txt' somewhere in a subdirectory of 'public/uploads/tmp'
And the file called 'bork.txt' in a subdirectory of 'public/uploads/tmp' should be identical to the file at 'fixtures/bork.txt'
And there should not be a file at 'public/uploads/grark.png'
And there should not be a file at 'public/uploads/thumb_grark.png'
When I store the file
Then there should be a file at 'public/uploads/grark.png'
And there should be a file at 'public/uploads/thumb_grark.png'
And the file at 'public/uploads/grark.png' should be identical to the file at 'fixtures/bork.txt'
And the file at 'public/uploads/thumb_grark.png' should be identical to the file at 'fixtures/bork.txt'
And the uploader should have the url '/uploads/grark.png'
And the uploader's version 'thumb' should have the url '/uploads/thumb_grark.png'
Scenario: retrieving a file from cache then storing
Given the file 'fixtures/bork.txt' is cached file at 'public/uploads/tmp/1369894322-345-1234-2255/bork.txt'
Given the file 'fixtures/monkey.txt' is cached file at 'public/uploads/tmp/1369894322-345-1234-2255/thumb_bork.txt'
When I retrieve the cache name '1369894322-345-1234-2255/bork.txt' from the cache
And I store the file
Then there should be a file at 'public/uploads/grark.png'
Then there should be a file at 'public/uploads/thumb_grark.png'
And the file at 'public/uploads/grark.png' should be identical to the file at 'fixtures/bork.txt'
And the file at 'public/uploads/thumb_grark.png' should be identical to the file at 'fixtures/monkey.txt'
Scenario: retrieving a file from store
Given the file 'fixtures/bork.txt' is stored at 'public/uploads/bork.txt'
Given the file 'fixtures/monkey.txt' is stored at 'public/uploads/thumb_bork.txt'
When I retrieve the file 'bork.txt' from the store
Then the uploader should have the url '/uploads/bork.txt'
And the uploader's version 'thumb' should have the url '/uploads/thumb_bork.txt'
carrierwave-3.0.7/features/versions_overriden_store_dir.feature 0000664 0000000 0000000 00000005164 14577507162 0025251 0 ustar 00root root 0000000 0000000 Feature: uploader with file storage and versions with overridden store dir
In order to be awesome
As a developer using CarrierWave
I want to upload files to the filesystem
Background:
Given an uploader class that uses the 'file' storage
And that the uploader class has a version named 'thumb'
And that the version 'thumb' has the store_dir overridden to 'public/monkey/llama'
And an instance of that class
Scenario: store a file
When I store the file 'fixtures/bork.txt'
Then there should be a file at 'public/uploads/bork.txt'
Then there should be a file at 'public/monkey/llama/thumb_bork.txt'
And the file at 'public/uploads/bork.txt' should be identical to the file at 'fixtures/bork.txt'
And the file at 'public/monkey/llama/thumb_bork.txt' should be identical to the file at 'fixtures/bork.txt'
Scenario: cache a file and then store it
When I cache the file 'fixtures/bork.txt'
Then there should be a file called 'bork.txt' somewhere in a subdirectory of 'public/uploads/tmp'
Then there should be a file called 'thumb_bork.txt' somewhere in a subdirectory of 'public/uploads/tmp'
And the file called 'bork.txt' in a subdirectory of 'public/uploads/tmp' should be identical to the file at 'fixtures/bork.txt'
And the file called 'thumb_bork.txt' in a subdirectory of 'public/uploads/tmp' should be identical to the file at 'fixtures/bork.txt'
And there should not be a file at 'public/uploads/bork.txt'
And there should not be a file at 'public/monkey/llama/thumb_bork.txt'
When I store the file
Then there should be a file at 'public/uploads/bork.txt'
Then there should be a file at 'public/monkey/llama/thumb_bork.txt'
And the file at 'public/uploads/bork.txt' should be identical to the file at 'fixtures/bork.txt'
And the file at 'public/monkey/llama/thumb_bork.txt' should be identical to the file at 'fixtures/bork.txt'
Scenario: retrieving a file from cache then storing
Given the file 'fixtures/bork.txt' is cached file at 'public/uploads/tmp/1369894322-345-1234-2255/bork.txt'
Given the file 'fixtures/monkey.txt' is cached file at 'public/uploads/tmp/1369894322-345-1234-2255/thumb_bork.txt'
When I retrieve the cache name '1369894322-345-1234-2255/bork.txt' from the cache
And I store the file
Then there should be a file at 'public/uploads/bork.txt'
Then there should be a file at 'public/monkey/llama/thumb_bork.txt'
And the file at 'public/uploads/bork.txt' should be identical to the file at 'fixtures/bork.txt'
And the file at 'public/monkey/llama/thumb_bork.txt' should be identical to the file at 'fixtures/monkey.txt'
carrierwave-3.0.7/gemfiles/ 0000775 0000000 0000000 00000000000 14577507162 0015644 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/gemfiles/rails-6-0.gemfile 0000664 0000000 0000000 00000000420 14577507162 0020604 0 ustar 00root root 0000000 0000000 source "https://rubygems.org"
gem "rails", "~> 6.0.0"
gem "activemodel-serializers-xml"
gem 'sqlite3', platforms: :ruby
gem "activerecord-jdbcsqlite3-adapter", platform: [:jruby, :truffleruby]
gem "fog-google", "~> 1.13.0" if RUBY_VERSION < '2.7'
gemspec :path => "../"
carrierwave-3.0.7/gemfiles/rails-6-1.gemfile 0000664 0000000 0000000 00000000420 14577507162 0020605 0 ustar 00root root 0000000 0000000 source "https://rubygems.org"
gem "rails", "~> 6.1.0"
gem "activemodel-serializers-xml"
gem 'sqlite3', platforms: :ruby
gem "activerecord-jdbcsqlite3-adapter", platform: [:jruby, :truffleruby]
gem "fog-google", "~> 1.13.0" if RUBY_VERSION < '2.7'
gemspec :path => "../"
carrierwave-3.0.7/gemfiles/rails-7-0.gemfile 0000664 0000000 0000000 00000000332 14577507162 0020607 0 ustar 00root root 0000000 0000000 source "https://rubygems.org"
gem "rails", "~> 7.0.0"
gem "activemodel-serializers-xml"
gem 'sqlite3', platforms: :ruby
gem "activerecord-jdbcsqlite3-adapter", platform: [:jruby, :truffleruby]
gemspec :path => "../"
carrierwave-3.0.7/gemfiles/rails-7-1.gemfile 0000664 0000000 0000000 00000000447 14577507162 0020617 0 ustar 00root root 0000000 0000000 source "https://rubygems.org"
gem "rails", "~> 7.1.0"
gem "activemodel-serializers-xml"
gem 'sqlite3', platforms: :ruby
gem "activerecord-jdbcsqlite3-adapter", platform: [:jruby, :truffleruby]
gem "observer" # Workaround for https://github.com/rmagick/rmagick/pull/1411
gemspec :path => "../"
carrierwave-3.0.7/gemfiles/rails-main.gemfile 0000664 0000000 0000000 00000000365 14577507162 0021236 0 ustar 00root root 0000000 0000000 source "https://rubygems.org"
gem "rails", github: "rails/rails", branch: "main"
gem "activemodel-serializers-xml"
gem 'sqlite3', platforms: :ruby
gem "activerecord-jdbcsqlite3-adapter", platform: [:jruby, :truffleruby]
gemspec :path => "../"
carrierwave-3.0.7/lib/ 0000775 0000000 0000000 00000000000 14577507162 0014617 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/lib/carrierwave.rb 0000664 0000000 0000000 00000005166 14577507162 0017466 0 ustar 00root root 0000000 0000000 require 'fileutils'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/object/try'
require 'active_support/core_ext/class/attribute'
require 'active_support/concern'
module CarrierWave
class << self
attr_accessor :root, :base_path
attr_writer :tmp_path
def configure(&block)
CarrierWave::Uploader::Base.configure(&block)
end
def clean_cached_files!(seconds=60*60*24)
CarrierWave::Uploader::Base.clean_cached_files!(seconds)
end
def tmp_path
@tmp_path ||= File.expand_path(File.join('..', 'tmp'), root)
end
end
end
if defined?(Jets)
module CarrierWave
class Turbine < Jets::Turbine
initializer "carrierwave.setup_paths" do |app|
CarrierWave.root = Jets.root.to_s
CarrierWave.tmp_path = "/tmp/carrierwave"
CarrierWave.configure do |config|
config.cache_dir = "/tmp/carrierwave/uploads/tmp"
end
end
initializer "carrierwave.active_record" do
ActiveSupport.on_load :active_record do
require 'carrierwave/orm/activerecord'
end
end
end
end
elsif defined?(Rails)
module CarrierWave
class Railtie < Rails::Railtie
initializer "carrierwave.setup_paths" do |app|
CarrierWave.root = Rails.root.join(Rails.public_path).to_s
CarrierWave.base_path = ENV['RAILS_RELATIVE_URL_ROOT']
available_locales = Array(app.config.i18n.available_locales || [])
if available_locales.blank? || available_locales.include?(:en)
I18n.load_path.prepend(File.join(File.dirname(__FILE__), 'carrierwave', 'locale', "en.yml"))
end
end
initializer "carrierwave.active_record" do
ActiveSupport.on_load :active_record do
require 'carrierwave/orm/activerecord'
end
end
config.before_eager_load do
CarrierWave::Storage::Fog.eager_load
end
end
end
elsif defined?(Sinatra)
if defined?(Padrino) && defined?(PADRINO_ROOT)
CarrierWave.root = File.join(PADRINO_ROOT, "public")
else
CarrierWave.root =
if Sinatra::Application.respond_to?(:public_folder)
# Sinatra >= 1.3
Sinatra::Application.public_folder
else
# Sinatra < 1.3
Sinatra::Application.public
end
end
end
require "carrierwave/utilities"
require "carrierwave/error"
require "carrierwave/sanitized_file"
require "carrierwave/mounter"
require "carrierwave/mount"
require "carrierwave/processing"
require "carrierwave/version"
require "carrierwave/storage"
require "carrierwave/uploader"
require "carrierwave/compatibility/paperclip"
require "carrierwave/test/matchers"
carrierwave-3.0.7/lib/carrierwave/ 0000775 0000000 0000000 00000000000 14577507162 0017131 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/lib/carrierwave/compatibility/ 0000775 0000000 0000000 00000000000 14577507162 0022002 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/lib/carrierwave/compatibility/paperclip.rb 0000664 0000000 0000000 00000007212 14577507162 0024310 0 ustar 00root root 0000000 0000000 module CarrierWave
module Compatibility
##
# Mix this module into an Uploader to make it mimic Paperclip's storage paths
# This will make your Uploader use the same default storage path as paperclip
# does. If you need to override it, you can override the +paperclip_path+ method
# and provide a Paperclip style path:
#
# class MyUploader < CarrierWave::Uploader::Base
# include CarrierWave::Compatibility::Paperclip
#
# def paperclip_path
# ":rails_root/public/uploads/:id/:attachment/:style_:basename.:extension"
# end
# end
#
# ---
#
# This file contains code taken from Paperclip
#
# LICENSE
#
# The MIT License
#
# Copyright (c) 2008 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.
#
module Paperclip
extend ActiveSupport::Concern
DEFAULT_MAPPINGS = {
:rails_root => lambda{|u, f| Rails.root.to_s },
:rails_env => lambda{|u, f| Rails.env },
:id_partition => lambda{|u, f| ("%09d" % u.model.id).scan(/\d{3}/).join("/")},
:id => lambda{|u, f| u.model.id },
:attachment => lambda{|u, f| u.mounted_as.to_s.downcase.pluralize },
:style => lambda{|u, f| u.paperclip_style },
:basename => lambda{|u, f| u.filename.gsub(/#{File.extname(u.filename)}$/, "") },
:extension => lambda{|u, d| File.extname(u.filename).gsub(/^\.+/, "")},
:class => lambda{|u, f| u.model.class.name.underscore.pluralize}
}.freeze
included do
attr_accessor :filename
class_attribute :mappings
self.mappings ||= DEFAULT_MAPPINGS.dup
end
def store_path(for_file=filename)
path = paperclip_path
self.filename = for_file
path ||= File.join(*[store_dir, paperclip_style.to_s, for_file].compact)
interpolate_paperclip_path(path)
end
def store_dir
":rails_root/public/system/:attachment/:id"
end
def paperclip_default_style
:original
end
def paperclip_path
end
def paperclip_style
version_name || paperclip_default_style
end
module ClassMethods
def interpolate(sym, &block)
mappings[sym] = block
end
end
private
def interpolate_paperclip_path(path)
mappings.each_pair.inject(path) do |agg, pair|
agg.gsub(":#{pair[0]}") { pair[1].call(self, self.paperclip_style).to_s }
end
end
end # Paperclip
end # Compatibility
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/downloader/ 0000775 0000000 0000000 00000000000 14577507162 0021267 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/lib/carrierwave/downloader/base.rb 0000664 0000000 0000000 00000006472 14577507162 0022537 0 ustar 00root root 0000000 0000000 require 'open-uri'
require 'ssrf_filter'
require 'addressable'
require 'carrierwave/downloader/remote_file'
module CarrierWave
module Downloader
class Base
include CarrierWave::Utilities::Uri
attr_reader :uploader
def initialize(uploader)
@uploader = uploader
end
##
# Downloads a file from given URL and returns a RemoteFile.
#
# === Parameters
#
# [url (String)] The URL where the remote file is stored
# [remote_headers (Hash)] Request headers
#
def download(url, remote_headers = {})
@current_download_retry_count = 0
headers = remote_headers.
reverse_merge('User-Agent' => "CarrierWave/#{CarrierWave::VERSION}")
uri = process_uri(url.to_s)
begin
if skip_ssrf_protection?(uri)
response = OpenURI.open_uri(process_uri(url.to_s), headers)
else
request = nil
if ::SsrfFilter::VERSION.to_f < 1.1
response = SsrfFilter.get(uri, headers: headers) do |req|
request = req
end
else
response = SsrfFilter.get(uri, headers: headers, request_proc: ->(req) { request = req }) do |res|
res.body # ensure to read body
end
end
response.uri = request.uri
response.value
end
rescue StandardError => e
if @current_download_retry_count < @uploader.download_retry_count
@current_download_retry_count += 1
sleep @uploader.download_retry_wait_time
retry
else
raise CarrierWave::DownloadError, "could not download file: #{e.message}"
end
end
CarrierWave::Downloader::RemoteFile.new(response)
end
##
# Processes the given URL by parsing it, and escaping if necessary. Public to allow overriding.
#
# === Parameters
#
# [url (String)] The URL where the remote file is stored
#
def process_uri(source)
uri = Addressable::URI.parse(source)
uri.host = uri.normalized_host
# Perform decode first, as the path is likely to be already encoded
uri.path = encode_path(decode_uri(uri.path)) if uri.path =~ CarrierWave::Utilities::Uri::PATH_UNSAFE
uri.query = encode_non_ascii(uri.query) if uri.query
uri.fragment = encode_non_ascii(uri.fragment) if uri.fragment
URI.parse(uri.to_s)
rescue URI::InvalidURIError, Addressable::URI::InvalidURIError
raise CarrierWave::DownloadError, "couldn't parse URL: #{source}"
end
##
# If this returns true, SSRF protection will be bypassed.
# You can override this if you want to allow accessing specific local URIs that are not SSRF exploitable.
#
# === Parameters
#
# [uri (URI)] The URI where the remote file is stored
#
# === Examples
#
# class CarrierWave::Downloader::CustomDownloader < CarrierWave::Downloader::Base
# def skip_ssrf_protection?(uri)
# uri.hostname == 'localhost' && uri.port == 80
# end
# end
#
# my_uploader.downloader = CarrierWave::Downloader::CustomDownloader
#
def skip_ssrf_protection?(uri)
false
end
end
end
end
carrierwave-3.0.7/lib/carrierwave/downloader/remote_file.rb 0000664 0000000 0000000 00000003227 14577507162 0024112 0 ustar 00root root 0000000 0000000 module CarrierWave
module Downloader
class RemoteFile
attr_reader :file, :uri
def initialize(file)
case file
when String
@file = StringIO.new(file)
when Net::HTTPResponse
body = file.body
raise CarrierWave::DownloadError, 'could not download file: No Content' if body.nil?
@file = StringIO.new(body)
@content_type = file.content_type
@headers = file
@uri = file.uri
else
@file = file
@content_type = file.content_type
@headers = file.meta
@uri = file.base_uri
end
end
def content_type
@content_type || 'application/octet-stream'
end
def headers
@headers || {}
end
def original_filename
filename = filename_from_header || filename_from_uri
extensions = Marcel::Magic.new(content_type).extensions
unless File.extname(filename).present? || extensions.blank?
extension = extensions.first
filename = "#{filename}.#{extension}"
end
filename
end
private
def filename_from_header
return nil unless headers['content-disposition']
match = headers['content-disposition'].match(/filename=(?:"([^"]+)"|([^";]+))/)
return nil unless match
match[1].presence || match[2].presence
end
def filename_from_uri
CGI.unescape(File.basename(uri.path))
end
def method_missing(*args, &block)
file.send(*args, &block)
end
def respond_to_missing?(*args)
super || file.respond_to?(*args)
end
end
end
end
carrierwave-3.0.7/lib/carrierwave/error.rb 0000664 0000000 0000000 00000000433 14577507162 0020607 0 ustar 00root root 0000000 0000000 module CarrierWave
class UploadError < StandardError; end
class IntegrityError < UploadError; end
class InvalidParameter < UploadError; end
class ProcessingError < UploadError; end
class DownloadError < UploadError; end
class UnknownStorageError < StandardError; end
end
carrierwave-3.0.7/lib/carrierwave/locale/ 0000775 0000000 0000000 00000000000 14577507162 0020370 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/lib/carrierwave/locale/en.yml 0000664 0000000 0000000 00000002223 14577507162 0021514 0 ustar 00root root 0000000 0000000 en:
errors:
messages:
carrierwave_processing_error: failed to be processed
carrierwave_integrity_error: is not of an allowed file type
carrierwave_download_error: could not be downloaded
extension_allowlist_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
extension_denylist_error: "You are not allowed to upload %{extension} files, prohibited types: %{prohibited_types}"
content_type_allowlist_error: "You are not allowed to upload %{content_type} files, allowed types: %{allowed_types}"
content_type_denylist_error: "You are not allowed to upload %{content_type} files"
processing_error: "Failed to manipulate, maybe it is not an image?"
min_size_error: "File size should be greater than %{min_size}"
max_size_error: "File size should be less than %{max_size}"
min_width_error: "Image width should be greater than %{min_width}px"
max_width_error: "Image width should be less than %{max_width}px"
min_height_error: "Image height should be greater than %{min_height}px"
max_height_error: "Image height should be less than %{max_height}px"
carrierwave-3.0.7/lib/carrierwave/mount.rb 0000664 0000000 0000000 00000035070 14577507162 0020625 0 ustar 00root root 0000000 0000000 module CarrierWave
##
# If a Class is extended with this module, it gains the mount_uploader
# method, which is used for mapping attributes to uploaders and allowing
# easy assignment.
#
# You can use mount_uploader with pretty much any class, however it is
# intended to be used with some kind of persistent storage, like an ORM.
# If you want to persist the uploaded files in a particular Class, it
# needs to implement a `read_uploader` and a `write_uploader` method.
#
module Mount
##
# === Returns
#
# [Hash{Symbol => CarrierWave}] what uploaders are mounted on which columns
#
def uploaders
@uploaders ||= superclass.respond_to?(:uploaders) ? superclass.uploaders.dup : {}
end
def uploader_options
@uploader_options ||= superclass.respond_to?(:uploader_options) ? superclass.uploader_options.dup : {}
end
##
# Return a particular option for a particular uploader
#
# === Parameters
#
# [column (Symbol)] The column the uploader is mounted at
# [option (Symbol)] The option, e.g. validate_integrity
#
# === Returns
#
# [Object] The option value
#
def uploader_option(column, option)
if uploader_options[column].has_key?(option)
uploader_options[column][option]
else
uploaders[column].send(option)
end
end
##
# Mounts the given uploader on the given column. This means that assigning
# and reading from the column will upload and retrieve files. Supposing
# that a User class has an uploader mounted on image, you can assign and
# retrieve files like this:
#
# @user.image # =>
# @user.image.store!(some_file_object)
#
# @user.image.url # => '/some_url.png'
#
# It is also possible (but not recommended) to omit the uploader, which
# will create an anonymous uploader class.
#
# Passing a block makes it possible to customize the uploader. This can be
# convenient for brevity, but if there is any significant logic in the
# uploader, you should do the right thing and have it in its own file.
#
# === Added instance methods
#
# Supposing a class has used +mount_uploader+ to mount an uploader on a column
# named +image+, in that case the following methods will be added to the class:
#
# [image] Returns an instance of the uploader only if anything has been uploaded
# [image=] Caches the given file
#
# [image_url] Returns the url to the uploaded file
#
# [image_cache] Returns a string that identifies the cache location of the file
# [image_cache=] Retrieves the file from the cache based on the given cache name
#
# [remote_image_url] Returns previously cached remote url
# [remote_image_url=] Retrieve the file from the remote url
#
# [remove_image] An attribute reader that can be used with a checkbox to mark a file for removal
# [remove_image=] An attribute writer that can be used with a checkbox to mark a file for removal
# [remove_image?] Whether the file should be removed when store_image! is called.
#
# [store_image!] Stores a file that has been assigned with +image=+
# [remove_image!] Removes the uploaded file from the filesystem.
#
# [image_integrity_error] Returns an error object if the last file to be assigned caused an integrity error
# [image_processing_error] Returns an error object if the last file to be assigned caused a processing error
# [image_download_error] Returns an error object if the last file to be remotely assigned caused a download error
#
# [image_identifier] Reads out the identifier of the file
#
# === Parameters
#
# [column (Symbol)] the attribute to mount this uploader on
# [uploader (CarrierWave::Uploader)] the uploader class to mount
# [options (Hash{Symbol => Object})] a set of options
# [&block (Proc)] customize anonymous uploaders
#
# === Options
#
# [:mount_on => Symbol] if the name of the column to be serialized to differs you can override it using this option
# [:ignore_integrity_errors => Boolean] if set to true, integrity errors will result in caching failing silently
# [:ignore_processing_errors => Boolean] if set to true, processing errors will result in caching failing silently
#
# === Examples
#
# Mounting uploaders on different columns.
#
# class Song
# mount_uploader :lyrics, LyricsUploader
# mount_uploader :alternative_lyrics, LyricsUploader
# mount_uploader :file, SongUploader
# end
#
# This will add an anonymous uploader with only the default settings:
#
# class Data
# mount_uploader :csv
# end
#
# this will add an anonymous uploader overriding the store_dir:
#
# class Product
# mount_uploader :blueprint do
# def store_dir
# 'blueprints'
# end
# end
# end
#
def mount_uploader(column, uploader=nil, options={}, &block)
mount_base(column, uploader, options.merge(multiple: false), &block)
mod = Module.new
include mod
mod.class_eval <<-RUBY, __FILE__, __LINE__+1
def #{column}
_mounter(:#{column}).uploaders[0] ||= _mounter(:#{column}).blank_uploader
end
def #{column}=(new_file)
_mounter(:#{column}).cache([new_file])
end
def #{column}_url(*args)
#{column}.url(*args)
end
def #{column}_cache
_mounter(:#{column}).cache_names[0]
end
def #{column}_cache=(cache_name)
_mounter(:#{column}).cache_names = [cache_name]
end
def remote_#{column}_url
[_mounter(:#{column}).remote_urls].flatten[0]
end
def remote_#{column}_url=(url)
_mounter(:#{column}).remote_urls = url
end
def remote_#{column}_request_header=(header)
_mounter(:#{column}).remote_request_headers = [header]
end
def #{column}_identifier
_mounter(:#{column}).read_identifiers[0]
end
def #{column}_integrity_error
#{column}_integrity_errors.last
end
def #{column}_processing_error
#{column}_processing_errors.last
end
def #{column}_download_error
#{column}_download_errors.last
end
RUBY
end
##
# Mounts the given uploader on the given array column. This means that
# assigning and reading from the array column will upload and retrieve
# multiple files. Supposing that a User class has an uploader mounted on
# images, and that images can store an array, for example it could be a
# PostgreSQL JSON column. You can assign and retrieve files like this:
#
# @user.images # => []
# @user.images = [some_file_object]
# @user.images # => []
#
# @user.images[0].url # => '/some_url.png'
#
# It is also possible (but not recommended) to omit the uploader, which
# will create an anonymous uploader class.
#
# Passing a block makes it possible to customize the uploader. This can be
# convenient for brevity, but if there is any significant logic in the
# uploader, you should do the right thing and have it in its own file.
#
# === Added instance methods
#
# Supposing a class has used +mount_uploaders+ to mount an uploader on a column
# named +images+, in that case the following methods will be added to the class:
#
# [images] Returns an array of uploaders for each uploaded file
# [images=] Caches the given files
#
# [images_urls] Returns the urls to the uploaded files
#
# [images_cache] Returns a string that identifies the cache location of the files
# [images_cache=] Retrieves the files from the cache based on the given cache name
#
# [remote_image_urls] Returns previously cached remote urls
# [remote_image_urls=] Retrieve files from the given remote urls
#
# [remove_images] An attribute reader that can be used with a checkbox to mark the files for removal
# [remove_images=] An attribute writer that can be used with a checkbox to mark the files for removal
# [remove_images?] Whether the files should be removed when store_image! is called.
#
# [store_images!] Stores all files that have been assigned with +images=+
# [remove_images!] Removes the uploaded file from the filesystem.
#
# [image_integrity_errors] Returns error objects of files which failed to pass integrity check
# [image_processing_errors] Returns error objects of files which failed to be processed
# [image_download_errors] Returns error objects of files which failed to be downloaded
#
# [image_identifiers] Reads out the identifiers of the files
#
# === Parameters
#
# [column (Symbol)] the attribute to mount this uploader on
# [uploader (CarrierWave::Uploader)] the uploader class to mount
# [options (Hash{Symbol => Object})] a set of options
# [&block (Proc)] customize anonymous uploaders
#
# === Options
#
# [:mount_on => Symbol] if the name of the column to be serialized to differs you can override it using this option
# [:ignore_integrity_errors => Boolean] if set to true, integrity errors will result in caching failing silently
# [:ignore_processing_errors => Boolean] if set to true, processing errors will result in caching failing silently
#
# === Examples
#
# Mounting uploaders on different columns.
#
# class Song
# mount_uploaders :lyrics, LyricsUploader
# mount_uploaders :alternative_lyrics, LyricsUploader
# mount_uploaders :files, SongUploader
# end
#
# This will add an anonymous uploader with only the default settings:
#
# class Data
# mount_uploaders :csv_files
# end
#
# this will add an anonymous uploader overriding the store_dir:
#
# class Product
# mount_uploaders :blueprints do
# def store_dir
# 'blueprints'
# end
# end
# end
#
def mount_uploaders(column, uploader=nil, options={}, &block)
mount_base(column, uploader, options.merge(multiple: true), &block)
mod = Module.new
include mod
mod.class_eval <<-RUBY, __FILE__, __LINE__+1
def #{column}
_mounter(:#{column}).uploaders
end
def #{column}=(new_files)
_mounter(:#{column}).cache(new_files)
end
def #{column}_urls(*args)
_mounter(:#{column}).urls(*args)
end
def #{column}_cache
names = _mounter(:#{column}).cache_names
names.to_json if names.present?
end
def #{column}_cache=(cache_name)
_mounter(:#{column}).cache_names = JSON.parse(cache_name) if cache_name.present?
end
def remote_#{column}_urls
_mounter(:#{column}).remote_urls
end
def remote_#{column}_urls=(urls)
_mounter(:#{column}).remote_urls = urls
end
def remote_#{column}_request_headers=(headers)
_mounter(:#{column}).remote_request_headers = headers
end
def #{column}_identifiers
_mounter(:#{column}).read_identifiers
end
RUBY
end
private
def mount_base(column, uploader=nil, options={}, &block)
include CarrierWave::Mount::Extension
uploader = build_uploader(uploader, column, &block)
uploaders[column.to_sym] = uploader
uploader_options[column.to_sym] = options
# Make sure to write over accessors directly defined on the class.
# Simply super to the included module below.
class_eval <<-RUBY, __FILE__, __LINE__+1
def #{column}; super; end
def #{column}=(new_file); super; end
RUBY
mod = Module.new
include mod
mod.class_eval <<-RUBY, __FILE__, __LINE__+1
def #{column}?
_mounter(:#{column}).present?
end
def remove_#{column}
_mounter(:#{column}).remove
end
def remove_#{column}!
_mounter(:#{column}).remove!
self.remove_#{column} = true
write_#{column}_identifier
end
def remove_#{column}=(value)
_mounter(:#{column}).remove = value
end
def remove_#{column}?
_mounter(:#{column}).remove?
end
def store_#{column}!
_mounter(:#{column}).store!
end
def #{column}_integrity_errors
_mounter(:#{column}).integrity_errors
end
def #{column}_processing_errors
_mounter(:#{column}).processing_errors
end
def #{column}_download_errors
_mounter(:#{column}).download_errors
end
def write_#{column}_identifier
_mounter(:#{column}).write_identifier
end
def mark_remove_#{column}_false
_mounter(:#{column}).remove = false
end
def reset_previous_changes_for_#{column}
_mounter(:#{column}).reset_changes!
end
def remove_previously_stored_#{column}
_mounter(:#{column}).remove_previous
end
def remove_rolled_back_#{column}
_mounter(:#{column}).remove_added
end
RUBY
end
def build_uploader(uploader, column, &block)
uploader ||= CarrierWave::Uploader::Base
return uploader unless block_given?
uploader = Class.new(uploader)
const_set("CarrierWave#{column.to_s.camelize}Uploader", uploader)
uploader.class_eval(&block)
uploader
end
module Extension
##
# overwrite this to read from a serialized attribute
#
def read_uploader(column); end
##
# overwrite this to write to a serialized attribute
#
def write_uploader(column, identifier); end
private
def initialize_dup(other)
@_mounters = @_mounters.dup
super
end
def _mounter(column)
# We cannot memoize in frozen objects :(
return Mounter.build(self, column) if frozen?
@_mounters ||= {}
@_mounters[column] ||= Mounter.build(self, column)
end
end # Extension
end # Mount
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/mounter.rb 0000664 0000000 0000000 00000014456 14577507162 0021161 0 ustar 00root root 0000000 0000000 module CarrierWave
# this is an internal class, used by CarrierWave::Mount so that
# we don't pollute the model with a lot of methods.
class Mounter # :nodoc:
class Single < Mounter # :nodoc
def identifier
uploaders.first&.identifier
end
def temporary_identifier
temporary_identifiers.first
end
end
class Multiple < Mounter # :nodoc
def identifier
uploaders.map(&:identifier).presence
end
def temporary_identifier
temporary_identifiers.presence
end
end
def self.build(record, column)
if record.class.uploader_options[column][:multiple]
Multiple.new(record, column)
else
Single.new(record, column)
end
end
attr_reader :column, :record, :remote_urls, :remove,
:integrity_errors, :processing_errors, :download_errors
attr_accessor :remote_request_headers, :uploader_options
def initialize(record, column)
@record = record
@column = column
@options = record.class.uploader_options[column]
@download_errors = []
@processing_errors = []
@integrity_errors = []
@removed_uploaders = []
@added_uploaders = []
end
def uploader_class
record.class.uploaders[column]
end
def blank_uploader
uploader_class.new(record, column)
end
def identifiers
uploaders.map(&:identifier)
end
def read_identifiers
[record.read_uploader(serialization_column)].flatten.reject(&:blank?)
end
def uploaders
@uploaders ||= read_identifiers.map do |identifier|
uploader = blank_uploader
uploader.retrieve_from_store!(identifier)
uploader
end
end
def cache(new_files)
return if !new_files.is_a?(Array) && new_files.blank?
old_uploaders = uploaders
@uploaders = new_files.map do |new_file|
handle_error do
if new_file.is_a?(String)
if (uploader = old_uploaders.detect { |old_uploader| old_uploader.identifier == new_file })
uploader.staged = true
uploader
else
begin
uploader = blank_uploader
uploader.retrieve_from_cache!(new_file)
uploader
rescue CarrierWave::InvalidParameter
nil
end
end
else
uploader = blank_uploader
uploader.cache!(new_file)
uploader
end
end
end.reject(&:blank?)
@removed_uploaders += (old_uploaders - @uploaders)
write_temporary_identifier
end
def cache_names
uploaders.map(&:cache_name).compact
end
def cache_names=(cache_names)
cache_names = cache_names.reject(&:blank?)
return if cache_names.blank?
clear_unstaged
cache_names.each do |cache_name|
uploader = blank_uploader
uploader.retrieve_from_cache!(cache_name)
@uploaders << uploader
rescue CarrierWave::InvalidParameter
# ignore
end
write_temporary_identifier
end
def remote_urls=(urls)
if urls.nil?
urls = []
else
urls = Array.wrap(urls).reject(&:blank?)
return if urls.blank?
end
@remote_urls = urls
clear_unstaged
@remote_urls.zip(remote_request_headers || []) do |url, header|
handle_error do
uploader = blank_uploader
uploader.download!(url, header || {})
@uploaders << uploader
end
end
write_temporary_identifier
end
def store!
uploaders.each(&:store!)
end
def write_identifier
return if record.frozen?
clear! if remove?
additions, remains = uploaders.partition(&:cached?)
existing_identifiers = (@removed_uploaders + remains).map(&:identifier)
additions.each do |uploader|
uploader.deduplicate(existing_identifiers)
existing_identifiers << uploader.identifier
end
@added_uploaders += additions
record.write_uploader(serialization_column, identifier)
end
def urls(*args)
uploaders.map { |u| u.url(*args) }
end
def blank?
uploaders.none?(&:present?)
end
def remove=(value)
@remove = value
write_temporary_identifier
end
def remove?
remove.present? && (remove.to_s !~ /\A0|false$\z/)
end
def remove!
uploaders.each(&:remove!)
clear!
end
def clear!
@removed_uploaders += uploaders
@remove = nil
@uploaders = []
end
def reset_changes!
@removed_uploaders = []
@added_uploaders = []
end
def serialization_column
option(:mount_on) || column
end
def remove_previous
current_paths = uploaders.map(&:path)
@removed_uploaders
.reject {|uploader| current_paths.include?(uploader.path) }
.each { |uploader| uploader.remove! if uploader.remove_previously_stored_files_after_update }
reset_changes!
end
def remove_added
current_paths = (@removed_uploaders + uploaders.select(&:staged)).map(&:path)
@added_uploaders
.reject {|uploader| current_paths.include?(uploader.path) }
.each { |uploader| uploader.remove! }
reset_changes!
end
private
def option(name)
self.uploader_options ||= {}
self.uploader_options[name] ||= record.class.uploader_option(column, name)
end
def clear_unstaged
@uploaders ||= []
staged, unstaged = @uploaders.partition(&:staged)
@uploaders = staged
@removed_uploaders += unstaged
end
def handle_error
yield
rescue CarrierWave::DownloadError => e
@download_errors << e
raise e unless option(:ignore_download_errors)
rescue CarrierWave::ProcessingError => e
@processing_errors << e
raise e unless option(:ignore_processing_errors)
rescue CarrierWave::IntegrityError => e
@integrity_errors << e
raise e unless option(:ignore_integrity_errors)
end
def write_temporary_identifier
return if record.frozen?
record.write_uploader(serialization_column, temporary_identifier)
end
def temporary_identifiers
if remove?
[]
else
uploaders.map { |uploader| uploader.temporary_identifier }
end
end
end # Mounter
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/orm/ 0000775 0000000 0000000 00000000000 14577507162 0017726 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/lib/carrierwave/orm/activerecord.rb 0000664 0000000 0000000 00000004430 14577507162 0022726 0 ustar 00root root 0000000 0000000 require 'active_record'
require 'carrierwave/validations/active_model'
module CarrierWave
module ActiveRecord
include CarrierWave::Mount
private
def mount_base(column, uploader=nil, options={}, &block)
super
alias_method :read_uploader, :read_attribute
alias_method :write_uploader, :write_attribute
public :read_uploader
public :write_uploader
include CarrierWave::Validations::ActiveModel
validates_integrity_of column if uploader_option(column.to_sym, :validate_integrity)
validates_processing_of column if uploader_option(column.to_sym, :validate_processing)
validates_download_of column if uploader_option(column.to_sym, :validate_download)
after_save :"store_#{column}!"
before_save :"write_#{column}_identifier"
if ::ActiveRecord.try(:run_after_transaction_callbacks_in_order_defined)
after_commit :"remove_previously_stored_#{column}", :on => :update
after_commit :"reset_previous_changes_for_#{column}"
after_commit :"mark_remove_#{column}_false", :on => :update
else
after_commit :"mark_remove_#{column}_false", :on => :update
after_commit :"reset_previous_changes_for_#{column}"
after_commit :"remove_previously_stored_#{column}", :on => :update
end
after_commit :"remove_#{column}!", :on => :destroy
after_rollback :"remove_rolled_back_#{column}"
mod = Module.new
prepend mod
mod.class_eval <<-RUBY, __FILE__, __LINE__+1
# Reset cached mounter on record reload
def reload(*)
@_mounters = nil
super
end
# Reset cached mounter on record dup
def initialize_dup(other)
old_uploaders = _mounter(:"#{column}").uploaders
super
@_mounters[:"#{column}"] = nil
# The attribute needs to be cleared to prevent it from picked up as identifier
write_attribute(_mounter(:#{column}).serialization_column, nil)
_mounter(:"#{column}").cache(old_uploaders)
end
def write_#{column}_identifier
return unless has_attribute?(_mounter(:#{column}).serialization_column)
super
end
RUBY
end
end # ActiveRecord
end # CarrierWave
ActiveRecord::Base.extend CarrierWave::ActiveRecord
carrierwave-3.0.7/lib/carrierwave/processing.rb 0000664 0000000 0000000 00000000174 14577507162 0021634 0 ustar 00root root 0000000 0000000 require "carrierwave/processing/rmagick"
require "carrierwave/processing/mini_magick"
require "carrierwave/processing/vips"
carrierwave-3.0.7/lib/carrierwave/processing/ 0000775 0000000 0000000 00000000000 14577507162 0021305 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/lib/carrierwave/processing/mini_magick.rb 0000664 0000000 0000000 00000024721 14577507162 0024107 0 ustar 00root root 0000000 0000000 module CarrierWave
##
# This module simplifies manipulation with MiniMagick by providing a set
# of convenient helper methods. If you want to use them, you'll need to
# require this file:
#
# require 'carrierwave/processing/mini_magick'
#
# And then include it in your uploader:
#
# class MyUploader < CarrierWave::Uploader::Base
# include CarrierWave::MiniMagick
# end
#
# You can now use the provided helpers:
#
# class MyUploader < CarrierWave::Uploader::Base
# include CarrierWave::MiniMagick
#
# process :resize_to_fit => [200, 200]
# end
#
# Or create your own helpers with the powerful minimagick! method, which
# yields an ImageProcessing::Builder object. Check out the ImageProcessing
# docs at http://github.com/janko-m/image_processing and the list of all
# available ImageMagick options at
# http://www.imagemagick.org/script/command-line-options.php for more info.
#
# class MyUploader < CarrierWave::Uploader::Base
# include CarrierWave::MiniMagick
#
# process :radial_blur => 10
#
# def radial_blur(amount)
# minimagick! do |builder|
# builder.radial_blur(amount)
# builder = yield(builder) if block_given?
# builder
# end
# end
# end
#
# === Note
#
# The ImageProcessing gem uses MiniMagick, a mini replacement for RMagick
# that uses ImageMagick command-line tools, to build a "convert" command that
# performs the processing.
#
# You can find more information here:
#
# https://github.com/minimagick/minimagick/
#
#
module MiniMagick
extend ActiveSupport::Concern
included do
require "image_processing/mini_magick"
end
module ClassMethods
def convert(format)
process :convert => format
end
def resize_to_limit(width, height)
process :resize_to_limit => [width, height]
end
def resize_to_fit(width, height)
process :resize_to_fit => [width, height]
end
def resize_to_fill(width, height, gravity='Center')
process :resize_to_fill => [width, height, gravity]
end
def resize_and_pad(width, height, background=:transparent, gravity='Center')
process :resize_and_pad => [width, height, background, gravity]
end
end
##
# Changes the image encoding format to the given format
#
# See http://www.imagemagick.org/script/command-line-options.php#format
#
# === Parameters
#
# [format (#to_s)] an abbreviation of the format
#
# === Yields
#
# [MiniMagick::Image] additional manipulations to perform
#
# === Examples
#
# image.convert(:png)
#
def convert(format, page=nil, &block)
minimagick!(block) do |builder|
builder = builder.convert(format)
builder = builder.loader(page: page) if page
builder
end
end
##
# Resize the image to fit within the specified dimensions while retaining
# the original aspect ratio. Will only resize the image if it is larger than the
# specified dimensions. The resulting image may be shorter or narrower than specified
# in the smaller dimension but will not be larger than the specified values.
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
# [combine_options (Hash)] additional ImageMagick options to apply before resizing
#
# === Yields
#
# [MiniMagick::Image] additional manipulations to perform
#
def resize_to_limit(width, height, combine_options: {}, &block)
width, height = resolve_dimensions(width, height)
minimagick!(block) do |builder|
builder.resize_to_limit(width, height)
.apply(combine_options)
end
end
##
# Resize the image to fit within the specified dimensions while retaining
# the original aspect ratio. The image may be shorter or narrower than
# specified in the smaller dimension but will not be larger than the specified values.
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
# [combine_options (Hash)] additional ImageMagick options to apply before resizing
#
# === Yields
#
# [MiniMagick::Image] additional manipulations to perform
#
def resize_to_fit(width, height, combine_options: {}, &block)
width, height = resolve_dimensions(width, height)
minimagick!(block) do |builder|
builder.resize_to_fit(width, height)
.apply(combine_options)
end
end
##
# Resize the image to fit within the specified dimensions while retaining
# the aspect ratio of the original image. If necessary, crop the image in the
# larger dimension.
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
# [gravity (String)] the current gravity suggestion (default: 'Center'; options: 'NorthWest', 'North', 'NorthEast', 'West', 'Center', 'East', 'SouthWest', 'South', 'SouthEast')
# [combine_options (Hash)] additional ImageMagick options to apply before resizing
#
# === Yields
#
# [MiniMagick::Image] additional manipulations to perform
#
def resize_to_fill(width, height, gravity = 'Center', combine_options: {}, &block)
width, height = resolve_dimensions(width, height)
minimagick!(block) do |builder|
builder.resize_to_fill(width, height, gravity: gravity)
.apply(combine_options)
end
end
##
# Resize the image to fit within the specified dimensions while retaining
# the original aspect ratio. If necessary, will pad the remaining area
# with the given color, which defaults to transparent (for gif and png,
# white for jpeg).
#
# See http://www.imagemagick.org/script/command-line-options.php#gravity
# for gravity options.
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
# [background (String, :transparent)] the color of the background as a hexcode, like "#ff45de"
# [gravity (String)] how to position the image
# [combine_options (Hash)] additional ImageMagick options to apply before resizing
#
# === Yields
#
# [MiniMagick::Image] additional manipulations to perform
#
def resize_and_pad(width, height, background=:transparent, gravity='Center', combine_options: {}, &block)
width, height = resolve_dimensions(width, height)
minimagick!(block) do |builder|
builder.resize_and_pad(width, height, background: background, gravity: gravity)
.apply(combine_options)
end
end
##
# Returns the width of the image in pixels.
#
# === Returns
#
# [Integer] the image's width in pixels
#
def width
mini_magick_image[:width]
end
##
# Returns the height of the image in pixels.
#
# === Returns
#
# [Integer] the image's height in pixels
#
def height
mini_magick_image[:height]
end
##
# Manipulate the image with MiniMagick. This method will load up an image
# and then pass each of its frames to the supplied block. It will then
# save the image to disk.
#
# NOTE: This method exists mostly for backwards compatibility, you should
# probably use #minimagick!.
#
# === Gotcha
#
# This method assumes that the object responds to +current_path+.
# Any class that this module is mixed into must have a +current_path+ method.
# CarrierWave::Uploader does, so you won't need to worry about this in
# most cases.
#
# === Yields
#
# [MiniMagick::Image] manipulations to perform
#
# === Raises
#
# [CarrierWave::ProcessingError] if manipulation failed.
#
def manipulate!
cache_stored_file! if !cached?
image = ::MiniMagick::Image.open(current_path)
image = yield(image)
FileUtils.mv image.path, current_path
image.run_command("identify", current_path)
rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
raise e if e.message =~ /(You must have .+ installed|is not installed|executable not found)/
message = I18n.translate(:"errors.messages.processing_error")
raise CarrierWave::ProcessingError, message
ensure
image.destroy! if image
end
# Process the image with MiniMagick, using the ImageProcessing gem. This
# method will build a "convert" ImageMagick command and execute it on the
# current image.
#
# === Gotcha
#
# This method assumes that the object responds to +current_path+.
# Any class that this module is mixed into must have a +current_path+ method.
# CarrierWave::Uploader does, so you won't need to worry about this in
# most cases.
#
# === Yields
#
# [ImageProcessing::Builder] use it to define processing to be performed
#
# === Raises
#
# [CarrierWave::ProcessingError] if processing failed.
def minimagick!(block = nil)
builder = ImageProcessing::MiniMagick.source(current_path)
builder = yield(builder)
result = builder.call
result.close
# backwards compatibility (we want to eventually move away from MiniMagick::Image)
if block
image = ::MiniMagick::Image.new(result.path, result)
image = block.call(image)
result = image.instance_variable_get(:@tempfile)
end
FileUtils.mv result.path, current_path
if File.extname(result.path) != File.extname(current_path)
move_to = current_path.chomp(File.extname(current_path)) + File.extname(result.path)
file.content_type = Marcel::Magic.by_path(move_to).try(:type)
file.move_to(move_to, permissions, directory_permissions)
end
rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
raise e if e.message =~ /(You must have .+ installed|is not installed|executable not found)/
message = I18n.translate(:"errors.messages.processing_error")
raise CarrierWave::ProcessingError, message
end
private
def resolve_dimensions(*dimensions)
dimensions.map do |value|
next value unless value.instance_of?(Proc)
value.arity >= 1 ? value.call(self) : value.call
end
end
def mini_magick_image
::MiniMagick::Image.read(read)
end
end # MiniMagick
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/processing/rmagick.rb 0000664 0000000 0000000 00000027146 14577507162 0023261 0 ustar 00root root 0000000 0000000 module CarrierWave
##
# This module simplifies manipulation with RMagick by providing a set
# of convenient helper methods. If you want to use them, you'll need to
# require this file:
#
# require 'carrierwave/processing/rmagick'
#
# And then include it in your uploader:
#
# class MyUploader < CarrierWave::Uploader::Base
# include CarrierWave::RMagick
# end
#
# You can now use the provided helpers:
#
# class MyUploader < CarrierWave::Uploader::Base
# include CarrierWave::RMagick
#
# process :resize_to_fit => [200, 200]
# end
#
# Or create your own helpers with the powerful manipulate! method. Check
# out the RMagick docs at http://www.imagemagick.org/RMagick/doc/ for more
# info
#
# class MyUploader < CarrierWave::Uploader::Base
# include CarrierWave::RMagick
#
# process :do_stuff => 10.0
#
# def do_stuff(blur_factor)
# manipulate! do |img|
# img = img.sepiatone
# img = img.auto_orient
# img = img.radial_blur(blur_factor)
# end
# end
# end
#
# === Note
#
# You should be aware how RMagick handles memory. manipulate! takes care
# of freeing up memory for you, but for optimum memory usage you should
# use destructive operations as much as possible:
#
# DON'T DO THIS:
# img = img.resize_to_fit
#
# DO THIS INSTEAD:
# img.resize_to_fit!
#
# Read this for more information why:
#
# http://rubyforge.org/forum/forum.php?thread_id=1374&forum_id=1618
#
module RMagick
extend ActiveSupport::Concern
included do
begin
require "rmagick"
rescue LoadError
begin
require "RMagick"
rescue LoadError => e
e.message << " (You may need to install the rmagick gem)"
raise e
end
end
prepend Module.new {
def initialize(*)
super
@format = nil
end
}
end
module ClassMethods
def convert(format)
process :convert => format
end
def resize_to_limit(width, height)
process :resize_to_limit => [width, height]
end
def resize_to_fit(width, height)
process :resize_to_fit => [width, height]
end
def resize_to_fill(width, height, gravity=::Magick::CenterGravity)
process :resize_to_fill => [width, height, gravity]
end
def resize_and_pad(width, height, background=:transparent, gravity=::Magick::CenterGravity)
process :resize_and_pad => [width, height, background, gravity]
end
def resize_to_geometry_string(geometry_string)
process :resize_to_geometry_string => [geometry_string]
end
end
##
# Changes the image encoding format to the given format
#
# See even http://www.imagemagick.org/RMagick/doc/magick.html#formats
#
# === Parameters
#
# [format (#to_s)] an abbreviation of the format
#
# === Yields
#
# [Magick::Image] additional manipulations to perform
#
# === Examples
#
# image.convert(:png)
#
def convert(format)
manipulate!(:format => format)
@format = format
end
##
# Resize the image to fit within the specified dimensions while retaining
# the original aspect ratio. Will only resize the image if it is larger than the
# specified dimensions. The resulting image may be shorter or narrower than specified
# in the smaller dimension but will not be larger than the specified values.
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
#
# === Yields
#
# [Magick::Image] additional manipulations to perform
#
def resize_to_limit(width, height)
width = dimension_from width
height = dimension_from height
manipulate! do |img|
geometry = Magick::Geometry.new(width, height, 0, 0, Magick::GreaterGeometry)
new_img = img.change_geometry(geometry) do |new_width, new_height|
img.resize(new_width, new_height)
end
destroy_image(img)
new_img = yield(new_img) if block_given?
new_img
end
end
##
# From the RMagick documentation: "Resize the image to fit within the
# specified dimensions while retaining the original aspect ratio. The
# image may be shorter or narrower than specified in the smaller dimension
# but will not be larger than the specified values."
#
# See even http://www.imagemagick.org/RMagick/doc/image3.html#resize_to_fit
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
#
# === Yields
#
# [Magick::Image] additional manipulations to perform
#
def resize_to_fit(width, height)
width = dimension_from width
height = dimension_from height
manipulate! do |img|
img.resize_to_fit!(width, height)
img = yield(img) if block_given?
img
end
end
##
# From the RMagick documentation: "Resize the image to fit within the
# specified dimensions while retaining the aspect ratio of the original
# image. If necessary, crop the image in the larger dimension."
#
# See even http://www.imagemagick.org/RMagick/doc/image3.html#resize_to_fill
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
#
# === Yields
#
# [Magick::Image] additional manipulations to perform
#
def resize_to_fill(width, height, gravity=::Magick::CenterGravity)
width = dimension_from width
height = dimension_from height
manipulate! do |img|
img.crop_resized!(width, height, gravity)
img = yield(img) if block_given?
img
end
end
##
# Resize the image to fit within the specified dimensions while retaining
# the original aspect ratio. If necessary, will pad the remaining area
# with the given color, which defaults to transparent (for gif and png,
# white for jpeg).
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
# [background (String, :transparent)] the color of the background as a hexcode, like "#ff45de"
# [gravity (Magick::GravityType)] how to position the image
#
# === Yields
#
# [Magick::Image] additional manipulations to perform
#
def resize_and_pad(width, height, background=:transparent, gravity=::Magick::CenterGravity)
width = dimension_from width
height = dimension_from height
manipulate! do |img|
img.resize_to_fit!(width, height)
filled = ::Magick::Image.new(width, height) { |image| image.background_color = background == :transparent ? 'rgba(255,255,255,0)' : background.to_s }
filled.composite!(img, gravity, ::Magick::OverCompositeOp)
destroy_image(img)
filled = yield(filled) if block_given?
filled
end
end
##
# Resize the image per the provided geometry string.
#
# === Parameters
#
# [geometry_string (String)] the proportions in which to scale image
#
# === Yields
#
# [Magick::Image] additional manipulations to perform
#
def resize_to_geometry_string(geometry_string)
manipulate! do |img|
new_img = img.change_geometry(geometry_string) do |new_width, new_height|
img.resize(new_width, new_height)
end
destroy_image(img)
new_img = yield(new_img) if block_given?
new_img
end
end
##
# Returns the width of the image.
#
# === Returns
#
# [Integer] the image's width in pixels
#
def width
rmagick_image.columns
end
##
# Returns the height of the image.
#
# === Returns
#
# [Integer] the image's height in pixels
#
def height
rmagick_image.rows
end
##
# Manipulate the image with RMagick. This method will load up an image
# and then pass each of its frames to the supplied block. It will then
# save the image to disk.
#
# === Gotcha
#
# This method assumes that the object responds to +current_path+.
# Any class that this module is mixed into must have a +current_path+ method.
# CarrierWave::Uploader does, so you won't need to worry about this in
# most cases.
#
# === Yields
#
# [Magick::Image] manipulations to perform
# [Integer] Frame index if the image contains multiple frames
# [Hash] options, see below
#
# === Options
#
# The options argument to this method is also yielded as the third
# block argument.
#
# Currently, the following options are defined:
#
# ==== :write
# A hash of assignments to be evaluated in the block given to the RMagick write call.
#
# An example:
#
# manipulate! do |img, index, options|
# options[:write] = {
# :quality => 50,
# :depth => 8
# }
# img
# end
#
# This will translate to the following RMagick::Image#write call:
#
# image.write do |img|
# self.quality = 50
# self.depth = 8
# end
#
# ==== :read
# A hash of assignments to be given to the RMagick read call.
#
# The options available are identical to those for write, but are passed in directly, like this:
#
# manipulate! :read => { :density => 300 }
#
# ==== :format
# Specify the output format. If unset, the filename extension is used to determine the format.
#
# === Raises
#
# [CarrierWave::ProcessingError] if manipulation failed.
#
def manipulate!(options={}, &block)
cache_stored_file! if !cached?
read_block = create_info_block(options[:read])
image = ::Magick::Image.read(current_path, &read_block)
frames = ::Magick::ImageList.new
image.each_with_index do |frame, index|
frame = yield(*[frame, index, options].take(block.arity)) if block_given?
frames << frame if frame
end
frames.append(true) if block_given?
write_block = create_info_block(options[:write])
if options[:format] || @format
frames.write("#{options[:format] || @format}:#{current_path}", &write_block)
move_to = current_path.chomp(File.extname(current_path)) + ".#{options[:format] || @format}"
file.content_type = Marcel::Magic.by_path(move_to).try(:type)
file.move_to(move_to, permissions, directory_permissions)
else
frames.write(current_path, &write_block)
end
destroy_image(frames)
rescue ::Magick::ImageMagickError
raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.processing_error")
end
private
def create_info_block(options)
return nil unless options
proc do |img|
options.each do |k, v|
if v.is_a?(String) && (matches = v.match(/^["'](.+)["']/))
ActiveSupport::Deprecation.warn "Passing quoted strings like #{v} to #manipulate! is deprecated, pass them without quoting."
v = matches[1]
end
img.public_send(:"#{k}=", v)
end
end
end
def destroy_image(image)
image.try(:destroy!)
end
def dimension_from(value)
return value unless value.instance_of?(Proc)
value.arity >= 1 ? value.call(self) : value.call
end
def rmagick_image
::Magick::Image.from_blob(self.read).first
end
end # RMagick
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/processing/vips.rb 0000664 0000000 0000000 00000020707 14577507162 0022621 0 ustar 00root root 0000000 0000000 module CarrierWave
##
# This module simplifies manipulation with vips by providing a set
# of convenient helper methods. If you want to use them, you'll need to
# require this file:
#
# require 'carrierwave/processing/vips'
#
# And then include it in your uploader:
#
# class MyUploader < CarrierWave::Uploader::Base
# include CarrierWave::Vips
# end
#
# You can now use the provided helpers:
#
# class MyUploader < CarrierWave::Uploader::Base
# include CarrierWave::Vips
#
# process :resize_to_fit => [200, 200]
# end
#
# Or create your own helpers with the powerful vips! method, which
# yields an ImageProcessing::Builder object. Check out the ImageProcessing
# docs at http://github.com/janko-m/image_processing and the list of all
# available Vips options at
# https://libvips.github.io/libvips/API/current/using-cli.html for more info.
#
# class MyUploader < CarrierWave::Uploader::Base
# include CarrierWave::Vips
#
# process :radial_blur => 10
#
# def radial_blur(amount)
# vips! do |builder|
# builder.radial_blur(amount)
# builder = yield(builder) if block_given?
# builder
# end
# end
# end
#
# === Note
#
# The ImageProcessing gem uses ruby-vips, a binding for the vips image
# library. You can find more information here:
#
# https://github.com/libvips/ruby-vips
#
#
module Vips
extend ActiveSupport::Concern
included do
require "image_processing/vips"
# We need to disable caching since we're editing images in place.
::Vips.cache_set_max(0)
end
module ClassMethods
def convert(format)
process :convert => format
end
def resize_to_limit(width, height)
process :resize_to_limit => [width, height]
end
def resize_to_fit(width, height)
process :resize_to_fit => [width, height]
end
def resize_to_fill(width, height, gravity='centre')
process :resize_to_fill => [width, height, gravity]
end
def resize_and_pad(width, height, background=nil, gravity='centre', alpha=nil)
process :resize_and_pad => [width, height, background, gravity, alpha]
end
end
##
# Changes the image encoding format to the given format
#
# See https://libvips.github.io/libvips/API/current/using-cli.html#using-command-line-conversion
#
# === Parameters
#
# [format (#to_s)] an abbreviation of the format
#
# === Yields
#
# [Vips::Image] additional manipulations to perform
#
# === Examples
#
# image.convert(:png)
#
def convert(format, page=nil)
vips! do |builder|
builder = builder.convert(format)
builder = builder.loader(page: page) if page
builder
end
end
##
# Resize the image to fit within the specified dimensions while retaining
# the original aspect ratio. Will only resize the image if it is larger than the
# specified dimensions. The resulting image may be shorter or narrower than specified
# in the smaller dimension but will not be larger than the specified values.
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
# [combine_options (Hash)] additional Vips options to apply before resizing
#
# === Yields
#
# [Vips::Image] additional manipulations to perform
#
def resize_to_limit(width, height, combine_options: {})
width, height = resolve_dimensions(width, height)
vips! do |builder|
builder.resize_to_limit(width, height)
.apply(combine_options)
end
end
##
# Resize the image to fit within the specified dimensions while retaining
# the original aspect ratio. The image may be shorter or narrower than
# specified in the smaller dimension but will not be larger than the specified values.
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
# [combine_options (Hash)] additional Vips options to apply before resizing
#
# === Yields
#
# [Vips::Image] additional manipulations to perform
#
def resize_to_fit(width, height, combine_options: {})
width, height = resolve_dimensions(width, height)
vips! do |builder|
builder.resize_to_fit(width, height)
.apply(combine_options)
end
end
##
# Resize the image to fit within the specified dimensions while retaining
# the aspect ratio of the original image. If necessary, crop the image in the
# larger dimension.
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
# [combine_options (Hash)] additional vips options to apply before resizing
#
# === Yields
#
# [Vips::Image] additional manipulations to perform
#
def resize_to_fill(width, height, _gravity = nil, combine_options: {})
width, height = resolve_dimensions(width, height)
vips! do |builder|
builder.resize_to_fill(width, height).apply(combine_options)
end
end
##
# Resize the image to fit within the specified dimensions while retaining
# the original aspect ratio. If necessary, will pad the remaining area
# with the given color, which defaults to transparent (for gif and png,
# white for jpeg).
#
# See https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsCompassDirection
# for gravity options.
#
# === Parameters
#
# [width (Integer)] the width to scale the image to
# [height (Integer)] the height to scale the image to
# [background (List, nil)] the color of the background as a RGB, like [0, 255, 255], nil indicates transparent
# [gravity (String)] how to position the image
# [alpha (Boolean, nil)] pad the image with the alpha channel if supported
# [combine_options (Hash)] additional vips options to apply before resizing
#
# === Yields
#
# [Vips::Image] additional manipulations to perform
#
def resize_and_pad(width, height, background=nil, gravity='centre', alpha=nil, combine_options: {})
width, height = resolve_dimensions(width, height)
vips! do |builder|
builder.resize_and_pad(width, height, background: background, gravity: gravity, alpha: alpha)
.apply(combine_options)
end
end
##
# Returns the width of the image in pixels.
#
# === Returns
#
# [Integer] the image's width in pixels
#
def width
vips_image.width
end
##
# Returns the height of the image in pixels.
#
# === Returns
#
# [Integer] the image's height in pixels
#
def height
vips_image.height
end
# Process the image with vip, using the ImageProcessing gem. This
# method will build a "convert" vips command and execute it on the
# current image.
#
# === Gotcha
#
# This method assumes that the object responds to +current_path+.
# Any class that this module is mixed into must have a +current_path+ method.
# CarrierWave::Uploader does, so you won't need to worry about this in
# most cases.
#
# === Yields
#
# [ImageProcessing::Builder] use it to define processing to be performed
#
# === Raises
#
# [CarrierWave::ProcessingError] if processing failed.
def vips!
builder = ImageProcessing::Vips.source(current_path)
builder = yield(builder)
result = builder.call
result.close
FileUtils.mv result.path, current_path
if File.extname(result.path) != File.extname(current_path)
move_to = current_path.chomp(File.extname(current_path)) + File.extname(result.path)
file.content_type = Marcel::Magic.by_path(move_to).try(:type)
file.move_to(move_to, permissions, directory_permissions)
end
rescue ::Vips::Error
message = I18n.translate(:"errors.messages.processing_error")
raise CarrierWave::ProcessingError, message
end
private
def resolve_dimensions(*dimensions)
dimensions.map do |value|
next value unless value.instance_of?(Proc)
value.arity >= 1 ? value.call(self) : value.call
end
end
def vips_image
::Vips::Image.new_from_buffer(read, "")
end
end # Vips
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/sanitized_file.rb 0000664 0000000 0000000 00000020723 14577507162 0022453 0 ustar 00root root 0000000 0000000 require 'pathname'
require 'active_support/core_ext/string/multibyte'
require 'marcel'
module CarrierWave
##
# SanitizedFile is a base class which provides a common API around all
# the different quirky Ruby File libraries. It has support for Tempfile,
# File, StringIO, Merb-style upload Hashes, as well as paths given as
# Strings and Pathnames.
#
# It's probably needlessly comprehensive and complex. Help is appreciated.
#
class SanitizedFile
include CarrierWave::Utilities::FileName
attr_reader :file
class << self
attr_writer :sanitize_regexp
def sanitize_regexp
@sanitize_regexp ||= /[^[:word:]\.\-\+]/
end
end
def initialize(file)
self.file = file
@content = @content_type = nil
end
##
# Returns the filename as is, without sanitizing it.
#
# === Returns
#
# [String] the unsanitized filename
#
def original_filename
return @original_filename if @original_filename
if @file && @file.respond_to?(:original_filename)
@file.original_filename
elsif path
File.basename(path)
end
end
##
# Returns the filename, sanitized to strip out any evil characters.
#
# === Returns
#
# [String] the sanitized filename
#
def filename
sanitize(original_filename) if original_filename
end
alias_method :identifier, :filename
##
# Returns the file's size.
#
# === Returns
#
# [Integer] the file's size in bytes.
#
def size
if is_path?
exists? ? File.size(path) : 0
elsif @file.respond_to?(:size)
@file.size
elsif path
exists? ? File.size(path) : 0
else
0
end
end
##
# Returns the full path to the file. If the file has no path, it will return nil.
#
# === Returns
#
# [String, nil] the path where the file is located.
#
def path
return if @file.blank?
if is_path?
File.expand_path(@file)
elsif @file.respond_to?(:path) && !@file.path.blank?
File.expand_path(@file.path)
end
end
##
# === Returns
#
# [Boolean] whether the file is supplied as a pathname or string.
#
def is_path?
!!((@file.is_a?(String) || @file.is_a?(Pathname)) && !@file.blank?)
end
##
# === Returns
#
# [Boolean] whether the file is valid and has a non-zero size
#
def empty?
@file.nil? || self.size.nil? || (self.size.zero? && !self.exists?)
end
##
# === Returns
#
# [Boolean] Whether the file exists
#
def exists?
self.path.present? && File.exist?(self.path)
end
##
# Returns the contents of the file.
#
# === Returns
#
# [String] contents of the file
#
def read(*args)
if @content
if args.empty?
@content
else
length, outbuf = args
raise ArgumentError, "outbuf argument not supported since the content is already loaded" if outbuf
@content[0, length]
end
elsif is_path?
File.open(@file, "rb") {|file| file.read(*args)}
else
@file.try(:rewind)
@content = @file.read(*args)
@file.try(:close) unless @file.class.ancestors.include?(::StringIO) || @file.try(:closed?)
@content
end
end
##
# Moves the file to the given path
#
# === Parameters
#
# [new_path (String)] The path where the file should be moved.
# [permissions (Integer)] permissions to set on the file in its new location.
# [directory_permissions (Integer)] permissions to set on created directories.
#
def move_to(new_path, permissions=nil, directory_permissions=nil, keep_filename=false)
return if self.empty?
new_path = File.expand_path(new_path)
mkdir!(new_path, directory_permissions)
move!(new_path)
chmod!(new_path, permissions)
self.file = {tempfile: new_path, filename: keep_filename ? original_filename : nil, content_type: declared_content_type}
self
end
##
# Helper to move file to new path.
#
def move!(new_path)
if exists?
FileUtils.mv(path, new_path) unless File.identical?(new_path, path)
else
File.open(new_path, "wb") { |f| f.write(read) }
end
end
##
# Creates a copy of this file and moves it to the given path. Returns the copy.
#
# === Parameters
#
# [new_path (String)] The path where the file should be copied to.
# [permissions (Integer)] permissions to set on the copy
# [directory_permissions (Integer)] permissions to set on created directories.
#
# === Returns
#
# @return [CarrierWave::SanitizedFile] the location where the file will be stored.
#
def copy_to(new_path, permissions=nil, directory_permissions=nil)
return if self.empty?
new_path = File.expand_path(new_path)
mkdir!(new_path, directory_permissions)
copy!(new_path)
chmod!(new_path, permissions)
self.class.new({tempfile: new_path, content_type: declared_content_type})
end
##
# Helper to create copy of file in new path.
#
def copy!(new_path)
if exists?
FileUtils.cp(path, new_path) unless new_path == path
else
File.open(new_path, "wb") { |f| f.write(read) }
end
end
##
# Removes the file from the filesystem.
#
def delete
FileUtils.rm(self.path) if exists?
end
##
# Returns a File object, or nil if it does not exist.
#
# === Returns
#
# [File] a File object representing the SanitizedFile
#
def to_file
return @file if @file.is_a?(File)
File.open(path, "rb") if exists?
end
##
# Returns the content type of the file.
#
# === Returns
#
# [String] the content type of the file
#
def content_type
@content_type ||=
identified_content_type ||
declared_content_type ||
guessed_safe_content_type ||
Marcel::MimeType::BINARY
end
##
# Sets the content type of the file.
#
# === Returns
#
# [String] the content type of the file
#
def content_type=(type)
@content_type = type
end
##
# Used to sanitize the file name. Public to allow overriding for non-latin characters.
#
# === Returns
#
# [Regexp] the regexp for sanitizing the file name
#
def sanitize_regexp
CarrierWave::SanitizedFile.sanitize_regexp
end
private
def file=(file)
if file.is_a?(Hash)
@file = file["tempfile"] || file[:tempfile]
@original_filename = file["filename"] || file[:filename]
@declared_content_type = file["content_type"] || file[:content_type] || file["type"] || file[:type]
else
@file = file
@original_filename = nil
@declared_content_type = nil
end
end
# create the directory if it doesn't exist
def mkdir!(path, directory_permissions)
options = {}
options[:mode] = directory_permissions if directory_permissions
FileUtils.mkdir_p(File.dirname(path), **options) unless File.exist?(File.dirname(path))
end
def chmod!(path, permissions)
File.chmod(permissions, path) if permissions
end
# Sanitize the filename, to prevent hacking
def sanitize(name)
name = name.scrub
name = name.tr("\\", "/") # work-around for IE
name = File.basename(name)
name = name.gsub(sanitize_regexp, "_")
name = "_#{name}" if name =~ /\A\.+\z/
name = "unnamed" if name.size.zero?
name.mb_chars.to_s
end
def declared_content_type
@declared_content_type ||
if @file.respond_to?(:content_type) && @file.content_type
Marcel::MimeType.for(declared_type: @file.content_type.to_s.chomp)
end
end
# Guess content type from its file extension. Limit what to be returned to prevent spoofing.
def guessed_safe_content_type
return unless path
type = Marcel::Magic.by_path(original_filename).to_s
type if type.start_with?('text/') || type.start_with?('application/json')
end
def identified_content_type
with_io do |io|
Marcel::Magic.by_magic(io).try(:type)
end
rescue Errno::ENOENT
nil
end
def with_io(&block)
if file.is_a?(IO)
begin
yield file
ensure
file.try(:rewind)
end
elsif path
File.open(path, &block)
end
end
end # SanitizedFile
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/storage.rb 0000664 0000000 0000000 00000000154 14577507162 0021122 0 ustar 00root root 0000000 0000000 require "carrierwave/storage/abstract"
require "carrierwave/storage/file"
require "carrierwave/storage/fog"
carrierwave-3.0.7/lib/carrierwave/storage/ 0000775 0000000 0000000 00000000000 14577507162 0020575 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/lib/carrierwave/storage/abstract.rb 0000664 0000000 0000000 00000002270 14577507162 0022726 0 ustar 00root root 0000000 0000000 module CarrierWave
module Storage
##
# This file serves mostly as a specification for Storage engines. There is no requirement
# that storage engines must be a subclass of this class.
#
class Abstract
attr_reader :uploader
def initialize(uploader)
@uploader = uploader
end
def identifier
uploader.deduplicated_filename
end
def store!(file)
end
def retrieve!(identifier)
end
def cache!(new_file)
raise NotImplementedError, "Need to implement #cache! if you want to use #{self.class.name} as a cache storage."
end
def retrieve_from_cache!(identifier)
raise NotImplementedError, "Need to implement #retrieve_from_cache! if you want to use #{self.class.name} as a cache storage."
end
def delete_dir!(path)
raise NotImplementedError, "Need to implement #delete_dir! if you want to use #{self.class.name} as a cache storage."
end
def clean_cache!(seconds)
raise NotImplementedError, "Need to implement #clean_cache! if you want to use #{self.class.name} as a cache storage."
end
end # Abstract
end # Storage
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/storage/file.rb 0000664 0000000 0000000 00000007277 14577507162 0022056 0 ustar 00root root 0000000 0000000 module CarrierWave
module Storage
##
# File storage stores file to the Filesystem (surprising, no?). There's really not much
# to it, it uses the store_dir defined on the uploader as the storage location. That's
# pretty much it.
#
class File < Abstract
def initialize(*)
super
@cache_called = nil
end
##
# Move the file to the uploader's store path.
#
# By default, store!() uses copy_to(), which operates by copying the file
# from the cache to the store, then deleting the file from the cache.
# If move_to_store() is overridden to return true, then store!() uses move_to(),
# which simply moves the file from cache to store. Useful for large files.
#
# === Parameters
#
# [file (CarrierWave::SanitizedFile)] the file to store
#
# === Returns
#
# [CarrierWave::SanitizedFile] a sanitized file
#
def store!(file)
path = ::File.expand_path(uploader.store_path, uploader.root)
if uploader.move_to_store
file.move_to(path, uploader.permissions, uploader.directory_permissions)
else
file.copy_to(path, uploader.permissions, uploader.directory_permissions)
end
end
##
# Retrieve the file from its store path
#
# === Parameters
#
# [identifier (String)] the filename of the file
#
# === Returns
#
# [CarrierWave::SanitizedFile] a sanitized file
#
def retrieve!(identifier)
path = ::File.expand_path(uploader.store_path(identifier), uploader.root)
CarrierWave::SanitizedFile.new(path)
end
##
# Stores given file to cache directory.
#
# === Parameters
#
# [new_file (File, IOString, Tempfile)] any kind of file object
#
# === Returns
#
# [CarrierWave::SanitizedFile] a sanitized file
#
def cache!(new_file)
new_file.move_to(::File.expand_path(uploader.cache_path, uploader.root), uploader.permissions, uploader.directory_permissions, true)
rescue Errno::EMLINK, Errno::ENOSPC => e
raise(e) if @cache_called
@cache_called = true
# NOTE: Remove cached files older than 10 minutes
clean_cache!(600)
cache!(new_file)
end
##
# Retrieves the file with the given cache_name from the cache.
#
# === Parameters
#
# [cache_name (String)] uniquely identifies a cache file
#
# === Raises
#
# [CarrierWave::InvalidParameter] if the cache_name is incorrectly formatted.
#
def retrieve_from_cache!(identifier)
CarrierWave::SanitizedFile.new(::File.expand_path(uploader.cache_path(identifier), uploader.root))
end
##
# Deletes a cache dir
#
def delete_dir!(path)
if path
begin
Dir.rmdir(::File.expand_path(path, uploader.root))
rescue Errno::ENOENT
# Ignore: path does not exist
rescue Errno::ENOTDIR
# Ignore: path is not a dir
rescue Errno::ENOTEMPTY, Errno::EEXIST
# Ignore: dir is not empty
end
end
end
def clean_cache!(seconds)
Dir.glob(::File.expand_path(::File.join(uploader.cache_dir, '*'), uploader.root)).each do |dir|
# generate_cache_id returns key formatted TIMEINT-PID(-COUNTER)-RND
matched = dir.scan(/(\d+)-\d+-\d+(?:-\d+)?/).first
next unless matched
time = Time.at(matched[0].to_i)
if time < (Time.now.utc - seconds)
FileUtils.rm_rf(dir)
end
end
end
end # File
end # Storage
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/storage/fog.rb 0000664 0000000 0000000 00000040421 14577507162 0021676 0 ustar 00root root 0000000 0000000 module CarrierWave
module Storage
##
# Stores things using the "fog" gem.
#
# fog supports storing files with AWS, Google, Local and Rackspace
#
# You need to setup some options to configure your usage:
#
# [:fog_credentials] host info and credentials for service
# [:fog_directory] specifies name of directory to store data in, assumed to already exist
#
# [:fog_attributes] (optional) additional attributes to set on files
# [:fog_public] (optional) public readability, defaults to true
# [:fog_authenticated_url_expiration] (optional) time (in seconds) that authenticated urls
# will be valid, when fog_public is false and provider is AWS or Google, defaults to 600
# [:fog_use_ssl_for_aws] (optional) #public_url will use https for the AWS generated URL]
# [:fog_aws_accelerate] (optional) #public_url will use s3-accelerate subdomain
# instead of s3, defaults to false
#
#
# AWS credentials contain the following keys:
#
# [:aws_access_key_id]
# [:aws_secret_access_key]
# [:region] (optional) defaults to 'us-east-1'
# :region should be one of ['eu-west-1', 'us-east-1', 'ap-southeast-1', 'us-west-1', 'ap-northeast-1', 'eu-central-1']
#
#
# Google credentials contain the following keys:
# [:google_storage_access_key_id]
# [:google_storage_secret_access_key]
#
#
# Local credentials contain the following keys:
#
# [:local_root] local path to files
#
#
# Rackspace credentials contain the following keys:
#
# [:rackspace_username]
# [:rackspace_api_key]
#
#
# A full example with AWS credentials:
# CarrierWave.configure do |config|
# config.fog_credentials = {
# :aws_access_key_id => 'xxxxxx',
# :aws_secret_access_key => 'yyyyyy',
# :provider => 'AWS'
# }
# config.fog_directory = 'directoryname'
# config.fog_public = true
# end
#
class Fog < Abstract
class << self
def connection_cache
@connection_cache ||= {}
end
def eager_load
# see #1198. This will hopefully no longer be necessary in future release of fog
fog_credentials = CarrierWave::Uploader::Base.fog_credentials
if fog_credentials.present?
CarrierWave::Storage::Fog.connection_cache[fog_credentials] ||= ::Fog::Storage.new(fog_credentials)
end
end
end
##
# Store a file
#
# === Parameters
#
# [file (CarrierWave::SanitizedFile)] the file to store
#
# === Returns
#
# [CarrierWave::Storage::Fog::File] the stored file
#
def store!(file)
f = CarrierWave::Storage::Fog::File.new(uploader, self, uploader.store_path)
f.store(file)
f
end
##
# Retrieve a file
#
# === Parameters
#
# [identifier (String)] unique identifier for file
#
# === Returns
#
# [CarrierWave::Storage::Fog::File] the stored file
#
def retrieve!(identifier)
CarrierWave::Storage::Fog::File.new(uploader, self, uploader.store_path(identifier))
end
##
# Stores given file to cache directory.
#
# === Parameters
#
# [new_file (File, IOString, Tempfile)] any kind of file object
#
# === Returns
#
# [CarrierWave::SanitizedFile] a sanitized file
#
def cache!(new_file)
f = CarrierWave::Storage::Fog::File.new(uploader, self, uploader.cache_path)
f.store(new_file)
f
end
##
# Retrieves the file with the given cache_name from the cache.
#
# === Parameters
#
# [cache_name (String)] uniquely identifies a cache file
#
# === Raises
#
# [CarrierWave::InvalidParameter] if the cache_name is incorrectly formatted.
#
def retrieve_from_cache!(identifier)
CarrierWave::Storage::Fog::File.new(uploader, self, uploader.cache_path(identifier))
end
##
# Deletes a cache dir
#
def delete_dir!(path)
# do nothing, because there's no such things as 'empty directory'
end
def clean_cache!(seconds)
connection.directories.new(
:key => uploader.fog_directory,
:public => uploader.fog_public
).files.all(:prefix => uploader.cache_dir).each do |file|
# generate_cache_id returns key formatted TIMEINT-PID(-COUNTER)-RND
matched = file.key.match(/(\d+)-\d+-\d+(?:-\d+)?/)
next unless matched
time = Time.at(matched[1].to_i)
file.destroy if time < (Time.now.utc - seconds)
end
end
def connection
@connection ||= begin
options = credentials = uploader.fog_credentials
self.class.connection_cache[credentials] ||= ::Fog::Storage.new(options)
end
end
class File
DEFAULT_S3_REGION = 'us-east-1'.freeze
include CarrierWave::Utilities::Uri
include CarrierWave::Utilities::FileName
##
# Current local path to file
#
# === Returns
#
# [String] a path to file
#
attr_reader :path
##
# Return all attributes from file
#
# === Returns
#
# [Hash] attributes from file
#
def attributes
file.attributes
end
##
# Return a temporary authenticated url to a private file, if available
# Only supported for AWS, Rackspace, Google, AzureRM and Aliyun providers
#
# === Returns
#
# [String] temporary authenticated url
# or
# [NilClass] no authenticated url available
#
def authenticated_url(options = {})
if ['AWS', 'Google', 'Rackspace', 'OpenStack', 'AzureRM', 'Aliyun', 'backblaze'].include?(fog_provider)
# avoid a get by using local references
local_directory = connection.directories.new(:key => @uploader.fog_directory)
local_file = local_directory.files.new(:key => path)
expire_at = options[:expire_at] || ::Fog::Time.now.since(@uploader.fog_authenticated_url_expiration.to_i)
case fog_provider
when 'AWS', 'Google'
# Older versions of fog-google do not support options as a parameter
if url_options_supported?(local_file)
local_file.url(expire_at, options)
else
warn "Options hash not supported in #{local_file.class}. You may need to upgrade your Fog provider."
local_file.url(expire_at)
end
when 'Rackspace', 'OpenStack'
connection.get_object_https_url(@uploader.fog_directory, path, expire_at, options)
when 'Aliyun'
expire_at -= Time.now
local_file.url(expire_at)
else
local_file.url(expire_at)
end
end
end
##
# Lookup value for file content-type header
#
# === Returns
#
# [String] value of content-type
#
def content_type
@content_type || file.try(:content_type)
end
##
# Set non-default content-type header (default is file.content_type)
#
# === Returns
#
# [String] returns new content type value
#
def content_type=(new_content_type)
@content_type = new_content_type
end
##
# Remove the file from service
#
# === Returns
#
# [Boolean] true for success or raises error
#
def delete
# avoid a get by just using local reference
directory.files.new(:key => path).destroy.tap do |result|
@file = nil if result
end
end
##
# deprecated: All attributes from file (includes headers)
#
# === Returns
#
# [Hash] attributes from file
#
def headers
location = caller.first
warning = "[yellow][WARN] headers is deprecated, use attributes instead[/]"
warning << " [light_black](#{location})[/]"
Formatador.display_line(warning)
attributes
end
def initialize(uploader, base, path)
@uploader, @base, @path, @content_type = uploader, base, path, nil
end
##
# Read content of file from service
#
# === Returns
#
# [String] contents of file
def read
file_body = file&.body
return if file_body.nil?
return file_body unless file_body.is_a?(::File)
# Fog::Storage::XXX::File#body could return the source file which was uploaded to the remote server.
return read_source_file if ::File.exist?(file_body.path)
# If the source file doesn't exist, the remote content is read
@file = nil
file.body
end
##
# Return size of file body
#
# === Returns
#
# [Integer] size of file body
#
def size
file.nil? ? 0 : file.content_length
end
##
# Check if the file exists on the remote service
#
# === Returns
#
# [Boolean] true if file exists or false
def exists?
!!file
end
##
# Write file to service
#
# === Returns
#
# [Boolean] true on success or raises error
def store(new_file)
if new_file.is_a?(self.class)
new_file.copy_to(path)
else
fog_file = new_file.to_file
@content_type ||= new_file.content_type
@file = directory.files.create({
:body => fog_file || new_file.read,
:content_type => @content_type,
:key => path,
:public => @uploader.fog_public
}.merge(@uploader.fog_attributes))
fog_file.close if fog_file && !fog_file.closed?
end
true
end
##
# Return a url to a public file, if available
#
# === Returns
#
# [String] public url
# or
# [NilClass] no public url available
#
def public_url
encoded_path = encode_path(path)
if (host = @uploader.asset_host)
if host.respond_to? :call
"#{host.call(self)}/#{encoded_path}"
else
"#{host}/#{encoded_path}"
end
else
# AWS/Google optimized for speed over correctness
case fog_provider
when 'AWS'
# check if some endpoint is set in fog_credentials
if @uploader.fog_credentials.has_key?(:endpoint)
"#{@uploader.fog_credentials[:endpoint]}/#{@uploader.fog_directory}/#{encoded_path}"
else
protocol = @uploader.fog_use_ssl_for_aws ? "https" : "http"
subdomain_regex = /^(?:[a-z]|\d(?!\d{0,2}(?:\d{1,3}){3}$))(?:[a-z0-9\.]|(?![\-])|\-(?![\.])){1,61}[a-z0-9]$/
# To use the virtual-hosted style, the bucket name needs to be representable as a subdomain
use_virtual_hosted_style = @uploader.fog_directory.to_s =~ subdomain_regex && !(protocol == 'https' && @uploader.fog_directory =~ /\./)
region = @uploader.fog_credentials[:region].to_s
regional_host = case region
when DEFAULT_S3_REGION, ''
's3.amazonaws.com'
else
"s3.#{region}.amazonaws.com"
end
if use_virtual_hosted_style
regional_host = 's3-accelerate.amazonaws.com' if @uploader.fog_aws_accelerate
"#{protocol}://#{@uploader.fog_directory}.#{regional_host}/#{encoded_path}"
else # directory is not a valid subdomain, so use path style for access
"#{protocol}://#{regional_host}/#{@uploader.fog_directory}/#{encoded_path}"
end
end
when 'Google'
# https://cloud.google.com/storage/docs/access-public-data
"https://storage.googleapis.com/#{@uploader.fog_directory}/#{encoded_path}"
else
# avoid a get by just using local reference
directory.files.new(:key => path).public_url
end
end
end
##
# Return url to file, if available
#
# === Returns
#
# [String] url
# or
# [NilClass] no url available
#
def url(options = {})
if !@uploader.fog_public
authenticated_url(options)
else
public_url
end
end
##
# Return file name, if available
#
# === Returns
#
# [String] file name
# or
# [NilClass] no file name available
#
def filename(options = {})
return unless (file_url = url(options))
CGI.unescape(file_url.split('?').first).gsub(/.*\/(.*?$)/, '\1')
end
##
# Creates a copy of this file and returns it.
#
# === Parameters
#
# [new_path (String)] The path where the file should be copied to.
#
# === Returns
#
# @return [CarrierWave::Storage::Fog::File] the location where the file will be stored.
#
def copy_to(new_path)
file.copy(@uploader.fog_directory, new_path, copy_options)
CarrierWave::Storage::Fog::File.new(@uploader, @base, new_path)
end
##
# Return the local file
#
# === Returns
#
# [File] The local file as Ruby's File class
# or
# [NilClass] When there's no file, or the file is remotely stored
#
def to_file
return nil unless file.body.is_a? ::File
if file.body.closed?
::File.open(file.body.path) # Reopen if it's already closed
else
file.body
end
end
private
##
# connection to service
#
# === Returns
#
# [Fog::#{provider}::Storage] connection to service
#
def connection
@base.connection
end
##
# local reference to directory containing file
#
# === Returns
#
# [Fog::#{provider}::Directory] containing directory
#
def directory
@directory ||= connection.directories.new(
:key => @uploader.fog_directory,
:public => @uploader.fog_public
)
end
##
# lookup file
#
# === Returns
#
# [Fog::#{provider}::File] file data from remote service
#
def file
@file ||= directory.files.head(path)
end
def copy_options
options = {}
options.merge!(acl_header) if acl_header.present?
options[fog_provider == "Google" ? :content_type : 'Content-Type'] ||= content_type if content_type
options.merge(@uploader.fog_attributes)
end
def acl_header
case fog_provider
when 'AWS'
{ 'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private' }
when "Google"
@uploader.fog_public ? { destination_predefined_acl: "publicRead" } : {}
else
{}
end
end
def fog_provider
@uploader.fog_credentials[:provider].to_s
end
def read_source_file
source_file = to_file
return unless source_file
begin
source_file.read
ensure
source_file.close
end
end
def url_options_supported?(local_file)
parameters = local_file.method(:url).parameters
parameters.count == 2 && parameters[1].include?(:options)
end
end
end # Fog
end # Storage
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/test/ 0000775 0000000 0000000 00000000000 14577507162 0020110 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/lib/carrierwave/test/matchers.rb 0000664 0000000 0000000 00000026610 14577507162 0022250 0 ustar 00root root 0000000 0000000 module CarrierWave
module Test
##
# These are some matchers that can be used in RSpec specs, to simplify the testing
# of uploaders.
#
module Matchers
class BeIdenticalTo # :nodoc:
def initialize(expected)
@expected = expected
end
def matches?(actual)
@actual = actual
FileUtils.identical?(@actual, @expected)
end
def failure_message
"expected #{@actual.inspect} to be identical to #{@expected.inspect}"
end
def failure_message_when_negated
"expected #{@actual.inspect} to not be identical to #{@expected.inspect}"
end
def description
"be identical to #{@expected.inspect}"
end
# RSpec 2 compatibility:
alias_method :negative_failure_message, :failure_message_when_negated
end
def be_identical_to(expected)
BeIdenticalTo.new(expected)
end
class HavePermissions # :nodoc:
def initialize(expected)
@expected = expected
end
def matches?(actual)
@actual = actual
# Satisfy expectation here. Return false or raise an error if it's not met.
(File.stat(@actual.path).mode & 0o777) == @expected
end
def failure_message
"expected #{@actual.current_path.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode & 0o777).to_s(8)}"
end
def failure_message_when_negated
"expected #{@actual.current_path.inspect} not to have permissions #{@expected.to_s(8)}, but it did"
end
def description
"have permissions #{@expected.to_s(8)}"
end
# RSpec 2 compatibility:
alias_method :negative_failure_message, :failure_message_when_negated
end
def have_permissions(expected)
HavePermissions.new(expected)
end
class HaveDirectoryPermissions # :nodoc:
def initialize(expected)
@expected = expected
end
def matches?(actual)
@actual = actual
# Satisfy expectation here. Return false or raise an error if it's not met.
(File.stat(File.dirname(@actual.path)).mode & 0o777) == @expected
end
def failure_message
"expected #{File.dirname @actual.current_path.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode & 0o777).to_s(8)}"
end
def failure_message_when_negated
"expected #{File.dirname @actual.current_path.inspect} not to have permissions #{@expected.to_s(8)}, but it did"
end
def description
"have permissions #{@expected.to_s(8)}"
end
# RSpec 2 compatibility:
alias_method :negative_failure_message, :failure_message_when_negated
end
def have_directory_permissions(expected)
HaveDirectoryPermissions.new(expected)
end
class BeNoLargerThan # :nodoc:
def initialize(width, height)
@width, @height = width, height
end
def matches?(actual)
@actual = actual
# Satisfy expectation here. Return false or raise an error if it's not met.
image = ImageLoader.load_image(@actual.current_path)
@actual_width = image.width
@actual_height = image.height
@actual_width <= @width && @actual_height <= @height
end
def failure_message
"expected #{@actual.current_path.inspect} to be no larger than #{@width} by #{@height}, but it was #{@actual_width} by #{@actual_height}."
end
def failure_message_when_negated
"expected #{@actual.current_path.inspect} to be larger than #{@width} by #{@height}, but it wasn't."
end
def description
"be no larger than #{@width} by #{@height}"
end
# RSpec 2 compatibility:
alias_method :negative_failure_message, :failure_message_when_negated
end
def be_no_larger_than(width, height)
BeNoLargerThan.new(width, height)
end
class HaveDimensions # :nodoc:
def initialize(width, height)
@width, @height = width, height
end
def matches?(actual)
@actual = actual
# Satisfy expectation here. Return false or raise an error if it's not met.
image = ImageLoader.load_image(@actual.current_path)
@actual_width = image.width
@actual_height = image.height
@actual_width == @width && @actual_height == @height
end
def failure_message
"expected #{@actual.current_path.inspect} to have an exact size of #{@width} by #{@height}, but it was #{@actual_width} by #{@actual_height}."
end
def failure_message_when_negated
"expected #{@actual.current_path.inspect} not to have an exact size of #{@width} by #{@height}, but it did."
end
def description
"have an exact size of #{@width} by #{@height}"
end
# RSpec 2 compatibility:
alias_method :negative_failure_message, :failure_message_when_negated
end
def have_dimensions(width, height)
HaveDimensions.new(width, height)
end
class HaveHeight # :nodoc:
def initialize(height)
@height = height
end
def matches?(actual)
@actual = actual
# Satisfy expectation here. Return false or raise an error if it's not met.
image = ImageLoader.load_image(@actual.current_path)
@actual_height = image.height
@actual_height == @height
end
def failure_message
"expected #{@actual.current_path.inspect} to have an exact size of #{@height}, but it was #{@actual_height}."
end
def failure_message_when_negated
"expected #{@actual.current_path.inspect} not to have an exact size of #{@height}, but it did."
end
def description
"have an exact height of #{@height}"
end
# RSpec 2 compatibility:
alias_method :negative_failure_message, :failure_message_when_negated
end
def have_height(height)
HaveHeight.new(height)
end
class HaveWidth # :nodoc:
def initialize(width)
@width = width
end
def matches?(actual)
@actual = actual
# Satisfy expectation here. Return false or raise an error if it's not met.
image = ImageLoader.load_image(@actual.current_path)
@actual_width = image.width
@actual_width == @width
end
def failure_message
"expected #{@actual.current_path.inspect} to have an exact size of #{@width}, but it was #{@actual_width}."
end
def failure_message_when_negated
"expected #{@actual.current_path.inspect} not to have an exact size of #{@width}, but it did."
end
def description
"have an exact width of #{@width}"
end
# RSpec 2 compatibility:
alias_method :negative_failure_message, :failure_message_when_negated
end
def have_width(width)
HaveWidth.new(width)
end
class BeNoWiderThan # :nodoc:
def initialize(width)
@width = width
end
def matches?(actual)
@actual = actual
# Satisfy expectation here. Return false or raise an error if it's not met.
image = ImageLoader.load_image(@actual.current_path)
@actual_width = image.width
@actual_width <= @width
end
def failure_message
"expected #{@actual.current_path.inspect} to be no wider than #{@width}, but it was #{@actual_width}."
end
def failure_message_when_negated
"expected #{@actual.current_path.inspect} not to be wider than #{@width}, but it is."
end
def description
"have a width less than or equal to #{@width}"
end
# RSpec 2 compatibility:
alias_method :negative_failure_message, :failure_message_when_negated
end
def be_no_wider_than(width)
BeNoWiderThan.new(width)
end
class BeNoTallerThan # :nodoc:
def initialize(height)
@height = height
end
def matches?(actual)
@actual = actual
# Satisfy expectation here. Return false or raise an error if it's not met.
image = ImageLoader.load_image(@actual.current_path)
@actual_height = image.height
@actual_height <= @height
end
def failure_message
"expected #{@actual.current_path.inspect} to be no taller than #{@height}, but it was #{@actual_height}."
end
def failure_message_when_negated
"expected #{@actual.current_path.inspect} not to be taller than #{@height}, but it is."
end
def description
"have a height less than or equal to #{@height}"
end
# RSpec 2 compatibility:
alias_method :negative_failure_message, :failure_message_when_negated
end
def be_no_taller_than(height)
BeNoTallerThan.new(height)
end
class BeFormat # :nodoc:
def initialize(expected)
@expected = expected
end
def matches?(actual)
@actual = actual
# Satisfy expectation here. Return false or raise an error if it's not met.
image = ImageLoader.load_image(@actual.current_path)
@actual_expected = image.format
!@expected.nil? && @actual_expected.casecmp(@expected).zero?
end
def failure_message
"expected #{@actual.current_path.inspect} to have #{@expected} format, but it was #{@actual_expected}."
end
def failure_message_when_negated
"expected #{@actual.current_path.inspect} not to have #{@expected} format, but it did."
end
def description
"have #{@expected} format"
end
# RSpec 2 compatibility:
alias_method :negative_failure_message, :failure_message_when_negated
end
def be_format(expected)
BeFormat.new(expected)
end
class ImageLoader # :nodoc:
def self.load_image(filename)
if defined? ::MiniMagick
MiniMagickWrapper.new(filename)
else
unless defined? ::Magick
begin
require 'rmagick'
rescue LoadError
begin
require 'RMagick'
rescue LoadError
puts "WARNING: Failed to require rmagick, image processing may fail!"
end
end
end
MagickWrapper.new(filename)
end
end
end
class MagickWrapper # :nodoc:
attr_reader :image
def width
image.columns
end
def height
image.rows
end
def format
image.format
end
def initialize(filename)
@image = ::Magick::Image.read(filename).first
end
end
class MiniMagickWrapper # :nodoc:
attr_reader :image
def width
image[:width]
end
def height
image[:height]
end
def format
image[:format]
end
def initialize(filename)
@image = ::MiniMagick::Image.open(filename)
end
end
end # Matchers
end # Test
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/uploader.rb 0000664 0000000 0000000 00000005052 14577507162 0021273 0 ustar 00root root 0000000 0000000 require "carrierwave/uploader/configuration"
require "carrierwave/uploader/callbacks"
require "carrierwave/uploader/proxy"
require "carrierwave/uploader/url"
require "carrierwave/uploader/mountable"
require "carrierwave/uploader/cache"
require "carrierwave/uploader/store"
require "carrierwave/uploader/download"
require "carrierwave/uploader/remove"
require "carrierwave/uploader/extension_allowlist"
require "carrierwave/uploader/extension_denylist"
require "carrierwave/uploader/content_type_allowlist"
require "carrierwave/uploader/content_type_denylist"
require "carrierwave/uploader/file_size"
require "carrierwave/uploader/dimension"
require "carrierwave/uploader/processing"
require "carrierwave/uploader/versions"
require "carrierwave/uploader/default_url"
require "carrierwave/uploader/serialization"
module CarrierWave
##
# See CarrierWave::Uploader::Base
#
module Uploader
##
# An uploader is a class that allows you to easily handle the caching and storage of
# uploaded files. Please refer to the README for configuration options.
#
# Once you have an uploader you can use it in isolation:
#
# my_uploader = MyUploader.new
# my_uploader.cache!(File.open(path_to_file))
# my_uploader.retrieve_from_store!('monkey.png')
#
# Alternatively, you can mount it on an ORM or other persistence layer, with
# +CarrierWave::Mount#mount_uploader+. There are extensions for activerecord and datamapper
# these are *very* simple (they are only a dozen lines of code), so adding your own should
# be trivial.
#
class Base
attr_reader :file
include CarrierWave::Uploader::Configuration
include CarrierWave::Uploader::Callbacks
include CarrierWave::Uploader::Proxy
include CarrierWave::Uploader::Url
include CarrierWave::Uploader::Mountable
include CarrierWave::Uploader::Cache
include CarrierWave::Uploader::Store
include CarrierWave::Uploader::Download
include CarrierWave::Uploader::Remove
include CarrierWave::Uploader::ExtensionAllowlist
include CarrierWave::Uploader::ExtensionDenylist
include CarrierWave::Uploader::ContentTypeAllowlist
include CarrierWave::Uploader::ContentTypeDenylist
include CarrierWave::Uploader::FileSize
include CarrierWave::Uploader::Dimension
include CarrierWave::Uploader::Processing
include CarrierWave::Uploader::Versions
include CarrierWave::Uploader::DefaultUrl
include CarrierWave::Uploader::Serialization
end # Base
end # Uploader
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/uploader/ 0000775 0000000 0000000 00000000000 14577507162 0020744 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/lib/carrierwave/uploader/cache.rb 0000664 0000000 0000000 00000014642 14577507162 0022343 0 ustar 00root root 0000000 0000000 require 'securerandom'
module CarrierWave
class FormNotMultipart < UploadError
def message
"You tried to assign a String or a Pathname to an uploader, for security reasons, this is not allowed.\n\n If this is a file upload, please check that your upload form is multipart encoded."
end
end
class CacheCounter
@@counter = 0
def self.increment
@@counter += 1
end
end
##
# Generates a unique cache id for use in the caching system
#
# === Returns
#
# [String] a cache id in the format TIMEINT-PID-COUNTER-RND
#
def self.generate_cache_id
[
Time.now.utc.to_i,
SecureRandom.random_number(1_000_000_000_000_000),
'%04d' % (CarrierWave::CacheCounter.increment % 10_000),
'%04d' % SecureRandom.random_number(10_000)
].map(&:to_s).join('-')
end
module Uploader
module Cache
extend ActiveSupport::Concern
include CarrierWave::Uploader::Callbacks
include CarrierWave::Uploader::Configuration
included do
prepend Module.new {
def initialize(*)
super
@staged = false
end
}
attr_accessor :staged
end
module ClassMethods
##
# Removes cached files which are older than one day. You could call this method
# from a rake task to clean out old cached files.
#
# You can call this method directly on the module like this:
#
# CarrierWave.clean_cached_files!
#
# === Note
#
# This only works as long as you haven't done anything funky with your cache_dir.
# It's recommended that you keep cache files in one place only.
#
def clean_cached_files!(seconds=60*60*24)
(cache_storage || storage).new(new).clean_cache!(seconds)
end
end
##
# Returns true if the uploader has been cached
#
# === Returns
#
# [Bool] whether the current file is cached
#
def cached?
!!@cache_id
end
##
# Caches the remotely stored file
#
# This is useful when about to process images. Most processing solutions
# require the file to be stored on the local filesystem.
#
def cache_stored_file!
cache!
end
def sanitized_file
ActiveSupport::Deprecation.warn('#sanitized_file is deprecated, use #file instead.')
file
end
##
# Returns a String which uniquely identifies the currently cached file for later retrieval
#
# === Returns
#
# [String] a cache name, in the format TIMEINT-PID-COUNTER-RND/filename.txt
#
def cache_name
File.join(cache_id, original_filename) if cache_id && original_filename
end
##
# Caches the given file. Calls process! to trigger any process callbacks.
#
# By default, cache!() uses copy_to(), which operates by copying the file
# to the cache, then deleting the original file. If move_to_cache() is
# overridden to return true, then cache!() uses move_to(), which simply
# moves the file to the cache. Useful for large files.
#
# === Parameters
#
# [new_file (File, IOString, Tempfile)] any kind of file object
#
# === Raises
#
# [CarrierWave::FormNotMultipart] if the assigned parameter is a string
#
def cache!(new_file = file)
new_file = CarrierWave::SanitizedFile.new(new_file)
return if new_file.empty?
raise CarrierWave::FormNotMultipart if new_file.is_path? && ensure_multipart_form
self.cache_id = CarrierWave.generate_cache_id unless cache_id
@identifier = nil
@staged = true
@filename = new_file.filename
self.original_filename = new_file.filename
begin
# first, create a workfile on which we perform processings
if move_to_cache
@file = new_file.move_to(File.expand_path(workfile_path, root), permissions, directory_permissions)
else
@file = new_file.copy_to(File.expand_path(workfile_path, root), permissions, directory_permissions)
end
with_callbacks(:cache, @file) do
@file = cache_storage.cache!(@file)
end
ensure
FileUtils.rm_rf(workfile_path(''))
end
end
##
# Retrieves the file with the given cache_name from the cache.
#
# === Parameters
#
# [cache_name (String)] uniquely identifies a cache file
#
# === Raises
#
# [CarrierWave::InvalidParameter] if the cache_name is incorrectly formatted.
#
def retrieve_from_cache!(cache_name)
with_callbacks(:retrieve_from_cache, cache_name) do
self.cache_id, self.original_filename = cache_name.to_s.split('/', 2)
@staged = true
@filename = original_filename
@file = cache_storage.retrieve_from_cache!(full_original_filename)
end
end
##
# Calculates the path where the cache file should be stored.
#
# === Parameters
#
# [for_file (String)] name of the file
#
# === Returns
#
# [String] the cache path
#
def cache_path(for_file=full_original_filename)
File.join(*[cache_dir, @cache_id, for_file].compact)
end
protected
attr_reader :cache_id
private
def workfile_path(for_file=original_filename)
File.join(CarrierWave.tmp_path, @cache_id, version_name.to_s, for_file)
end
attr_reader :original_filename
def cache_id=(cache_id)
# Earlier version used 3 part cache_id. Thus we should allow for
# the cache_id to have both 3 part and 4 part formats.
raise CarrierWave::InvalidParameter, "invalid cache id" unless cache_id =~ /\A(-)?[\d]+\-[\d]+(\-[\d]{4})?\-[\d]{4}\z/
@cache_id = cache_id
end
def original_filename=(filename)
raise CarrierWave::InvalidParameter, "invalid filename" if filename =~ CarrierWave::SanitizedFile.sanitize_regexp
@original_filename = filename
end
def cache_storage
@cache_storage ||= (self.class.cache_storage || self.class.storage).new(self)
end
# We can override the full_original_filename method in other modules
def full_original_filename
forcing_extension(original_filename)
end
end # Cache
end # Uploader
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/uploader/callbacks.rb 0000664 0000000 0000000 00000001712 14577507162 0023211 0 ustar 00root root 0000000 0000000 module CarrierWave
module Uploader
module Callbacks
extend ActiveSupport::Concern
included do
class_attribute :_before_callbacks, :_after_callbacks,
:instance_writer => false
self._before_callbacks = Hash.new []
self._after_callbacks = Hash.new []
end
def with_callbacks(kind, *args)
self.class._before_callbacks[kind].each { |c| send c, *args }
yield
self.class._after_callbacks[kind].each { |c| send c, *args }
end
module ClassMethods
def before(kind, callback)
self._before_callbacks = self._before_callbacks.
merge kind => _before_callbacks[kind] + [callback]
end
def after(kind, callback)
self._after_callbacks = self._after_callbacks.
merge kind => _after_callbacks[kind] + [callback]
end
end # ClassMethods
end # Callbacks
end # Uploader
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/uploader/configuration.rb 0000664 0000000 0000000 00000016777 14577507162 0024162 0 ustar 00root root 0000000 0000000 require 'carrierwave/downloader/base'
module CarrierWave
module Uploader
module Configuration
extend ActiveSupport::Concern
included do
class_attribute :_storage, :_cache_storage, :instance_writer => false
add_config :root
add_config :base_path
add_config :asset_host
add_config :permissions
add_config :directory_permissions
add_config :storage_engines
add_config :store_dir
add_config :cache_dir
add_config :enable_processing
add_config :ensure_multipart_form
add_config :delete_tmp_file_after_storage
add_config :move_to_cache
add_config :move_to_store
add_config :remove_previously_stored_files_after_update
add_config :downloader
add_config :force_extension
# fog
add_deprecated_config :fog_provider
add_config :fog_attributes
add_config :fog_credentials
add_config :fog_directory
add_config :fog_public
add_config :fog_authenticated_url_expiration
add_config :fog_use_ssl_for_aws
add_config :fog_aws_accelerate
# Mounting
add_config :ignore_integrity_errors
add_config :ignore_processing_errors
add_config :ignore_download_errors
add_config :validate_integrity
add_config :validate_processing
add_config :validate_download
add_config :mount_on
add_config :cache_only
add_config :download_retry_count
add_config :download_retry_wait_time
# set default values
reset_config
end
module ClassMethods
##
# Sets the storage engine to be used when storing files with this uploader.
# Can be any class that implements a #store!(CarrierWave::SanitizedFile) and a #retrieve!
# method. See lib/carrierwave/storage/file.rb for an example. Storage engines should
# be added to CarrierWave::Uploader::Base.storage_engines so they can be referred
# to by a symbol, which should be more convenient
#
# If no argument is given, it will simply return the currently used storage engine.
#
# === Parameters
#
# [storage (Symbol, Class)] The storage engine to use for this uploader
#
# === Returns
#
# [Class] the storage engine to be used with this uploader
#
# === Examples
#
# storage :file
# storage CarrierWave::Storage::File
# storage MyCustomStorageEngine
#
def storage(storage = nil)
case storage
when Symbol
if (storage_engine = storage_engines[storage])
self._storage = eval storage_engine
else
raise CarrierWave::UnknownStorageError, "Unknown storage: #{storage}"
end
when nil
storage
else
self._storage = storage
end
_storage
end
alias_method :storage=, :storage
##
# Sets the cache storage engine to be used when storing cache files with this uploader.
# Same as .storage except for required methods being #cache!(CarrierWave::SanitizedFile),
# #retrieve_from_cache! and #delete_dir!.
#
# === Parameters
#
# [storage (Symbol, Class)] The cache storage engine to use for this uploader
#
# === Returns
#
# [Class] the cache storage engine to be used with this uploader
#
# === Examples
#
# cache_storage :file
# cache_storage CarrierWave::Storage::File
# cache_storage MyCustomStorageEngine
#
def cache_storage(storage = false)
unless storage == false
self._cache_storage = storage.is_a?(Symbol) ? eval(storage_engines[storage]) : storage
end
_cache_storage
end
alias_method :cache_storage=, :cache_storage
def add_config(name)
class_eval <<-RUBY, __FILE__, __LINE__ + 1
@#{name} = nil
def self.#{name}(value=nil)
@#{name} = value unless value.nil?
return @#{name} if self.object_id == #{self.object_id} || defined?(@#{name})
name = superclass.#{name}
return nil if name.nil? && !instance_variable_defined?(:@#{name})
@#{name} = name && !name.is_a?(Module) && !name.is_a?(Symbol) && !name.is_a?(Numeric) && !name.is_a?(TrueClass) && !name.is_a?(FalseClass) ? name.dup : name
end
def self.#{name}=(value)
@#{name} = value
end
def #{name}=(value)
@#{name} = value
end
def #{name}
value = @#{name} if instance_variable_defined?(:@#{name})
value = self.class.#{name} unless instance_variable_defined?(:@#{name})
if value.instance_of?(Proc)
value.arity >= 1 ? value.call(self) : value.call
else
value
end
end
RUBY
end
def add_deprecated_config(name)
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def self.#{name}(value=nil)
ActiveSupport::Deprecation.warn "##{name} is deprecated and has no effect"
end
def self.#{name}=(value)
ActiveSupport::Deprecation.warn "##{name} is deprecated and has no effect"
end
def #{name}=(value)
ActiveSupport::Deprecation.warn "##{name} is deprecated and has no effect"
end
def #{name}
ActiveSupport::Deprecation.warn "##{name} is deprecated and has no effect"
end
RUBY
end
def configure
yield self
end
##
# sets configuration back to default
#
def reset_config
configure do |config|
config.permissions = 0o644
config.directory_permissions = 0o755
config.storage_engines = {
:file => "CarrierWave::Storage::File",
:fog => "CarrierWave::Storage::Fog"
}
config.storage = :file
config.cache_storage = nil
config.fog_attributes = {}
config.fog_credentials = {}
config.fog_public = true
config.fog_authenticated_url_expiration = 600
config.fog_use_ssl_for_aws = true
config.fog_aws_accelerate = false
config.store_dir = 'uploads'
config.cache_dir = 'uploads/tmp'
config.delete_tmp_file_after_storage = true
config.move_to_cache = false
config.move_to_store = false
config.remove_previously_stored_files_after_update = true
config.downloader = CarrierWave::Downloader::Base
config.force_extension = false
config.ignore_integrity_errors = true
config.ignore_processing_errors = true
config.ignore_download_errors = true
config.validate_integrity = true
config.validate_processing = true
config.validate_download = true
config.root = lambda { CarrierWave.root }
config.base_path = CarrierWave.base_path
config.enable_processing = true
config.ensure_multipart_form = true
config.download_retry_count = 0
config.download_retry_wait_time = 5
end
end
end
end
end
end
carrierwave-3.0.7/lib/carrierwave/uploader/content_type_allowlist.rb 0000664 0000000 0000000 00000004130 14577507162 0026074 0 ustar 00root root 0000000 0000000 module CarrierWave
module Uploader
module ContentTypeAllowlist
extend ActiveSupport::Concern
included do
before :cache, :check_content_type_allowlist!
end
##
# Override this method in your uploader to provide an allowlist of files content types
# which are allowed to be uploaded.
# Not only strings but Regexp are allowed as well.
#
# === Returns
#
# [NilClass, String, Regexp, Array[String, Regexp]] an allowlist of content types which are allowed to be uploaded
#
# === Examples
#
# def content_type_allowlist
# %w(text/json application/json)
# end
#
# Basically the same, but using a Regexp:
#
# def content_type_allowlist
# [/(text|application)\/json/]
# end
#
def content_type_allowlist
end
private
def check_content_type_allowlist!(new_file)
allowlist = content_type_allowlist
if !allowlist && respond_to?(:content_type_whitelist) && content_type_whitelist
ActiveSupport::Deprecation.warn "#content_type_whitelist is deprecated, use #content_type_allowlist instead." unless instance_variable_defined?(:@content_type_whitelist_warned)
@content_type_whitelist_warned = true
allowlist = content_type_whitelist
end
return unless allowlist
content_type = new_file.content_type
if !allowlisted_content_type?(allowlist, content_type)
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_allowlist_error", content_type: content_type,
allowed_types: Array(allowlist).join(", "), default: :"errors.messages.content_type_whitelist_error")
end
end
def allowlisted_content_type?(allowlist, content_type)
Array(allowlist).any? do |item|
item = Regexp.quote(item) if item.class != Regexp
content_type =~ /\A#{item}/
end
end
end # ContentTypeAllowlist
end # Uploader
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/uploader/content_type_denylist.rb 0000664 0000000 0000000 00000004370 14577507162 0025723 0 ustar 00root root 0000000 0000000 module CarrierWave
module Uploader
module ContentTypeDenylist
extend ActiveSupport::Concern
included do
before :cache, :check_content_type_denylist!
end
##
# Override this method in your uploader to provide a denylist of files content types
# which are not allowed to be uploaded.
# Not only strings but Regexp are allowed as well.
#
# === Returns
#
# [NilClass, String, Regexp, Array[String, Regexp]] a denylist of content types which are not allowed to be uploaded
#
# === Examples
#
# def content_type_denylist
# %w(text/json application/json)
# end
#
# Basically the same, but using a Regexp:
#
# def content_type_denylist
# [/(text|application)\/json/]
# end
#
def content_type_denylist
end
private
def check_content_type_denylist!(new_file)
denylist = content_type_denylist
if !denylist && respond_to?(:content_type_blacklist) && content_type_blacklist
ActiveSupport::Deprecation.warn "#content_type_blacklist is deprecated, use #content_type_denylist instead." unless instance_variable_defined?(:@content_type_blacklist_warned)
@content_type_blacklist_warned = true
denylist = content_type_blacklist
end
return unless denylist
ActiveSupport::Deprecation.warn "Use of #content_type_denylist is deprecated for the security reason, use #content_type_allowlist instead to explicitly state what are safe to accept" unless instance_variable_defined?(:@content_type_denylist_warned)
@content_type_denylist_warned = true
content_type = new_file.content_type
if denylisted_content_type?(denylist, content_type)
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_denylist_error",
content_type: content_type, default: :"errors.messages.content_type_blacklist_error")
end
end
def denylisted_content_type?(denylist, content_type)
Array(denylist).any? { |item| content_type =~ /#{item}/ }
end
end # ContentTypeDenylist
end # Uploader
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/uploader/default_url.rb 0000664 0000000 0000000 00000000550 14577507162 0023577 0 ustar 00root root 0000000 0000000 module CarrierWave
module Uploader
module DefaultUrl
def url(*args)
super || default_url(*args)
end
##
# Override this method in your uploader to provide a default url
# in case no file has been cached/stored yet.
#
def default_url(*args); end
end # DefaultPath
end # Uploader
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/uploader/dimension.rb 0000664 0000000 0000000 00000004516 14577507162 0023264 0 ustar 00root root 0000000 0000000 require 'active_support'
module CarrierWave
module Uploader
module Dimension
extend ActiveSupport::Concern
included do
before :cache, :check_dimensions!
end
##
# Override this method in your uploader to provide a Range of width which
# are allowed to be uploaded.
# === Returns
#
# [NilClass, Range] a width range which are permitted to be uploaded
#
# === Examples
#
# def width_range
# 1000..2000
# end
#
def width_range; end
##
# Override this method in your uploader to provide a Range of height which
# are allowed to be uploaded.
# === Returns
#
# [NilClass, Range] a height range which are permitted to be uploaded
#
# === Examples
#
# def height_range
# 1000..
# end
#
def height_range; end
private
def check_dimensions!(new_file)
# NOTE: Skip the check for resized images
return if version_name.present?
return unless width_range || height_range
unless respond_to?(:width) || respond_to?(:height)
raise 'You need to include one of CarrierWave::MiniMagick, CarrierWave::RMagick, or CarrierWave::Vips to perform image dimension validation'
end
if width_range&.begin && width < width_range.begin
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.min_width_error", :min_width => ActiveSupport::NumberHelper.number_to_delimited(width_range.begin))
elsif width_range&.end && width > width_range.end
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.max_width_error", :max_width => ActiveSupport::NumberHelper.number_to_delimited(width_range.end))
elsif height_range&.begin && height < height_range.begin
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.min_height_error", :min_height => ActiveSupport::NumberHelper.number_to_delimited(height_range.begin))
elsif height_range&.end && height > height_range.end
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.max_height_error", :max_height => ActiveSupport::NumberHelper.number_to_delimited(height_range.end))
end
end
end # Dimension
end # Uploader
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/uploader/download.rb 0000664 0000000 0000000 00000001250 14577507162 0023076 0 ustar 00root root 0000000 0000000 module CarrierWave
module Uploader
module Download
extend ActiveSupport::Concern
include CarrierWave::Uploader::Callbacks
include CarrierWave::Uploader::Configuration
include CarrierWave::Uploader::Cache
##
# Caches the file by downloading it from the given URL, using downloader.
#
# === Parameters
#
# [url (String)] The URL where the remote file is stored
# [remote_headers (Hash)] Request headers
#
def download!(uri, remote_headers = {})
file = downloader.new(self).download(uri, remote_headers)
cache!(file)
end
end # Download
end # Uploader
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/uploader/extension_allowlist.rb 0000664 0000000 0000000 00000004404 14577507162 0025401 0 ustar 00root root 0000000 0000000 module CarrierWave
module Uploader
module ExtensionAllowlist
extend ActiveSupport::Concern
included do
before :cache, :check_extension_allowlist!
end
##
# Override this method in your uploader to provide an allowlist of extensions which
# are allowed to be uploaded. Compares the file's extension case insensitive.
# Furthermore, not only strings but Regexp are allowed as well.
#
# When using a Regexp in the allowlist, `\A` and `\z` are automatically added to
# the Regexp expression, also case insensitive.
#
# === Returns
#
# [NilClass, String, Regexp, Array[String, Regexp]] an allowlist of extensions which are allowed to be uploaded
#
# === Examples
#
# def extension_allowlist
# %w(jpg jpeg gif png)
# end
#
# Basically the same, but using a Regexp:
#
# def extension_allowlist
# [/jpe?g/, 'gif', 'png']
# end
#
def extension_allowlist
end
private
def check_extension_allowlist!(new_file)
allowlist = extension_allowlist
if !allowlist && respond_to?(:extension_whitelist) && extension_whitelist
ActiveSupport::Deprecation.warn "#extension_whitelist is deprecated, use #extension_allowlist instead." unless instance_variable_defined?(:@extension_whitelist_warned)
@extension_whitelist_warned = true
allowlist = extension_whitelist
end
return unless allowlist
extension = new_file.extension.to_s
if !allowlisted_extension?(allowlist, extension)
# Look for whitelist first, then fallback to allowlist
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.extension_allowlist_error", extension: new_file.extension.inspect,
allowed_types: Array(allowlist).join(", "), default: :"errors.messages.extension_whitelist_error")
end
end
def allowlisted_extension?(allowlist, extension)
downcase_extension = extension.downcase
Array(allowlist).any? { |item| downcase_extension =~ /\A#{item}\z/i }
end
end # ExtensionAllowlist
end # Uploader
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/uploader/extension_denylist.rb 0000664 0000000 0000000 00000004543 14577507162 0025226 0 ustar 00root root 0000000 0000000 module CarrierWave
module Uploader
module ExtensionDenylist
extend ActiveSupport::Concern
included do
before :cache, :check_extension_denylist!
end
##
# Override this method in your uploader to provide a denylist of extensions which
# are prohibited to be uploaded. Compares the file's extension case insensitive.
# Furthermore, not only strings but Regexp are allowed as well.
#
# When using a Regexp in the denylist, `\A` and `\z` are automatically added to
# the Regexp expression, also case insensitive.
#
# === Returns
# [NilClass, String, Regexp, Array[String, Regexp]] a deny list of extensions which are prohibited to be uploaded
#
# === Examples
#
# def extension_denylist
# %w(swf tiff)
# end
#
# Basically the same, but using a Regexp:
#
# def extension_denylist
# [/swf/, 'tiff']
# end
#
def extension_denylist
end
private
def check_extension_denylist!(new_file)
denylist = extension_denylist
if !denylist && respond_to?(:extension_blacklist) && extension_blacklist
ActiveSupport::Deprecation.warn "#extension_blacklist is deprecated, use #extension_denylist instead." unless instance_variable_defined?(:@extension_blacklist_warned)
@extension_blacklist_warned = true
denylist = extension_blacklist
end
return unless denylist
ActiveSupport::Deprecation.warn "Use of #extension_denylist is deprecated for the security reason, use #extension_allowlist instead to explicitly state what are safe to accept" unless instance_variable_defined?(:@extension_denylist_warned)
@extension_denylist_warned = true
extension = new_file.extension.to_s
if denylisted_extension?(denylist, extension)
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.extension_denylist_error", extension: new_file.extension.inspect,
prohibited_types: Array(extension_denylist).join(", "), default: :"errors.messages.extension_blacklist_error")
end
end
def denylisted_extension?(denylist, extension)
Array(denylist).any? { |item| extension =~ /\A#{item}\z/i }
end
end
end
end
carrierwave-3.0.7/lib/carrierwave/uploader/file_size.rb 0000664 0000000 0000000 00000002374 14577507162 0023250 0 ustar 00root root 0000000 0000000 require 'active_support'
module CarrierWave
module Uploader
module FileSize
extend ActiveSupport::Concern
included do
before :cache, :check_size!
end
##
# Override this method in your uploader to provide a Range of Size which
# are allowed to be uploaded.
# === Returns
#
# [NilClass, Range] a size range (in bytes) which are permitted to be uploaded
#
# === Examples
#
# def size_range
# 3256...5748
# end
#
def size_range; end
private
def check_size!(new_file)
size = new_file.size
expected_size_range = size_range
if expected_size_range.is_a?(::Range)
if size < expected_size_range.min
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.min_size_error", :min_size => ActiveSupport::NumberHelper.number_to_human_size(expected_size_range.min))
elsif size > expected_size_range.max
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.max_size_error", :max_size => ActiveSupport::NumberHelper.number_to_human_size(expected_size_range.max))
end
end
end
end # FileSize
end # Uploader
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/uploader/mountable.rb 0000664 0000000 0000000 00000002541 14577507162 0023261 0 ustar 00root root 0000000 0000000 module CarrierWave
module Uploader
module Mountable
attr_reader :model, :mounted_as
##
# If a model is given as the first parameter, it will be stored in the
# uploader, and available through +#model+. Likewise, mounted_as stores
# the name of the column where this instance of the uploader is mounted.
# These values can then be used inside your uploader.
#
# If you do not wish to mount your uploaders with the ORM extensions in
# -more then you can override this method inside your uploader. Just be
# sure to call +super+
#
# === Parameters
#
# [model (Object)] Any kind of model object
# [mounted_as (Symbol)] The name of the column where this uploader is mounted
#
# === Examples
#
# class MyUploader < CarrierWave::Uploader::Base
#
# def store_dir
# File.join('public', 'files', mounted_as, model.permalink)
# end
# end
#
def initialize(model=nil, mounted_as=nil)
@model = model
@mounted_as = mounted_as
end
##
# Returns array index of given uploader within currently mounted uploaders
#
def index
model.__send__(:_mounter, mounted_as).uploaders.index(self)
end
end # Mountable
end # Uploader
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/uploader/processing.rb 0000664 0000000 0000000 00000007557 14577507162 0023463 0 ustar 00root root 0000000 0000000 module CarrierWave
module Uploader
module Processing
extend ActiveSupport::Concern
include CarrierWave::Uploader::Callbacks
included do
class_attribute :processors, :instance_writer => false
self.processors = []
before :cache, :process!
end
module ClassMethods
##
# Adds a processor callback which applies operations as a file is uploaded.
# The argument may be the name of any method of the uploader, expressed as a symbol,
# or a list of such methods, or a hash where the key is a method and the value is
# an array of arguments to call the method with. Also accepts an :if or :unless condition
#
# === Parameters
#
# args (*Symbol, Hash{Symbol => Array[]})
#
# === Examples
#
# class MyUploader < CarrierWave::Uploader::Base
#
# process :sepiatone, :vignette
# process :scale => [200, 200]
# process :scale => [200, 200], :if => :image?
# process :scale => [200, 200], :unless => :disallowed_image_type?
# process :sepiatone, :if => :image?
#
# def sepiatone
# ...
# end
#
# def vignette
# ...
# end
#
# def scale(height, width)
# ...
# end
#
# def image?
# ...
# end
#
# def disallowed_image_type?
# ...
# end
#
# end
#
def process(*args)
new_processors = args.inject({}) do |hash, arg|
arg = { arg => [] } unless arg.is_a?(Hash)
hash.merge!(arg)
end
condition_type = new_processors.keys.detect { |key| [:if, :unless].include?(key) }
condition = new_processors.delete(:if) || new_processors.delete(:unless)
new_processors.each do |processor, processor_args|
self.processors += [[processor, processor_args, condition, condition_type]]
if processor == :convert
# Treat :convert specially, since it should trigger the file extension change
force_extension processor_args
end
end
end
end # ClassMethods
##
# Apply all process callbacks added through CarrierWave.process
#
def process!(new_file=nil)
return unless enable_processing
with_callbacks(:process, new_file) do
self.class.processors.each do |method, args, condition, condition_type|
if condition && condition_type == :if
if condition.respond_to?(:call)
next unless condition.call(self, :args => args, :method => method, :file => new_file)
else
next unless self.send(condition, new_file)
end
elsif condition && condition_type == :unless
if condition.respond_to?(:call)
next if condition.call(self, :args => args, :method => method, :file => new_file)
elsif self.send(condition, new_file)
next
end
end
if args.is_a? Array
kwargs, args = args.partition { |arg| arg.is_a? Hash }
end
if kwargs.present?
kwargs = kwargs.reduce(:merge)
self.send(method, *args, **kwargs)
else
self.send(method, *args)
end
end
end
end
private
def forcing_extension(filename)
if force_extension && filename
Pathname.new(filename).sub_ext(".#{force_extension.to_s.delete_prefix('.')}").to_s
else
filename
end
end
end # Processing
end # Uploader
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/uploader/proxy.rb 0000664 0000000 0000000 00000004141 14577507162 0022452 0 ustar 00root root 0000000 0000000 module CarrierWave
module Uploader
module Proxy
##
# === Returns
#
# [Boolean] Whether the uploaded file is blank
#
def blank?
file.blank?
end
##
# === Returns
#
# [String] the path where the file is currently located.
#
def current_path
file.try(:path)
end
alias_method :path, :current_path
##
# Returns a string that uniquely identifies the retrieved or last stored file
#
# === Returns
#
# [String] uniquely identifies a file
#
def identifier
@identifier || (file && storage.try(:identifier))
end
##
# Returns a String which is to be used as a temporary value which gets assigned to the column.
# The purpose is to mark the column that it will be updated. Finally before the save it will be
# overwritten by the #identifier value, which is usually #filename.
#
# === Returns
#
# [String] a temporary_identifier, by default the value of #cache_name is used
#
def temporary_identifier
cache_name || @identifier
end
##
# Read the contents of the file
#
# === Returns
#
# [String] contents of the file
#
def read(*args)
file.try(:read, *args)
end
##
# Fetches the size of the currently stored/cached file
#
# === Returns
#
# [Integer] size of the file
#
def size
file.try(:size) || 0
end
##
# Return the size of the file when asked for its length
#
# === Returns
#
# [Integer] size of the file
#
# === Note
#
# This was added because of the way Rails handles length/size validations in 3.0.6 and above.
#
def length
size
end
##
# Read the content type of the file
#
# === Returns
#
# [String] content type of the file
#
def content_type
file.try(:content_type)
end
end # Proxy
end # Uploader
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/uploader/remove.rb 0000664 0000000 0000000 00000000623 14577507162 0022567 0 ustar 00root root 0000000 0000000 module CarrierWave
module Uploader
module Remove
extend ActiveSupport::Concern
include CarrierWave::Uploader::Callbacks
##
# Removes the file and reset it
#
def remove!
with_callbacks(:remove) do
@file.delete if @file
@file = nil
@cache_id = nil
end
end
end # Remove
end # Uploader
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/uploader/serialization.rb 0000664 0000000 0000000 00000001221 14577507162 0024142 0 ustar 00root root 0000000 0000000 require "json"
require "active_support/core_ext/hash"
module CarrierWave
module Uploader
module Serialization
extend ActiveSupport::Concern
def serializable_hash(options = nil)
{"url" => url}.merge Hash[versions.map { |name, version| [name.to_s, { "url" => version.url }] }]
end
def as_json(options=nil)
serializable_hash
end
def to_json(options=nil)
JSON.generate(as_json)
end
def to_xml(options={})
merged_options = options.merge(:root => mounted_as || "uploader", :type => 'uploader')
serializable_hash.to_xml(merged_options)
end
end
end
end
carrierwave-3.0.7/lib/carrierwave/uploader/store.rb 0000664 0000000 0000000 00000010232 14577507162 0022423 0 ustar 00root root 0000000 0000000 module CarrierWave
module Uploader
module Store
extend ActiveSupport::Concern
include CarrierWave::Uploader::Callbacks
include CarrierWave::Uploader::Configuration
include CarrierWave::Uploader::Cache
included do
prepend Module.new {
def initialize(*)
super
@file, @filename, @cache_id, @identifier, @deduplication_index = nil
end
}
end
##
# Override this in your Uploader to change the filename.
#
# Be careful using record ids as filenames. If the filename is stored in the database
# the record id will be nil when the filename is set. Don't use record ids unless you
# understand this limitation.
#
# Do not use the version_name in the filename, as it will prevent versions from being
# loaded correctly.
#
# === Returns
#
# [String] a filename
#
def filename
@filename
end
##
# Returns a filename which doesn't conflict with already-stored files.
#
# === Returns
#
# [String] the filename with suffix added for deduplication
#
def deduplicated_filename
return unless filename
return filename unless @deduplication_index
parts = filename.split('.')
basename = parts.shift
basename.sub!(/ ?\(\d+\)\z/, '')
([basename.to_s + (@deduplication_index > 1 ? "(#{@deduplication_index})" : '')] + parts).join('.')
end
##
# Calculates the path where the file should be stored. If +for_file+ is given, it will be
# used as the identifier, otherwise +CarrierWave::Uploader#identifier+ is assumed.
#
# === Parameters
#
# [for_file (String)] name of the file
#
# === Returns
#
# [String] the store path
#
def store_path(for_file=identifier)
File.join([store_dir, full_filename(for_file)].compact)
end
##
# Stores the file by passing it to this Uploader's storage engine.
#
# If new_file is omitted, a previously cached file will be stored.
#
# === Parameters
#
# [new_file (File, IOString, Tempfile)] any kind of file object
#
def store!(new_file=nil)
cache!(new_file) if new_file && !cached?
if !cache_only && @file && @cache_id
with_callbacks(:store, new_file) do
new_file = storage.store!(@file)
if delete_tmp_file_after_storage
@file.delete unless move_to_store
cache_storage.delete_dir!(cache_path(nil))
end
@file = new_file
@identifier = storage.identifier
@original_filename = @cache_id = @deduplication_index = nil
@staged = false
end
end
end
##
# Retrieves the file from the storage.
#
# === Parameters
#
# [identifier (String)] uniquely identifies the file to retrieve
#
def retrieve_from_store!(identifier)
with_callbacks(:retrieve_from_store, identifier) do
@file = storage.retrieve!(identifier)
@identifier = identifier
end
end
##
# Look for an identifier which doesn't collide with the given already-stored identifiers.
# It is done by adding a index number as the suffix.
# For example, if there's 'image.jpg' and the @deduplication_index is set to 2,
# The stored file will be named as 'image(2).jpg'.
#
# === Parameters
#
# [current_identifiers (Array[String])] List of identifiers for already-stored files
#
def deduplicate(current_identifiers)
@deduplication_index = nil
return unless current_identifiers.include?(identifier)
(1..current_identifiers.size + 1).each do |i|
@deduplication_index = i
break unless current_identifiers.include?(identifier)
end
end
private
def full_filename(for_file)
forcing_extension(for_file)
end
def storage
@storage ||= self.class.storage.new(self)
end
end # Store
end # Uploader
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/uploader/url.rb 0000664 0000000 0000000 00000002041 14577507162 0022070 0 ustar 00root root 0000000 0000000 module CarrierWave
module Uploader
module Url
extend ActiveSupport::Concern
include CarrierWave::Uploader::Configuration
include CarrierWave::Utilities::Uri
##
# === Parameters
#
# [Hash] optional, the query params (only AWS)
#
# === Returns
#
# [String] the location where this file is accessible via a url
#
def url(options = {})
if file.respond_to?(:url)
tmp_url = file.method(:url).arity.zero? ? file.url : file.url(options)
return tmp_url if tmp_url.present?
end
if file.respond_to?(:path)
path = encode_path(file.path.sub(File.expand_path(root), ''))
if (host = asset_host)
if host.respond_to? :call
"#{host.call(file)}#{path}"
else
"#{host}#{path}"
end
else
(base_path || "") + path
end
end
end
def to_s
url || ''
end
end # Url
end # Uploader
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/uploader/versions.rb 0000664 0000000 0000000 00000024327 14577507162 0023151 0 ustar 00root root 0000000 0000000 require "active_support/core_ext/object/deep_dup"
module CarrierWave
module Uploader
module Versions
class Builder
def initialize(name)
@name = name
@options = {}
@blocks = []
@klass = nil
end
def configure(options, &block)
@options.merge!(options)
@blocks << block if block
@klass = nil
end
def build(superclass)
return @klass if @klass
@klass = Class.new(superclass)
superclass.const_set("#{@name.to_s.camelize}VersionUploader", @klass)
@klass.version_names += [@name]
@klass.versions = {}
@klass.processors = []
@klass.version_options = @options
@klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
# Define the enable_processing method for versions so they get the
# value from the parent class unless explicitly overwritten
def self.enable_processing(value=nil)
self.enable_processing = value if value
if defined?(@enable_processing) && !@enable_processing.nil?
@enable_processing
else
superclass.enable_processing
end
end
# Regardless of what is set in the parent uploader, do not enforce the
# move_to_cache config option on versions because it moves the original
# file to the version's target file.
#
# If you want to enforce this setting on versions, override this method
# in each version:
#
# version :thumb do
# def move_to_cache
# true
# end
# end
#
def move_to_cache
false
end
# Need to rely on the parent version's identifier, as versions don't have its own one.
def identifier
parent_version.identifier
end
RUBY
@blocks.each { |block| @klass.class_eval(&block) }
@klass
end
def deep_dup
other = dup
other.instance_variable_set(:@blocks, @blocks.dup)
other
end
def method_missing(name, *args)
super
rescue NoMethodError => e
raise e.exception <<~ERROR
#{e.message}
If you're trying to configure a version, do it inside a block like `version(:thumb) { self.#{name} #{args.map(&:inspect).join(', ')} }`.
ERROR
end
def respond_to_missing?(*)
super
end
end
extend ActiveSupport::Concern
include CarrierWave::Uploader::Callbacks
included do
class_attribute :versions, :version_names, :version_options, :instance_reader => false, :instance_writer => false
self.versions = {}
self.version_names = []
attr_accessor :parent_version
after :cache, :cache_versions!
after :store, :store_versions!
after :remove, :remove_versions!
after :retrieve_from_cache, :retrieve_versions_from_cache!
after :retrieve_from_store, :retrieve_versions_from_store!
prepend Module.new {
def initialize(*)
super
@versions = nil
end
}
end
module ClassMethods
##
# Adds a new version to this uploader
#
# === Parameters
#
# [name (#to_sym)] name of the version
# [options (Hash)] optional options hash
# [&block (Proc)] a block to eval on this version of the uploader
#
# === Examples
#
# class MyUploader < CarrierWave::Uploader::Base
#
# version :thumb do
# process :scale => [200, 200]
# end
#
# version :preview, :if => :image? do
# process :scale => [200, 200]
# end
#
# version :square, :unless => :invalid_image_type? do
# process :scale => [100, 100]
# end
#
# end
#
def version(name, options = {}, &block)
name = name.to_sym
versions[name] ||= Builder.new(name)
versions[name].configure(options, &block)
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{name}
versions[:#{name}]
end
RUBY
versions[name]
end
private
def inherited(subclass)
# To prevent subclass version changes affecting superclass versions
subclass.versions = versions.deep_dup
super
end
end # ClassMethods
##
# Returns a hash mapping the name of each version of the uploader to an instance of it
#
# === Returns
#
# [Hash{Symbol => CarrierWave::Uploader}] a list of uploader instances
#
def versions
return @versions if @versions
@versions = {}
self.class.versions.each do |name, version|
@versions[name] = version.build(self.class).new(model, mounted_as)
@versions[name].parent_version = self
end
@versions
end
##
# === Returns
#
# [String] the name of this version of the uploader
#
def version_name
self.class.version_names.join('_').to_sym unless self.class.version_names.blank?
end
##
#
# === Parameters
#
# [name (#to_sym)] name of the version
#
# === Returns
#
# [Boolean] True when the version exists according to its :if or :unless condition
#
def version_exists?(name)
name = name.to_sym
return false unless versions.has_key?(name)
if_condition = versions[name].class.version_options[:if]
unless_condition = versions[name].class.version_options[:unless]
if if_condition
if if_condition.respond_to?(:call)
if_condition.call(self, :version => name, :file => file)
else
send(if_condition, file)
end
elsif unless_condition
if unless_condition.respond_to?(:call)
!unless_condition.call(self, :version => name, :file => file)
else
!send(unless_condition, file)
end
else
true
end
end
##
# Copies the parent's cache_id when caching a version file.
# This behavior is not essential but it makes easier to understand
# that the cached files are generated by the single upload attempt.
#
def cache!(*args)
self.cache_id = parent_version.cache_id if parent_version
super
end
##
# When given a version name as a parameter, will return the url for that version
# This also works with nested versions.
# When given a query hash as a parameter, will return the url with signature that contains query params
# Query hash only works with AWS (S3 storage).
#
# === Example
#
# my_uploader.url # => /path/to/my/uploader.gif
# my_uploader.url(:thumb) # => /path/to/my/thumb_uploader.gif
# my_uploader.url(:thumb, :small) # => /path/to/my/thumb_small_uploader.gif
# my_uploader.url(:query => {"response-content-disposition" => "attachment"})
# my_uploader.url(:version, :sub_version, :query => {"response-content-disposition" => "attachment"})
#
# === Parameters
#
# [*args (Symbol)] any number of versions
# OR/AND
# [Hash] query params
#
# === Returns
#
# [String] the location where this file is accessible via a url
#
def url(*args)
if (version = args.first) && version.respond_to?(:to_sym)
raise ArgumentError, "Version #{version} doesn't exist!" if versions[version.to_sym].nil?
# recursively proxy to version
versions[version.to_sym].url(*args[1..-1])
elsif args.first
super(args.first)
else
super
end
end
##
# Recreate versions and reprocess them. This can be used to recreate
# versions if their parameters somehow have changed.
#
def recreate_versions!(*names)
# As well as specified versions, we need to reprocess versions
# that are the source of another version.
self.cache_id = CarrierWave.generate_cache_id
derived_versions.each_value do |v|
v.cache!(file) if names.empty? || !(v.descendant_version_names & names).empty?
end
active_versions.each do |name, v|
v.store! if names.empty? || names.include?(name)
end
ensure
@cache_id = nil
end
protected
def descendant_version_names
[version_name] + derived_versions.flat_map do |name, version|
version.descendant_version_names
end
end
def active_versions
versions.select do |name, uploader|
version_exists?(name)
end
end
private
def derived_versions
active_versions.reject do |name, v|
v.class.version_options[:from_version]
end.merge(active_sibling_versions.select do |name, v|
v.class.version_options[:from_version] == self.class.version_names.last
end)
end
def active_sibling_versions
parent_version&.active_versions || {}
end
def full_filename(for_file)
[version_name, super(for_file)].compact.join('_')
end
def full_original_filename
[version_name, super].compact.join('_')
end
def cache_versions!(new_file)
derived_versions.each_value { |v| v.cache!(new_file) }
end
def store_versions!(new_file)
active_versions.each_value { |v| v.store!(new_file) }
end
def remove_versions!
versions.each_value { |v| v.remove! }
end
def retrieve_versions_from_cache!(cache_name)
active_versions.each_value { |v| v.retrieve_from_cache!(cache_name) }
end
def retrieve_versions_from_store!(identifier)
active_versions.each_value { |v| v.retrieve_from_store!(identifier) }
end
end # Versions
end # Uploader
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/utilities.rb 0000664 0000000 0000000 00000000177 14577507162 0021476 0 ustar 00root root 0000000 0000000 require 'carrierwave/utilities/uri'
require 'carrierwave/utilities/file_name'
module CarrierWave
module Utilities
end
end
carrierwave-3.0.7/lib/carrierwave/utilities/ 0000775 0000000 0000000 00000000000 14577507162 0021144 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/lib/carrierwave/utilities/file_name.rb 0000664 0000000 0000000 00000002207 14577507162 0023411 0 ustar 00root root 0000000 0000000 module CarrierWave
module Utilities
module FileName
##
# Returns the part of the filename before the extension. So if a file is called 'test.jpeg'
# this would return 'test'
#
# === Returns
#
# [String] the first part of the filename
#
def basename
split_extension(filename)[0] if filename
end
##
# Returns the file extension
#
# === Returns
#
# [String] extension of file or "" if the file has no extension
#
def extension
split_extension(filename)[1] if filename
end
private
def split_extension(filename)
# regular expressions to try for identifying extensions
extension_matchers = [
/\A(.+)\.(tar\.([glx]?z|bz2))\z/, # matches "something.tar.gz"
/\A(.+)\.([^\.]+)\z/ # matches "something.jpg"
]
extension_matchers.each do |regexp|
if filename =~ regexp
return $1, $2
end
end
[filename, ""] # In case we weren't able to split the extension
end
end # FileName
end # Utilities
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/utilities/uri.rb 0000664 0000000 0000000 00000001121 14577507162 0022263 0 ustar 00root root 0000000 0000000 require 'uri'
module CarrierWave
module Utilities
module Uri
# based on Ruby < 2.0's URI.encode
PATH_SAFE = URI::REGEXP::PATTERN::UNRESERVED + '\/'
PATH_UNSAFE = Regexp.new("[^#{PATH_SAFE}]", false)
NON_ASCII = /[^[:ascii:]]/.freeze
private
def encode_path(path)
URI::DEFAULT_PARSER.escape(path, PATH_UNSAFE)
end
def encode_non_ascii(str)
URI::DEFAULT_PARSER.escape(str, NON_ASCII)
end
def decode_uri(str)
URI::DEFAULT_PARSER.unescape(str)
end
end # Uri
end # Utilities
end # CarrierWave
carrierwave-3.0.7/lib/carrierwave/validations/ 0000775 0000000 0000000 00000000000 14577507162 0021446 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/lib/carrierwave/validations/active_model.rb 0000664 0000000 0000000 00000004651 14577507162 0024434 0 ustar 00root root 0000000 0000000 require 'active_model/validator'
require 'active_support/concern'
module CarrierWave
# == Active Model Presence Validator
module Validations
module ActiveModel
extend ActiveSupport::Concern
class ProcessingValidator < ::ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.__send__("#{attribute}_processing_errors").each do |e|
record.errors.add(attribute, :carrierwave_processing_error, message: (e.message != e.class.to_s) && e.message)
end
end
end
class IntegrityValidator < ::ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.__send__("#{attribute}_integrity_errors").each do |e|
record.errors.add(attribute, :carrierwave_integrity_error, message: (e.message != e.class.to_s) && e.message)
end
end
end
class DownloadValidator < ::ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.__send__("#{attribute}_download_errors").each do |e|
record.errors.add(attribute, :carrierwave_download_error, message: (e.message != e.class.to_s) && e.message)
end
end
end
module HelperMethods
##
# Makes the record invalid if the file couldn't be uploaded due to an integrity error
#
# Accepts the usual parameters for validations in Rails (:if, :unless, etc...)
#
def validates_integrity_of(*attr_names)
validates_with IntegrityValidator, _merge_attributes(attr_names)
end
##
# Makes the record invalid if the file couldn't be processed (assuming the process failed
# with a CarrierWave::ProcessingError)
#
# Accepts the usual parameters for validations in Rails (:if, :unless, etc...)
#
def validates_processing_of(*attr_names)
validates_with ProcessingValidator, _merge_attributes(attr_names)
end
#
##
# Makes the record invalid if the remote file couldn't be downloaded
#
# Accepts the usual parameters for validations in Rails (:if, :unless, etc...)
#
def validates_download_of(*attr_names)
validates_with DownloadValidator, _merge_attributes(attr_names)
end
end
included do
extend HelperMethods
include HelperMethods
end
end
end
end
carrierwave-3.0.7/lib/carrierwave/version.rb 0000664 0000000 0000000 00000000062 14577507162 0021141 0 ustar 00root root 0000000 0000000 module CarrierWave
VERSION = "3.0.7".freeze
end
carrierwave-3.0.7/lib/generators/ 0000775 0000000 0000000 00000000000 14577507162 0016770 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/lib/generators/templates/ 0000775 0000000 0000000 00000000000 14577507162 0020766 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/lib/generators/templates/uploader.rb.erb 0000664 0000000 0000000 00000002710 14577507162 0023675 0 ustar 00root root 0000000 0000000 class <%= class_name %>Uploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
# include CarrierWave::RMagick
# include CarrierWave::MiniMagick
# Choose what kind of storage to use for this uploader:
storage :file
# storage :fog
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
# Provide a default URL as a default if there hasn't been a file uploaded:
# def default_url(*args)
# # For Rails 3.1+ asset pipeline compatibility:
# # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
#
# "/images/fallback/" + [version_name, "default.png"].compact.join('_')
# end
# Process files as they are uploaded:
# process scale: [200, 300]
#
# def scale(width, height)
# # do something
# end
# Create different versions of your uploaded files:
# version :thumb do
# process resize_to_fit: [50, 50]
# end
# Add an allowlist of extensions which are allowed to be uploaded.
# For images you might use something like this:
# def extension_allowlist
# %w(jpg jpeg gif png)
# end
# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
# def filename
# "something.jpg"
# end
end
carrierwave-3.0.7/lib/generators/uploader_generator.rb 0000664 0000000 0000000 00000000365 14577507162 0023202 0 ustar 00root root 0000000 0000000 class UploaderGenerator < Rails::Generators::NamedBase
source_root File.expand_path('templates', __dir__)
def create_uploader_file
template "uploader.rb.erb", File.join('app/uploaders', class_path, "#{file_name}_uploader.rb")
end
end
carrierwave-3.0.7/script/ 0000775 0000000 0000000 00000000000 14577507162 0015355 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/script/console 0000775 0000000 0000000 00000000674 14577507162 0016754 0 ustar 00root root 0000000 0000000 #!/usr/bin/env ruby
# File: script/console
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
libs = " -r irb/completion"
# Perhaps use a console_lib to store any extra methods I may want available in the console
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
libs << " -r #{File.dirname(__FILE__) + '/../lib/carrierwave.rb'}"
puts "Loading carrierwave gem"
exec "#{irb} #{libs} --simple-prompt"
carrierwave-3.0.7/script/destroy 0000775 0000000 0000000 00000000603 14577507162 0016773 0 ustar 00root root 0000000 0000000 #!/usr/bin/env ruby
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
begin
require 'rubigen'
rescue LoadError
require 'rubygems'
require 'rubigen'
end
require 'rubigen/scripts/destroy'
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
RubiGen::Scripts::Destroy.new.run(ARGV)
carrierwave-3.0.7/script/generate 0000775 0000000 0000000 00000000605 14577507162 0017076 0 ustar 00root root 0000000 0000000 #!/usr/bin/env ruby
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
begin
require 'rubigen'
rescue LoadError
require 'rubygems'
require 'rubigen'
end
require 'rubigen/scripts/generate'
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
RubiGen::Scripts::Generate.new.run(ARGV)
carrierwave-3.0.7/spec/ 0000775 0000000 0000000 00000000000 14577507162 0015003 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/spec/compatibility/ 0000775 0000000 0000000 00000000000 14577507162 0017654 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/spec/compatibility/paperclip_spec.rb 0000664 0000000 0000000 00000007703 14577507162 0023201 0 ustar 00root root 0000000 0000000 require 'spec_helper'
require 'carrierwave/orm/activerecord'
module Rails; end unless defined?(Rails)
describe CarrierWave::Compatibility::Paperclip do
let(:uploader_class) do
Class.new(CarrierWave::Uploader::Base) do
include CarrierWave::Compatibility::Paperclip
version :thumb
version :list
end
end
let(:model) { double('model') }
let(:uploader) { uploader_class.new(model, :monkey) }
before do
allow(Rails).to receive(:root).and_return('/rails/root')
allow(Rails).to receive(:env).and_return('test')
allow(model).to receive(:id).and_return(23)
allow(model).to receive(:ook).and_return('eek')
allow(model).to receive(:money).and_return('monkey.png')
end
after { FileUtils.rm_rf(public_path) }
describe '#store_path' do
subject { uploader.store_path("monkey.png") }
it "mimics paperclip default" do
is_expected.to eq("/rails/root/public/system/monkeys/23/original/monkey.png")
end
it "interpolates the root path" do
allow(uploader).to receive(:paperclip_path).and_return(":rails_root/foo/bar")
is_expected.to eq(Rails.root + "/foo/bar")
end
it "interpolates the attachment" do
allow(uploader).to receive(:paperclip_path).and_return("/foo/:attachment/bar")
is_expected.to eq("/foo/monkeys/bar")
end
it "interpolates the id" do
allow(uploader).to receive(:paperclip_path).and_return("/foo/:id/bar")
is_expected.to eq("/foo/23/bar")
end
it "interpolates the id partition" do
allow(uploader).to receive(:paperclip_path).and_return("/foo/:id_partition/bar")
is_expected.to eq("/foo/000/000/023/bar")
end
it "interpolates the basename" do
allow(uploader).to receive(:paperclip_path).and_return("/foo/:basename/bar")
is_expected.to eq("/foo/monkey/bar")
end
it "interpolates the extension" do
allow(uploader).to receive(:paperclip_path).and_return("/foo/:extension/bar")
is_expected.to eq("/foo/png/bar")
end
end
describe '.interpolate' do
subject { uploader.store_path("monkey.png") }
before do
uploader_class.interpolate :ook do |custom, style|
custom.model.ook
end
uploader_class.interpolate :aak do |model, style|
style
end
end
it 'allows you to add custom interpolations' do
allow(uploader).to receive(:paperclip_path).and_return("/foo/:id/:ook")
is_expected.to eq('/foo/23/eek')
end
it 'mimics paperclips arguments' do
allow(uploader).to receive(:paperclip_path).and_return("/foo/:aak")
is_expected.to eq('/foo/original')
end
context 'when multiple uploaders include the compatibility module' do
let(:uploader) { uploader_class_other.new(model, :monkey) }
let(:uploader_class_other) do
Class.new(CarrierWave::Uploader::Base) do
include CarrierWave::Compatibility::Paperclip
version :thumb
version :list
end
end
before { allow(uploader).to receive(:paperclip_path).and_return("/foo/:id/:ook") }
it "doesn't share custom interpolations" do
is_expected.to eq('/foo/23/:ook')
end
end
context 'when there are multiple versions' do
let(:complex_uploader_class) do
Class.new(CarrierWave::Uploader::Base) do
include CarrierWave::Compatibility::Paperclip
interpolate :ook do |model, style|
'eek'
end
version :thumb
version :list
def paperclip_path
"#{public_path}/foo/:ook/:id/:style"
end
end
end
let(:uploader) { complex_uploader_class.new(model, :monkey) }
let!(:file) { File.open(file_path('test.jpg')) }
before { uploader.store!(file) }
it 'interpolates for all versions correctly' do
expect(uploader.thumb.path).to eq("#{public_path}/foo/eek/23/thumb")
expect(uploader.list.path).to eq("#{public_path}/foo/eek/23/list")
end
end
end
end
carrierwave-3.0.7/spec/downloader/ 0000775 0000000 0000000 00000000000 14577507162 0017141 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/spec/downloader/base_spec.rb 0000664 0000000 0000000 00000022170 14577507162 0021414 0 ustar 00root root 0000000 0000000 require 'spec_helper'
describe CarrierWave::Downloader::Base do
let(:uploader_class) { Class.new(CarrierWave::Uploader::Base) }
let(:uploader) { uploader_class.new }
let(:file) { File.read(file_path("test.jpg")) }
let(:filename) { "test.jpg" }
let(:uri) { "http://www.example.com/#{URI::DEFAULT_PARSER.escape(filename)}" }
subject { CarrierWave::Downloader::Base.new(uploader) }
context "with unicode symbols in URL" do
let(:filename) { "юникод.jpg" }
before do
stub_request(:get, uri).to_return(body: file)
end
let(:remote_file) { subject.download(uri) }
it "downloads a file" do
expect(remote_file).to be_an_instance_of(CarrierWave::Downloader::RemoteFile)
end
it "sets the filename to the file's decoded sanitized filename" do
expect(remote_file.original_filename).to eq(filename)
end
end
context "with equal and colons in the query path" do
let(:query) { 'test=query&with=equal&before=colon:param' }
let(:uri) { "https://example.com/#{filename}?#{query}" }
before do
stub_request(:get, uri).to_return(body: file)
end
it "leaves colon in resulting URI" do
expect(subject.process_uri(uri).query).to eq query
end
it "downloads a file" do
expect(subject.download(uri).file.read).to eq file
end
end
context "with internationalized domain name" do
let(:uri) { "https://ドメインå例.jp/test.jpg" }
before do
stub_request(:get, 'https://xn--eckwd4c7cu47r2wf.jp/test.jpg').to_return(body: file)
allow(Resolv).to receive(:getaddresses).with('xn--eckwd4c7cu47r2wf.jp').and_return(['1.2.3.4'])
end
it "downloads a file" do
expect(subject.download(uri).file.read).to eq file
end
end
context "with non-ascii characters in the query and the fragment" do
let(:uri) { "http://example.com/test.jpg?q=Ð#ã‚ã‚ã‚" }
before do
stub_request(:get, "http://example.com/test.jpg?q=%D0%90").to_return(body: file)
end
it "downloads a file" do
expect(subject.download(uri).file.read).to eq file
end
end
context 'with request headers' do
let(:authentication_headers) do
{
'Accept'=>'*/*',
'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
'User-Agent'=>"CarrierWave/#{CarrierWave::VERSION}",
'Authorization'=>'Bearer QWE'
}
end
before do
stub_request(:get, uri).
with(:headers => authentication_headers).
to_return(body: file)
end
it 'pass custom headers to request' do
expect(subject.download(uri, { 'Authorization' => 'Bearer QWE' }).file.read).to eq file
end
end
context "with missing file" do
before do
stub_request(:get, uri).to_return(status: 404)
end
it "raises an error when trying to download a missing file" do
expect{ subject.download(uri) }.to raise_error(CarrierWave::DownloadError)
end
it "doesn't obscure original exception message" do
expect { subject.download(uri) }.to raise_error(CarrierWave::DownloadError, /could not download file: 404/)
end
end
context "with server error" do
before do
stub_request(:get, uri).to_return(status: 500)
end
it "raises an error when trying to download" do
expect{ subject.download(uri) }.to raise_error(CarrierWave::DownloadError)
end
it "doesn't obscure original exception message" do
expect { subject.download(uri) }.to raise_error(CarrierWave::DownloadError, /could not download file: 500/)
end
end
context "with a url that contains space" do
let(:filename) { "my test.jpg" }
before do
stub_request(:get, uri).to_return(body: file)
end
it "accepts spaces in the url" do
expect(subject.download(uri).original_filename).to eq filename
end
end
context "with redirects" do
let(:another_uri) { 'http://example.com/redirected.jpg' }
before do
stub_request(:get, uri).
to_return(status: 301, body: "Redirecting", headers: { "Location" => another_uri })
stub_request(:get, /redirected.jpg/).to_return(body: file)
end
it "retrieves redirected file" do
expect(subject.download(uri).file.read).to eq file
end
it "extracts filename from the url after redirection" do
expect(subject.download(uri).original_filename).to eq 'redirected.jpg'
end
end
context "with SSRF prevention" do
before do
stub_request(:get, 'http://169.254.169.254/').to_return(body: file)
stub_request(:get, 'http://127.0.0.1/').to_return(body: file)
end
it "prevents accessing local files" do
expect { subject.download('/etc/passwd') }.to raise_error(CarrierWave::DownloadError)
expect { subject.download('file:///etc/passwd') }.to raise_error(CarrierWave::DownloadError)
end
it "prevents accessing internal addresses" do
expect { uploader.download!("http://169.254.169.254/") }.to raise_error CarrierWave::DownloadError
expect { uploader.download!("http://lvh.me/") }.to raise_error CarrierWave::DownloadError
end
end
context 'with download_retry_count' do
before do
stub_request(:get, uri).to_return({ status: 503 }, { status: 200 })
end
let(:instance) { described_class.new(uploader) }
subject { instance.download uri }
context 'when download_retry_count == 0 ' do
before do
uploader.download_retry_count = 0
end
it 'throws an exception' do
expect { subject }.to raise_error CarrierWave::DownloadError
end
end
context 'when download_retry_count > 0' do
before do
uploader.download_retry_count = 1
uploader.download_retry_wait_time = 0.01
end
it 'does not throw an exception' do
expect { subject }.not_to raise_error
end
end
end
context 'when actually downloading a file' do
let(:uri) { 'https://raw.githubusercontent.com/carrierwaveuploader/carrierwave/master/spec/fixtures/test.jpg' }
before { WebMock.disable! }
after { WebMock.enable! }
it 'retrieves the body successfully' do
expect(subject.download(uri).file.read).to eq 'this is stuff'
end
end
describe '#process_uri' do
it "returns an URI instance" do
uri = "http://example.com/"
expect(subject.process_uri(uri)).to be_an_instance_of(URI::HTTP)
end
it "converts a URL with internationalized domain name to Punycode URI" do
uri = "http://ドメインå例.jp/#{CGI.escape(filename)}"
processed = subject.process_uri(uri)
expect(processed.to_s).to eq 'http://xn--eckwd4c7cu47r2wf.jp/test.jpg'
end
it "parses but not escape already escaped uris" do
uri = 'http://example.com/%5B.jpg'
processed = subject.process_uri(uri)
expect(processed.to_s).to eq(uri)
end
it "does not perform normalization on path when not necessary" do
uri = 'http://example.com/o%CC%88.png'
processed = subject.process_uri(uri)
expect(processed.to_s).to eq(uri)
end
it "parses but not escape uris with query-string-only characters not needing escaping" do
uri = 'http://example.com/?foo[]=bar'
processed = subject.process_uri(uri)
expect(processed.to_s).to eq(uri)
end
it "escapes and parse unescaped uris" do
uri = 'http://example.com/ %[].jpg'
processed = subject.process_uri(uri)
expect(processed.to_s).to eq('http://example.com/%20%25%5B%5D.jpg')
end
it "parses but not escape uris with query-string characters representing urls not needing escaping " do
uri = 'http://example.com/?src0=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F1234_1280x720.jpg'
processed = subject.process_uri(uri)
expect(processed.to_s).to eq(uri)
end
it "escapes and parse brackets in uri paths without harming the query string" do
uri = 'http://example.com/].jpg?test[]'
processed = subject.process_uri(uri)
expect(processed.to_s).to eq('http://example.com/%5D.jpg?test[]')
end
it "escapes and parse unescaped characters in path" do
uri = 'http://example.com/ã‚ã‚ã‚.jpg'
processed = subject.process_uri(uri)
expect(processed.to_s).to eq('http://example.com/%E3%81%82%E3%81%82%E3%81%82.jpg')
end
it "escapes and parse unescaped characters in query string" do
uri = 'http://example.com/?q=ã‚ã‚ã‚'
processed = subject.process_uri(uri)
expect(processed.to_s).to eq('http://example.com/?q=%E3%81%82%E3%81%82%E3%81%82')
end
it "escapes and parse unescaped characters in the fragment" do
uri = 'http://example.com/#ã‚ã‚ã‚'
processed = subject.process_uri(uri)
expect(processed.to_s).to eq('http://example.com/#%E3%81%82%E3%81%82%E3%81%82')
end
it "throws an exception on bad uris" do
uri = '~http:'
expect { subject.process_uri(uri) }.to raise_error(CarrierWave::DownloadError)
end
end
describe "#skip_ssrf_protection?" do
let(:uri) { 'http://localhost/test.jpg' }
before do
WebMock.stub_request(:get, uri).to_return(body: file)
allow(subject).to receive(:skip_ssrf_protection?).and_return(true)
end
it "allows local request to be made" do
expect(subject.download(uri).read).to eq 'this is stuff'
end
end
end
carrierwave-3.0.7/spec/downloader/remote_file_spec.rb 0000664 0000000 0000000 00000007257 14577507162 0023005 0 ustar 00root root 0000000 0000000 require 'spec_helper'
describe CarrierWave::Downloader::RemoteFile do
subject { CarrierWave::Downloader::RemoteFile.new(file) }
let(:file) do
Net::HTTPSuccess.new('1.0', '200', "").tap do |response|
response.body = File.read(file_path("test.jpg"))
response.instance_variable_set(:@read, true)
response.uri = URI.parse 'http://example.com/test'
response['content-type'] = 'image/jpeg'
response['vary'] = 'Accept-Encoding'
end
end
context 'with Net::HTTPResponse instance' do
it 'returns content type' do
expect(subject.content_type).to eq 'image/jpeg'
end
it 'returns header' do
expect(subject.headers['vary']).to eq 'Accept-Encoding'
end
it 'returns URI' do
expect(subject.uri.to_s).to eq 'http://example.com/test'
end
end
{
'204' => Net::HTTPNoContent,
'205' => Net::HTTPResetContent
}.each do |response_code, response_class|
context "with a #{response_class} instance" do
let!(:file) do
response_class.new('1.0', response_code, '').tap do |response|
response.reading_body(StringIO.new, true) {}
end
end
it 'raises CarrierWave::DownloadError' do
expect { subject }.to raise_error(CarrierWave::DownloadError, 'could not download file: No Content')
end
end
end
context 'with OpenURI::Meta instance' do
let(:file) do
File.open(file_path("test.jpg")).tap { |f| OpenURI::Meta.init(f) }.tap do |file|
file.base_uri = URI.parse 'http://example.com/test'
file.meta_add_field 'content-type', 'image/jpeg'
file.meta_add_field 'vary', 'Accept-Encoding'
end
end
it 'returns content type' do
expect(subject.content_type).to eq 'image/jpeg'
end
it 'returns header' do
expect(subject.headers['vary']).to eq 'Accept-Encoding'
end
it 'returns URI' do
expect(subject.uri.to_s).to eq 'http://example.com/test'
end
end
describe '#original_filename' do
let(:content_disposition){ nil }
before do
file['content-disposition'] = content_disposition if content_disposition
end
it 'sets file extension based on content-type if missing' do
expect(subject.original_filename).to eq "test.jpg"
end
context 'when filename is quoted' do
let(:content_disposition){ 'filename="another_test.jpg"' }
it "reads filename correctly" do
expect(subject.original_filename).to eq 'another_test.jpg'
end
end
context 'when filename is quoted and empty' do
let(:content_disposition){ 'filename=""' }
it "sets file extension based on content-type if missing" do
expect(subject.original_filename).to eq 'test.jpg'
end
end
context 'when filename is not quoted and empty' do
let(:content_disposition){ 'filename=' }
it "reads filename correctly" do
expect(subject.original_filename).to eq 'test.jpg'
end
end
context 'when filename is not quoted' do
let(:content_disposition){ 'filename=another_test.jpg' }
it "reads filename correctly" do
expect(subject.original_filename).to eq 'another_test.jpg'
end
end
context 'when filename is not quoted and terminated by semicolon' do
let(:content_disposition){ 'filename=another_test.jpg; size=1234' }
it "reads filename correctly" do
expect(subject.original_filename).to eq 'another_test.jpg'
end
end
context 'when filename is quoted and contains a semicolon' do
let(:content_disposition){ 'filename="another;_test.jpg"; size=1234' }
it "reads filename correctly" do
expect(subject.original_filename).to eq 'another;_test.jpg'
end
end
end
end
carrierwave-3.0.7/spec/fixtures/ 0000775 0000000 0000000 00000000000 14577507162 0016654 5 ustar 00root root 0000000 0000000 carrierwave-3.0.7/spec/fixtures/Uppercase.jpg 0000664 0000000 0000000 00000000015 14577507162 0021301 0 ustar 00root root 0000000 0000000 this is stuff carrierwave-3.0.7/spec/fixtures/addresses.csv 0000664 0000000 0000000 00000000510 14577507162 0021342 0 ustar 00root root 0000000 0000000 John,Doe,120 jefferson st.,Riverside, NJ, 08075
Jack,McGinnis,220 hobo Av.,Phila, PA,09119
"John ""Da Man""",Repici,120 Jefferson St.,Riverside, NJ,08075
Stephen,Tyler,"7452 Terrace ""At the Plaza"" road",SomeTown,SD, 91234
,Blankman,,SomeTown, SD, 00298
"Joan ""the bone"", Anne",Jet,"9th, at Terrace plc",Desert City,CO,00123
carrierwave-3.0.7/spec/fixtures/bork.ABCDE 0000664 0000000 0000000 00000000000 14577507162 0020317 0 ustar 00root root 0000000 0000000 carrierwave-3.0.7/spec/fixtures/bork.json 0000664 0000000 0000000 00000000025 14577507162 0020501 0 ustar 00root root 0000000 0000000 {
"bork": "bork"
}
carrierwave-3.0.7/spec/fixtures/bork.ttxt 0000664 0000000 0000000 00000000715 14577507162 0020541 0 ustar 00root root 0000000 0000000 bork bork bork Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. carrierwave-3.0.7/spec/fixtures/bork.txt 0000664 0000000 0000000 00000000715 14577507162 0020355 0 ustar 00root root 0000000 0000000 bork bork bork Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. carrierwave-3.0.7/spec/fixtures/bork.txtt 0000664 0000000 0000000 00000000715 14577507162 0020541 0 ustar 00root root 0000000 0000000 bork bork bork Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. carrierwave-3.0.7/spec/fixtures/case.JPG 0000664 0000000 0000000 00000000015 14577507162 0020125 0 ustar 00root root 0000000 0000000 this is stuff carrierwave-3.0.7/spec/fixtures/landscape.jpg 0000664 0000000 0000000 00000511326 14577507162 0021320 0 ustar 00root root 0000000 0000000 ÿØÿà JFIF ÿáHiExif II*
† ˜ ( 1 ¨ 2 Ä i‡ Ø \ Panasonic DMC-FZ5 H H ACD Systems Digital Imaging 2005:07:18 13:17:36 # š‚ ‚ ‚ Š "ˆ 'ˆ P 0220 ’ ¦ ‘ ‘ º ’
 ’ Ê ’ ’ ’
’ Ò |’ \ Ú ’ 718 0100 € à 6 ¢ £ £ ¤ ¤ ¤ ¤ T ¤ K ¤ ¤ ¤ ¤
¤
Ä (
2005:07:15 11:58:30 2005:07:15 11:58:30 d
(
Panasonic ! ú ê " # $ % ä &