cloudinary 1.9.1 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +42 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +21 -0
  4. data/.github/pull_request_template.md +24 -0
  5. data/.gitignore +7 -1
  6. data/.travis.yml +15 -8
  7. data/CHANGELOG.md +261 -0
  8. data/README.md +3 -0
  9. data/Rakefile +3 -45
  10. data/cloudinary.gemspec +27 -20
  11. data/lib/active_storage/blob_key.rb +20 -0
  12. data/lib/active_storage/service/cloudinary_service.rb +249 -0
  13. data/lib/cloudinary.rb +53 -63
  14. data/lib/cloudinary/account_api.rb +231 -0
  15. data/lib/cloudinary/account_config.rb +30 -0
  16. data/lib/cloudinary/api.rb +228 -71
  17. data/lib/cloudinary/auth_token.rb +10 -4
  18. data/lib/cloudinary/base_api.rb +79 -0
  19. data/lib/cloudinary/base_config.rb +70 -0
  20. data/lib/cloudinary/cache.rb +38 -0
  21. data/lib/cloudinary/cache/breakpoints_cache.rb +31 -0
  22. data/lib/cloudinary/cache/key_value_cache_adapter.rb +25 -0
  23. data/lib/cloudinary/cache/rails_cache_adapter.rb +34 -0
  24. data/lib/cloudinary/cache/storage/rails_cache_storage.rb +5 -0
  25. data/lib/cloudinary/carrier_wave.rb +4 -2
  26. data/lib/cloudinary/carrier_wave/remote.rb +3 -2
  27. data/lib/cloudinary/carrier_wave/storage.rb +2 -1
  28. data/lib/cloudinary/{controller.rb → cloudinary_controller.rb} +3 -5
  29. data/lib/cloudinary/config.rb +43 -0
  30. data/lib/cloudinary/helper.rb +77 -7
  31. data/lib/cloudinary/migrator.rb +3 -1
  32. data/lib/cloudinary/railtie.rb +7 -3
  33. data/lib/cloudinary/responsive.rb +111 -0
  34. data/lib/cloudinary/uploader.rb +67 -15
  35. data/lib/cloudinary/utils.rb +324 -54
  36. data/lib/cloudinary/version.rb +1 -1
  37. data/lib/cloudinary/video_helper.rb +96 -22
  38. data/lib/tasks/cloudinary/fetch_assets.rake +48 -0
  39. data/lib/tasks/{cloudinary.rake → cloudinary/sync_static.rake} +0 -0
  40. data/tools/allocate_test_cloud.sh +9 -0
  41. data/tools/get_test_cloud.sh +9 -0
  42. data/tools/update_version +220 -0
  43. data/vendor/assets/javascripts/cloudinary/jquery.cloudinary.js +51 -13
  44. data/vendor/assets/javascripts/cloudinary/jquery.fileupload.js +24 -4
  45. data/vendor/assets/javascripts/cloudinary/jquery.ui.widget.js +741 -561
  46. data/vendor/assets/javascripts/cloudinary/load-image.all.min.js +1 -1
  47. metadata +92 -67
  48. data/spec/access_control_spec.rb +0 -99
  49. data/spec/api_spec.rb +0 -545
  50. data/spec/archive_spec.rb +0 -129
  51. data/spec/auth_token_spec.rb +0 -79
  52. data/spec/cloudinary_helper_spec.rb +0 -190
  53. data/spec/cloudinary_spec.rb +0 -32
  54. data/spec/data/sync_static/app/assets/javascripts/1.coffee +0 -1
  55. data/spec/data/sync_static/app/assets/javascripts/1.js +0 -1
  56. data/spec/data/sync_static/app/assets/stylesheets/1.css +0 -3
  57. data/spec/docx.docx +0 -0
  58. data/spec/favicon.ico +0 -0
  59. data/spec/logo.png +0 -0
  60. data/spec/rake_spec.rb +0 -160
  61. data/spec/sample_asset_file.tsv +0 -4
  62. data/spec/search_spec.rb +0 -109
  63. data/spec/spec_helper.rb +0 -245
  64. data/spec/storage_spec.rb +0 -44
  65. data/spec/streaminig_profiles_api_spec.rb +0 -74
  66. data/spec/support/helpers/temp_file_helpers.rb +0 -22
  67. data/spec/support/shared_contexts/rake.rb +0 -19
  68. data/spec/uploader_spec.rb +0 -363
  69. data/spec/utils_methods_spec.rb +0 -54
  70. data/spec/utils_spec.rb +0 -906
  71. data/spec/video_tag_spec.rb +0 -251
  72. data/spec/video_url_spec.rb +0 -164
