cloudinary 1.9.1 → 1.20.0

Sign up to get free protection for your applications and to get access to all the features.
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