data/spec/favicon.ico DELETED
Binary file
data/spec/logo.png DELETED
Binary file
data/spec/rake_spec.rb DELETED
@@ -1,160 +0,0 @@
1
- require 'spec_helper'
2
- require 'cloudinary'
3
-
4
- require 'tmpdir'
5
- require 'pathname'
6
-
7
- describe 'cloudinary:sync_static' do
8
- include_context 'rake' # context needs to have the exact rake task as name
9
- include Helpers::TempFileHelpers
10
-
11
- before(:all) do
12
- copy_root_to_temp('spec/data/sync_static/')
13
-
14
- # Reuse some existing spec assets so as not to add weight to the project
15
- copy_file_to_temp('spec/logo.png', 'app/assets/images/logo1.png')
16
- copy_file_to_temp('spec/logo.png', 'app/assets/images/logo2.png')
17
- copy_file_to_temp('samples/basic/lake.jpg', 'app/assets/images/lake1.jpg')
18
- copy_file_to_temp('samples/basic/lake.jpg', 'public/images/lake2.jpg')
19
- end
20
-
21
- after(:all) do
22
- clean_up_temp_files!
23
- end
24
-
25
- before (:each) do
26
- allow(Cloudinary).to receive(:app_root).and_return(Pathname.new(temp_root))
27
- Cloudinary::Static.send(:reset_static_file_config!)
28
- end
29
-
30
- after (:each) do
31
- # destroy all uploaded assets
32
- Cloudinary::Static.send(:build_metadata).each do |_, data|
33
- Cloudinary::Uploader.destroy(data['public_id'], {:type => :asset})
34
- end
35
- # delete metadata_file_path
36
- FileUtils.rm_f(Cloudinary::Static.send(:metadata_file_path))
37
- FileUtils.rm_f(Cloudinary::Static.send(:metadata_trash_file_path))
38
- end
39
-
40
- it 'should find correct file when running in default configuration' do
41
- # with default settings, 4 images only will be uploaded
42
- subject.invoke
43
- expect(Cloudinary::Static.send(:build_metadata).size).to eq 4
44
- end
45
-
46
- it 'should respect deprecated static_image_dirs config setting' do
47
- allow(Cloudinary.config).to receive(:static_image_dirs).and_return(['public/images'])
48
- subject.invoke
49
- expect(Cloudinary::Static.send(:build_metadata).size).to eq 1
50
- end
51
-
52
- it 'should allow to specify only dirs in images group, taking the file_mask from default' do
53
- allow(Cloudinary.config).to receive(:static_files).and_return({
54
- 'images' => {
55
- 'dirs' => ['public/images']
56
- }
57
- })
58
- subject.invoke
59
- expect(Cloudinary::Static.send(:build_metadata).size).to eq 1
60
- end
61
-
62
- it 'should allow to specify only file_mask in images group, taking the dirs from default' do
63
- allow(Cloudinary.config).to receive(:static_files).and_return({
64
- 'images' => {
65
- 'file_mask' => 'png'
66
- }
67
- })
68
- subject.invoke
69
- expect(Cloudinary::Static.send(:build_metadata).size).to eq 2
70
- end
71
-
72
- it 'should allow to specify regex expressions in file_mask' do
73
- allow(Cloudinary.config).to receive(:static_files).and_return({
74
- 'images' => {
75
- 'file_mask' => 'p.?g|jp.?'
76
- },
77
- 'javascripts' => {
78
- 'dirs' => ['app/assets/javascripts'],
79
- 'file_mask' => 'js'
80
- },
81
- 'stylesheets' => {
82
- 'dirs' => ['app/assets/stylesheets'],
83
- 'file_mask' => 'css'
84
- }
85
- })
86
- subject.invoke
87
- expect(Cloudinary::Static.send(:build_metadata).size).to eq 6
88
- end
89
-
90
- context 'Cloudinary::Utils.cloudinary_url' do
91
- def all_asset_forms_of(public_id)
92
- [ "/#{public_id}", public_id.split('/').last]
93
- end
94
-
95
- RSpec::Matchers.define :be_asset_mapped_by_cloudinary_url_to do |expected|
96
- match do |actual|
97
- actual = [actual] unless actual.respond_to? :all?
98
- actual.all? do |public_path|
99
- @public_path = public_path
100
- @actual = Cloudinary::Utils.cloudinary_url(public_path, :cloud_name => 'test', :type => 'asset')
101
- @actual == expected
102
- end
103
- end
104
- failure_message do |actual|
105
- "URL for '#{@public_path}' should have been '#{expected}' but was '#{@actual}'.#{ differ.diff_as_string(@actual, expected)}"
106
- end
107
- failure_message_when_negated do |actual|
108
- "URL for '#{@public_path}' should not have been '#{expected}'."
109
- end
110
- def differ
111
- RSpec::Support::Differ.new(
112
- :object_preparer => lambda { |object| RSpec::Matchers::Composable.surface_descriptions_in(object) },
113
- :color => RSpec::Matchers.configuration.color?
114
- )
115
- end
116
-
117
- end
118
-
119
- before(:each) do
120
- allow(Cloudinary.config).to receive(:static_files).and_return({
121
- 'images' => {
122
- 'dirs' => ['app/assets/images'],
123
- 'file_mask' => 'p.?g|jp.?'
124
- },
125
- 'javascripts' => {
126
- 'dirs' => ['app/assets/javascripts'],
127
- 'file_mask' => 'js'
128
- },
129
- 'stylesheets' => {
130
- 'dirs' => ['app/assets/stylesheets'],
131
- 'file_mask' => 'css'
132
- }
133
- })
134
-
135
- end
136
-
137
- it 'should return Cloudinary asset urls for assets when Cloudinary.config.static_file_support is true' do
138
- allow(Cloudinary.config).to receive(:static_file_support).and_return(true)
139
- subject.invoke
140
-
141
- expect(['logo1.png', '/images/logo1.png']).to be_asset_mapped_by_cloudinary_url_to('http://res.cloudinary.com/test/image/asset/logo1-7dc60722d4653261648038b579fdb89e.png')
142
- expect('images/logo1.png').not_to be_asset_mapped_by_cloudinary_url_to('http://res.cloudinary.com/test/image/asset/logo1-7dc60722d4653261648038b579fdb89e.png')
143
- expect('1.js').to be_asset_mapped_by_cloudinary_url_to('http://res.cloudinary.com/test/raw/asset/1-b01de57adb485efdde843154d030644e.js')
144
- expect(['javascripts/1.js', '/javascripts/1.js']).not_to be_asset_mapped_by_cloudinary_url_to('http://res.cloudinary.com/test/raw/asset/1-b01de57adb485efdde843154d030644e.js')
145
- expect('1.css').to be_asset_mapped_by_cloudinary_url_to('http://res.cloudinary.com/test/raw/asset/1-f24cc6123afd401ab86d8596cabc619f.css')
146
- expect(['stylesheets/1.css', '/stylesheets/1.css']).not_to be_asset_mapped_by_cloudinary_url_to('http://res.cloudinary.com/test/raw/asset/1-f24cc6123afd401ab86d8596cabc619f.css')
147
-
148
- # without :type => 'asset'
149
- expect(Cloudinary::Utils.cloudinary_url('logo1.png')).not_to include('7dc60722d4653261648038b579fdb89e')
150
- end
151
-
152
- it 'should return Cloudinary asset urls for assets when Cloudinary.config.static_image_support is true (backwards compatibility)' do
153
- allow(Cloudinary.config).to receive(:static_image_support).and_return(true)
154
- subject.invoke
155
-
156
- expect(['logo1.png', '/images/logo1.png']).to be_asset_mapped_by_cloudinary_url_to('http://res.cloudinary.com/test/image/asset/logo1-7dc60722d4653261648038b579fdb89e.png')
157
- end
158
- end
159
-
160
- end
@@ -1,4 +0,0 @@
1
- public_path public_id upload_time.to_i version width height
2
- stylesheets/foo.css css-foo 123 1 100 100
3
- javascripts/bar.js bar 123 1 100 100
4
- images/foo.jpg images-foo 123 1 100 100
data/spec/search_spec.rb DELETED
@@ -1,109 +0,0 @@
1
- require 'spec_helper'
2
- require 'cloudinary'
3
-
4
- describe Cloudinary::Search do
5
- context 'unit' do
6
- it 'should create empty json' do
7
- query_hash = Cloudinary::Search.to_h
8
- expect(query_hash).to eq({})
9
- end
10
-
11
- it 'should always return same object in fluent interface' do
12
- instance = Cloudinary::Search.new
13
- %w(expression sort_by max_results next_cursor aggregate with_field).each do |method|
14
- same_instance = instance.send(method, 'emptyarg')
15
- expect(instance).to eq(same_instance)
16
- end
17
- end
18
-
19
- it 'should add expression to query' do
20
- query = Cloudinary::Search.expression('format:jpg').to_h
21
- expect(query).to eq(expression: 'format:jpg')
22
- end
23
-
24
- it 'should add sort_by to query' do
25
- query = Cloudinary::Search.sort_by('created_at', 'asc').sort_by('updated_at', 'desc').to_h
26
- expect(query).to eq(sort_by: [{ 'created_at' => 'asc' }, { 'updated_at' => 'desc' }])
27
- end
28
-
29
- it 'should add max_results to query' do
30
- query = Cloudinary::Search.max_results(10).to_h
31
- expect(query).to eq(max_results: 10)
32
- end
33
-
34
- it 'should add next_cursor to query' do
35
- query = Cloudinary::Search.next_cursor('ASDFIUHASF9832HAFSOF').to_h
36
- expect(query).to eq(next_cursor: 'ASDFIUHASF9832HAFSOF')
37
- end
38
-
39
- it 'should add aggregations arguments as array to query' do
40
- query = Cloudinary::Search.aggregate('format').aggregate('size_category').to_h
41
- expect(query).to eq(aggregate: %w(format size_category))
42
- end
43
-
44
- it 'should add with_field to query' do
45
- query = Cloudinary::Search.with_field('context').with_field('tags').to_h
46
- expect(query).to eq(with_field: %w(context tags))
47
- end
48
- end
49
-
50
- context 'integration' do
51
- SEARCH_TAG = TIMESTAMP_TAG + "_search"
52
- include_context 'cleanup', SEARCH_TAG
53
- prefix = "api_test_#{SUFFIX}"
54
- test_id_1 = "#{prefix}_1"
55
- test_id_2 = "#{prefix}_2"
56
- test_id_3 = "#{prefix}_3"
57
- before(:all) do
58
- Cloudinary::Uploader.upload(TEST_IMG, public_id: test_id_1, tags: [TEST_TAG, TIMESTAMP_TAG, SEARCH_TAG], context: 'stage=in_review')
59
- Cloudinary::Uploader.upload(TEST_IMG, public_id: test_id_2, tags: [TEST_TAG, TIMESTAMP_TAG, SEARCH_TAG], context: 'stage=new')
60
- Cloudinary::Uploader.upload(TEST_IMG, public_id: test_id_3, tags: [TEST_TAG, TIMESTAMP_TAG, SEARCH_TAG], context: 'stage=validated')
61
- sleep(3)
62
- end
63
-
64
- it "should return all images tagged with #{SEARCH_TAG}" do
65
- results = Cloudinary::Search.expression("tags:#{SEARCH_TAG}").execute
66
- expect(results['resources'].count).to eq 3
67
- end
68
-
69
- it "should return resource #{test_id_1}" do
70
- results = Cloudinary::Search.expression("public_id:#{test_id_1}").execute
71
- expect(results['resources'].count).to eq 1
72
- end
73
-
74
- it 'should paginate resources limited by tag and ordered by ascending public_id' do
75
- results = Cloudinary::Search.max_results(1).expression("tags:#{SEARCH_TAG}").sort_by('public_id', 'asc').execute
76
- expect(results['resources'].count).to eq 1
77
- expect(results['resources'][0]['public_id']).to eq test_id_1
78
- expect(results['total_count']).to eq 3
79
-
80
- results = Cloudinary::Search.max_results(1).expression("tags:#{SEARCH_TAG}").sort_by('public_id', 'asc').next_cursor(results['next_cursor']).execute
81
- expect(results['resources'].count).to eq 1
82
- expect(results['resources'][0]['public_id']).to eq test_id_2
83
- expect(results['total_count']).to eq 3
84
-
85
- results = Cloudinary::Search.max_results(1).expression("tags:#{SEARCH_TAG}").sort_by('public_id', 'asc').next_cursor(results['next_cursor']).execute
86
- expect(results['resources'].count).to eq 1
87
- expect(results['resources'][0]['public_id']).to eq test_id_3
88
- expect(results['total_count']).to eq 3
89
- expect(results['next_cursor']).to be_nil
90
- end
91
-
92
- it 'should include context' do
93
- results = Cloudinary::Search.expression("tags:#{SEARCH_TAG}").with_field('context').execute
94
- expect(results['resources'].count).to eq 3
95
- results['resources'].each do |res|
96
- expect(res['context'].keys).to eq ['stage']
97
- end
98
- end
99
- it 'should include context, tags and image_metadata' do
100
- results = Cloudinary::Search.expression("tags:#{SEARCH_TAG}").with_field('context').with_field('tags').with_field('image_metadata').execute
101
- expect(results['resources'].count).to eq 3
102
- results['resources'].each do |res|
103
- expect(res['context'].keys).to eq ['stage']
104
- expect(res.key?('image_metadata')).to eq true
105
- expect(res['tags'].count).to eq 3
106
- end
107
- end
108
- end
109
- end
data/spec/spec_helper.rb DELETED
@@ -1,245 +0,0 @@
1
- require 'rspec'
2
- require 'rexml/parsers/ultralightparser'
3
- require 'rspec/version'
4
- require 'rest_client'
5
- require 'cloudinary'
6
-
7
- Cloudinary.config.enhance_image_tag = true
8
-
9
- TEST_IMAGE_URL = "http://cloudinary.com/images/old_logo.png"
10
- TEST_IMG = "spec/logo.png"
11
- TEST_IMG_W = 241
12
- TEST_IMG_H = 51
13
- SUFFIX = ENV['TRAVIS_JOB_ID'] || rand(999999999).to_s
14
- TEST_TAG = 'cloudinary_gem_test'
15
- TIMESTAMP_TAG = "#{TEST_TAG}_#{SUFFIX}_#{RUBY_VERSION}_#{ defined? Rails::version ? Rails::version : 'no_rails'}"
16
-
17
- # Auth token
18
- KEY = "00112233FF99"
19
- ALT_KEY = "CCBB2233FF00"
20
-
21
-
22
- Dir[File.join(File.dirname(__FILE__), '/support/**/*.rb')].each {|f| require f}
23
-
24
- module RSpec
25
- def self.project_root
26
- File.join(File.dirname(__FILE__), '..')
27
- end
28
- end
29
-
30
- # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
31
- RSpec.configure do |config|
32
- unless RSpec::Version::STRING.match( /^3/)
33
- config.treat_symbols_as_metadata_keys_with_true_values = true
34
- end
35
- config.run_all_when_everything_filtered = true
36
- config.filter_run_excluding :delete_all => true
37
- end
38
-
39
- RSpec.shared_context "cleanup" do |tag|
40
- tag ||= TEST_TAG
41
- after :all do
42
- Cloudinary::Api.delete_resources_by_tag(tag) unless Cloudinary.config.keep_test_products
43
- end
44
-
45
- end
46
-
47
-
48
- CALLS_SERVER_WITH_PARAMETERS = "calls server with parameters"
49
- RSpec.shared_examples CALLS_SERVER_WITH_PARAMETERS do |expected|
50
- expect(RestClient::Request).to receive(:execute).with(deep_hash_value(expected))
51
- end
52
-
53
- # Create a regexp with the given +tag+ name.
54
- def html_tag_matcher( tag)
55
- /<#{tag}([\s]+([-[:word:]]+)[\s]*\=\s*\"([^\"]*)\")*\s*>.*<\s*\/#{tag}\s*>/
56
- end
57
-
58
- # Represents an HTML tag
59
- class TestTag
60
- attr_accessor :name, :attributes, :children, :text, :html_string
61
- # Creates a new +TestTag+ from a given +element+ string
62
- def initialize(element)
63
- @html_string = element
64
- element = valid_tag(element) unless element.is_a? Array
65
- case element[0]
66
- when :start_element
67
- @name = element[2]
68
- @attributes = element[3]
69
- @children = (Array(element[4..-1]) || []).map {|c | TestTag.new c}
70
- when :text
71
- @text = element[1]
72
- @name = "text"
73
- @attributes = []
74
- @children = []
75
- end
76
-
77
- end
78
-
79
- # Parses a given +tag+ in string format
80
- def valid_tag(tag)
81
- parser = REXML::Parsers::UltraLightParser.new( tag)
82
- parser.parse[0]
83
- end
84
-
85
- # Returns attribute named +symbol_or_string+
86
- def [](symbol_or_string)
87
- attributes[symbol_or_string.to_s]
88
- end
89
-
90
- def method_missing(symbol, *args)
91
- if (m = /children_by_(\w+)/.match(symbol.to_s)) and !args.empty?
92
- @children.select{ |c| c[m[1]] == args[0]}
93
- else
94
- super
95
- end
96
- end
97
-
98
- def ==(other)
99
- case other
100
- when String
101
- @text == other
102
- else
103
- other.respond_to?( :text) &&
104
- other.respond_to?( :name) &&
105
- other.respond_to?( :attributes) &&
106
- other.respond_to?( :children) &&
107
- @text == other.text &&
108
- @name == other.name &&
109
- @attributes == other.attributes &&
110
- @children == other.children
111
- end
112
- end
113
- end
114
-
115
- RSpec::Matchers.define :produce_url do |expected_url|
116
- match do |params|
117
- public_id, options = params
118
- actual_options = options.clone
119
- @url = Cloudinary::Utils.cloudinary_url(public_id, actual_options)
120
- values_match? expected_url, @url
121
- end
122
- failure_message do |actual|
123
- "expected #{actual} to\nproduce: #{expected_url}\nbut got: #{@url}"
124
- end
125
- end
126
-
127
- RSpec::Matchers.define :mutate_options_to do |expected_options|
128
- match do |params|
129
- public_id, options = params
130
- options = options.clone
131
- Cloudinary::Utils.cloudinary_url(public_id, options)
132
- @actual = options
133
- values_match? expected_options, @actual
134
- end
135
- end
136
-
137
- RSpec::Matchers.define :empty_options do
138
- match do |params|
139
- public_id, options = params
140
- options = options.clone
141
- Cloudinary::Utils.cloudinary_url(public_id, options)
142
- options.empty?
143
- end
144
- end
145
-
146
- # Verify that the given URL can be served by Cloudinary by fetching the resource from the server
147
- RSpec::Matchers.define :be_served_by_cloudinary do
148
- match do |url|
149
- if url.is_a? Array
150
- url, options = url
151
- url = Cloudinary::Utils.cloudinary_url(url, options.clone)
152
- if Cloudinary.config.upload_prefix
153
- res_prefix_uri = URI.parse(Cloudinary.config.upload_prefix)
154
- res_prefix_uri.path = '/res'
155
- url.gsub!(/https?:\/\/res.cloudinary.com/, res_prefix_uri.to_s)
156
- end
157
- end
158
- code = 0
159
- @url = url
160
- RestClient.get @url do |response, request, result|
161
- @result = result
162
- code = response.code
163
- end
164
- values_match? 200, code
165
- end
166
-
167
- failure_message do |actual|
168
- if @result
169
- "Couldn't serve #{actual}. #{@result["status"]}: #{@result["x-cld-error"]}"
170
- else
171
- "Couldn't serve #{actual}."
172
- end
173
- end
174
- failure_message_when_negated do |actual|
175
- if @result
176
- "Expected #{@url} not to be served by cloudinary. #{@result["status"]}: #{@result["x-cld-error"]}"
177
- else
178
- "Expected #{@url} not to be served by cloudinary."
179
- end
180
-
181
- end
182
- end
183
-
184
- def deep_fetch(hash, path)
185
- Array(path).reduce(hash) { |h, key| h && h.fetch(key, nil) }
186
- end
187
-
188
- # Matches deep values in the actual Hash, disregarding other keys and values.
189
- # @example
190
- # expect( {:foo => { :bar => 'foobar'}}).to have_deep_hash_values_of( [:foo, :bar] => 'foobar')
191
- # expect( foo_instance).to receive(:bar_method).with(deep_hash_values_of([:foo, :bar] => 'foobar'))
192
- RSpec::Matchers.define :deep_hash_value do |expected|
193
- match do |actual|
194
- expected.all? do |path, value|
195
- Cloudinary.values_match? value, deep_fetch(actual, path)
196
- end
197
- end
198
- end
199
-
200
- RSpec::Matchers.alias_matcher :have_deep_hash_values_of, :deep_hash_value
201
-
202
- module Cloudinary
203
- # @api private
204
- def self.values_match?(expected, actual)
205
- if Hash === actual
206
- return hashes_match?(expected, actual) if Hash === expected
207
- elsif Array === expected && Enumerable === actual && !(Struct === actual)
208
- return arrays_match?(expected, actual.to_a)
209
- elsif Regexp === expected
210
- return expected.match actual.to_s
211
- end
212
-
213
-
214
- return true if actual == expected
215
-
216
- begin
217
- expected === actual
218
- rescue ArgumentError
219
- # Some objects, like 0-arg lambdas on 1.9+, raise
220
- # ArgumentError for `expected === actual`.
221
- false
222
- end
223
- end
224
-
225
- # @private
226
- def self.arrays_match?(expected_list, actual_list)
227
- return false if expected_list.size != actual_list.size
228
-
229
- expected_list.zip(actual_list).all? do |expected, actual|
230
- values_match?(expected, actual)
231
- end
232
- end
233
-
234
- # @private
235
- def self.hashes_match?(expected_hash, actual_hash)
236
- return false if expected_hash.size != actual_hash.size
237
-
238
- expected_hash.all? do |expected_key, expected_value|
239
- actual_value = actual_hash.fetch(expected_key) { return false }
240
- values_match?(expected_value, actual_value)
241
- end
242
- end
243
-
244
- private_class_method :arrays_match?, :hashes_match?
245
- end