contentful_middleman 1.5.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b9f4cc17f613bedbda4d1c47204f5fb11212c2c1
4
- data.tar.gz: 27f6a703690c874159b13366a75be793a7e36c30
3
+ metadata.gz: b7204295d8cde9e391d33b7bc656a09779c00f77
4
+ data.tar.gz: 6cd99474e752542dd354c12600dbe0589a8109aa
5
5
  SHA512:
6
- metadata.gz: a92c016aed093ad6be4607c5720de5b6c58f826eb96c34280d9a2a3c36d0c3a8f113df3d307234d19acf67e13ae6d0a44f547cb165f3b576359553860eef128c
7
- data.tar.gz: e29f82913048e350ef55898d7a27ba27efe9b00a6ba00483914e0ca530ed117dd07fe19c058dc097d961f7280d0b9c331a7064bcb4cf703ed2a09df17b75e963
6
+ metadata.gz: f1df4c1735ccafbff1ac1364244fd32b2ebb0e024404d5c8cf379dc0d9ca52b342bef4dd6601bac76978299f35ffde77852d7f76a70175e9d4fd4519d35a874c
7
+ data.tar.gz: b51f7dc9c107f3be63332fa42c8acfc95b5fe3f9de0d167e54d65998055575cc193b6d5ac1ce9c1a2c81bcb857ba9df1a8cc330167cc92757777bd82ad6a62f3
data/.travis.yml CHANGED
@@ -1,7 +1,7 @@
1
1
  rvm:
2
- - 2.0.0
3
2
  - 2.1.2
4
3
  - 2.2.3
4
+ - 2.3.1
5
5
  - jruby
6
6
 
7
7
  script: "bundle exec rake test"
data/CHANGELOG.md CHANGED
@@ -1,6 +1,16 @@
1
1
  # Change Log
2
2
  ## Unreleased
3
3
 
4
+ ## 2.0.0
5
+
6
+ ## Changed
7
+ * Upgraded to 2.0.1 Ruby CDA SDK which solves a number of pre-existing bugs [#116](https://github.com/contentful/contentful_middleman/issues/116) [#113](https://github.com/contentful/contentful_middleman/issues/113) [#103](https://github.com/contentful/contentful_middleman/issues/103)
8
+ * Field IDs will now be `snake_cased` following the 2.0.1 Ruby CDA SDK Upgrade
9
+
10
+ ## Added
11
+ * Added `_meta` to all entries and assets [#117](https://github.com/contentful/contentful_middleman/issues/117) [#93](https://github.com/contentful/contentful_middleman/issues/93)
12
+ * Added `client_options` to extension options to control Client instantiation
13
+
4
14
  ## 1.5.0
5
15
 
6
16
  ### Added
@@ -0,0 +1,48 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at david.litvakb@gmail.com. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at [http://contributor-covenant.org/version/1/3/0/][version]
46
+
47
+ [homepage]: http://contributor-covenant.org
48
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/DEPLOYING.md ADDED
@@ -0,0 +1,38 @@
1
+ # Deploying to Specific Platforms
2
+
3
+ > This document is intended for documenting caveats for deploying in different platforms.
4
+ > It's contents are intended to be filled by users, submitting PR with their experiences, this will then be reviewed and merged if found it's indeed a best practice.
5
+
6
+ * Format of the contribution:
7
+
8
+ > # Name of the Platform
9
+ >
10
+ > ## Descriptive title of specific caveat
11
+ >
12
+ > > Description of the issue
13
+ >
14
+ > Steps to solve:
15
+ >
16
+ > 1. Do ...
17
+ > 2. Then ...
18
+ > 3. After ...
19
+ >
20
+ > Contributed by @your_username
21
+
22
+ ---
23
+
24
+ # Placeholder Platform
25
+
26
+ ## Placeholder Caveat
27
+
28
+ > Placeholder description
29
+
30
+ Steps to solve:
31
+
32
+ 1. Do ...
33
+ 2. Then ...
34
+ 3. After ...
35
+
36
+ > Contributed by @...
37
+
38
+ ---
data/Gemfile CHANGED
@@ -9,7 +9,4 @@ gemspec
9
9
 
10
10
  gem "yard"
11
11
 
12
- # Test tools
13
- gem "simplecov"
14
-
15
12
  gem "cane", :platforms => [:mri_19, :mri_20], :require => false
data/README.md CHANGED
@@ -54,6 +54,7 @@ Parameter | Description
54
54
  space | Hash with an user choosen name for the space as key and the space id as value
55
55
  access_token | Contentful Delivery API access token
56
56
  cda_query | Hash describing query configuration. See [contentful.rb](https://github.com/contentful/contentful.rb) for more info (look for filter options there). Note that by default only 100 entries will be fetched, this can be configured to up to 1000 entries using the `limit` option. Example: `f.cda_query = { limit: 1000 }`
57
+ cda_query | Hash describing client configuration. See [contentful.rb](https://github.com/contentful/contentful.rb#client-configuration-options) for more info. This option should commonly be used to change Rate Limit Management, Include Resolution, Logging and Proxies.
57
58
  content_types | Hash describing the mapping applied to entries of the imported content types
58
59
  default_locale | String with the value for the default locale for your space. Defaults to `'en-US'`.
59
60
  use_preview_api | Boolean to toggle the used API. Set it to `false` to use `cdn.contentful.com` (default value). Set it to `true` to use `preview.contentful.com`. More info in [the documentation](https://www.contentful.com/developers/documentation/content-delivery-api/#preview-api)
@@ -330,3 +331,7 @@ You can configure `:tries` and `:expires_in` in the `#with_preview` call like th
330
331
  <% end %>
331
332
  ```
332
333
 
334
+ ## Platform Specific Deployment Caveats
335
+
336
+ For platform specific issues, please look into the [DEPLOYING](./DEPLOYING.md) document. This document is expected to grow with user contributions.
337
+ Feel free to add your own discoveries to that file by issuing a Pull Request.
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
22
22
  s.add_dependency("middleman-core", ["~> 3.4"])
23
23
 
24
24
  # Additional dependencies
25
- s.add_dependency("contentful", '~> 1.0')
25
+ s.add_dependency("contentful", '~> 2.0', '>= 2.0.1')
26
26
  s.add_dependency("contentful-webhook-listener", '~> 0.1')
27
27
 
28
28
  s.add_development_dependency 'rubygems-tasks', '~> 0.2'
@@ -32,6 +32,7 @@ Gem::Specification.new do |s|
32
32
  s.add_development_dependency "rake"
33
33
  s.add_development_dependency "rspec"
34
34
  s.add_development_dependency "vcr"
35
+ s.add_development_dependency "simplecov"
35
36
  s.add_development_dependency 'webmock', '~> 1', '>= 1.17.3'
36
37
  s.add_development_dependency 'tins', '~> 1.6.0'
37
38
  end
@@ -25,6 +25,9 @@ module ContentfulMiddleman
25
25
  option :cda_query, {},
26
26
  'The conditions that are used on the Content Delivery API to query for blog posts'
27
27
 
28
+ option :client_options, {},
29
+ 'Additional configuration for the Contentful Client'
30
+
28
31
  option :content_types, {},
29
32
  'The mapping of Content Types names to ids'
30
33
 
@@ -67,7 +67,7 @@ module ContentfulMiddleman
67
67
  dynamic_entries: :auto,
68
68
  raise_errors: true,
69
69
  default_locale: options.default_locale
70
- }
70
+ }.merge(options.client_options)
71
71
 
72
72
  client_options[:api_url] = API_PREVIEW_URL if options.use_preview_api
73
73
  client_options
@@ -50,7 +50,7 @@ module ContentfulMiddleman
50
50
  map_location(value)
51
51
  when Contentful::Link
52
52
  map_link(value)
53
- when Contentful::DynamicEntry
53
+ when Contentful::Entry
54
54
  map_entry(value)
55
55
  when Array
56
56
  map_array(value, locale)
@@ -59,6 +59,15 @@ module ContentfulMiddleman
59
59
  end
60
60
  end
61
61
 
62
+ def map_asset_metadata(asset)
63
+ context = Context.new
64
+ context.updated_at = asset.sys[:updated_at].iso8601 unless asset.sys[:updated_at].nil?
65
+ context.created_at = asset.sys[:created_at].iso8601 unless asset.sys[:created_at].nil?
66
+ context.id = asset.sys[:id]
67
+
68
+ context
69
+ end
70
+
62
71
  def map_asset(asset, locale = nil)
63
72
  context = Context.new
64
73
  if locale
@@ -69,13 +78,26 @@ module ContentfulMiddleman
69
78
 
70
79
  context.title = asset.title unless context.has?(:title) && !context.title.nil?
71
80
  context.description = asset.description unless context.has?(:description) && !context.description.nil?
72
- context.url = asset.file.url unless asset.file.nil? || (context.has?(:url) && !context.url.nil?)
81
+ context.url = asset.url unless asset.file.nil? || (context.has?(:url) && !context.url.nil?)
82
+
83
+ context._meta = map_asset_metadata(asset)
84
+
85
+ context
86
+ end
87
+
88
+ def map_entry_metadata(entry)
89
+ context = Context.new
90
+ context.content_type_id = entry.sys[:content_type].id unless entry.sys[:content_type].nil?
91
+ context.updated_at = entry.sys[:updated_at].iso8601 unless entry.sys[:updated_at].nil?
92
+ context.created_at = entry.sys[:created_at].iso8601 unless entry.sys[:created_at].nil?
93
+ context.id = entry.sys[:id]
73
94
 
74
95
  context
75
96
  end
76
97
 
77
98
  def map_entry_full(entry, context)
78
99
  context.id = entry.id
100
+ context._meta = map_entry_metadata(entry)
79
101
 
80
102
  fields = has_multiple_locales? ? entry.fields_with_locales : entry.fields
81
103
 
@@ -90,19 +90,19 @@ module ContentfulMiddleman
90
90
 
91
91
  def cache(name, super_call, query = {}, id = '')
92
92
  mapping = CACHE_MAPPINGS[name]
93
+ query_copy = Marshal.load(Marshal.dump(query))
93
94
 
94
- if should_fetch_from_api?(name, query: query, id: id)
95
- instance_variable_get("@#{mapping[:cache]}")[cache_key(name, query, id)] ||= {}
96
- instance_variable_get("@#{mapping[:cache]}")[cache_key(name, query, id)][:tries] = 0
97
- instance_variable_get("@#{mapping[:cache]}")[cache_key(name, query, id)][:expires] = DateTime.now + @expires_in
98
-
95
+ if should_fetch_from_api?(name, query: query_copy, id: id)
99
96
  new_resources = super_call.call(query, id)
100
- instance_variable_get("@#{mapping[:cache]}")[cache_key(name, query, id)][:data] = new_resources
97
+ instance_variable_get("@#{mapping[:cache]}")[cache_key(name, query_copy, id)] ||= {}
98
+ instance_variable_get("@#{mapping[:cache]}")[cache_key(name, query_copy, id)][:tries] = 0
99
+ instance_variable_get("@#{mapping[:cache]}")[cache_key(name, query_copy, id)][:expires] = DateTime.now + @expires_in
100
+ instance_variable_get("@#{mapping[:cache]}")[cache_key(name, query_copy, id)][:data] = new_resources
101
101
  return new_resources
102
102
  end
103
103
 
104
- instance_variable_get("@#{mapping[:cache]}")[cache_key(name, query, id)][:tries] += 1
105
- instance_variable_get("@#{mapping[:cache]}")[cache_key(name, query, id)][:data]
104
+ instance_variable_get("@#{mapping[:cache]}")[cache_key(name, query_copy, id)][:tries] += 1
105
+ instance_variable_get("@#{mapping[:cache]}")[cache_key(name, query_copy, id)][:data]
106
106
  end
107
107
 
108
108
 
@@ -1,3 +1,3 @@
1
1
  module ContentfulMiddleman
2
- VERSION = "1.5.0"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -80,4 +80,19 @@ describe ContentfulMiddleman::Instance do
80
80
  end
81
81
  end
82
82
  end
83
+
84
+ describe 'client options' do
85
+ it 'respects the client configuration' do
86
+ options = OptionsDouble.new(client_options: {max_include_resolution_depth: 1})
87
+ extension = ExtensionDouble.new(options)
88
+ subject = described_class.new(extension)
89
+
90
+ vcr('instance/include_resolution_1') {
91
+ nyancat = subject.send(:client).entry('nyancat')
92
+
93
+ expect(nyancat.best_friend).to be_a ::Contentful::Entry
94
+ expect(nyancat.best_friend.best_friend).to be_a ::Contentful::Link
95
+ }
96
+ end
97
+ end
83
98
  end
@@ -37,8 +37,19 @@ describe ContentfulMiddleman::Mapper::Base do
37
37
 
38
38
  expected = {
39
39
  :id=>"6KntaYXaHSyIw8M6eo26OK",
40
+ :_meta=> {
41
+ :content_type_id=> 'dog',
42
+ :updated_at=> '2013-11-18T09:13:37+00:00',
43
+ :created_at=> '2013-11-06T09:45:27+00:00',
44
+ :id=> '6KntaYXaHSyIw8M6eo26OK'
45
+ },
40
46
  :name=>"Doge",
41
47
  :image=> {
48
+ :_meta=> {
49
+ :updated_at=> "2013-12-18T13:27:14+00:00",
50
+ :created_at=> "2013-11-06T09:45:10+00:00",
51
+ :id=> "1x0xpXu4pSGS4OukSyWGUK"
52
+ },
42
53
  :title=>"Doge",
43
54
  :description=>"nice picture",
44
55
  :url=> "//images.contentful.com/cfexampleapi/1x0xpXu4pSGS4OukSyWGUK/cc1239c6385428ef26f4180190532818/doge.jpg"
@@ -48,7 +59,7 @@ describe ContentfulMiddleman::Mapper::Base do
48
59
 
49
60
  subject.map(context, entries.first)
50
61
 
51
- expect(context.hashize).to eq(expected)
62
+ expect(context.hashize).to match(expected)
52
63
  end
53
64
 
54
65
  it 'maps entries with multiple locales' do
@@ -56,12 +67,23 @@ describe ContentfulMiddleman::Mapper::Base do
56
67
  expect(context.hashize).to eq({})
57
68
 
58
69
  expected = {
70
+ :_meta => {
71
+ :content_type_id=>"dog",
72
+ :updated_at=>"2013-11-18T09:13:37+00:00",
73
+ :created_at=>"2013-11-06T09:45:27+00:00",
74
+ :id=>"6KntaYXaHSyIw8M6eo26OK"
75
+ },
59
76
  :id=>"6KntaYXaHSyIw8M6eo26OK",
60
77
  :name=> {
61
78
  :'en-US'=>"Doge"
62
79
  },
63
- :image=>{
64
- :'en-US'=>{
80
+ :image=> {
81
+ :'en-US'=> {
82
+ :_meta=> {
83
+ :updated_at=> "2013-12-18T13:27:14+00:00",
84
+ :created_at=> "2013-11-06T09:45:10+00:00",
85
+ :id=>"1x0xpXu4pSGS4OukSyWGUK"
86
+ },
65
87
  :title=>"Doge",
66
88
  :description=>"nice picture",
67
89
  :url=>"//images.contentful.com/cfexampleapi/1x0xpXu4pSGS4OukSyWGUK/cc1239c6385428ef26f4180190532818/doge.jpg"
@@ -83,19 +105,40 @@ describe ContentfulMiddleman::Mapper::Base do
83
105
  expect(context.hashize).to eq({})
84
106
 
85
107
  expected = {
108
+ :_meta => {
109
+ :content_type_id=>"test",
110
+ :updated_at=>"2016-09-29T14:53:54+00:00",
111
+ :created_at=>"2016-09-29T14:53:54+00:00",
112
+ :id=>"42kEjzNj9mIci2eyGOISiQ"
113
+ },
86
114
  :id=>"42kEjzNj9mIci2eyGOISiQ",
87
115
  :image=>{
88
- :'en-US'=>{
116
+ :'en-US'=> {
117
+ :_meta=> {
118
+ :updated_at=> "2016-09-29T14:53:26+00:00",
119
+ :created_at=> "2016-09-29T14:53:26+00:00",
120
+ :id=> "6Rloj9MIxOwg0w2kqCaWS2"
121
+ },
89
122
  title: "image-view-1139205 960 720",
90
123
  description: nil,
91
124
  url: "//images.contentful.com/1sjfpsn7l90g/6Rloj9MIxOwg0w2kqCaWS2/464b740a98d711905545f77d56fa3b2b/image-view-1139205_960_720.jpg"
92
125
  },
93
126
  :es=>{
127
+ :_meta=> {
128
+ :updated_at=> "2016-09-29T14:53:26+00:00",
129
+ :created_at=> "2016-09-29T14:53:26+00:00",
130
+ :id=> "2WGPppy4laAWWgUiWG02SA"
131
+ },
94
132
  title: "background-image-967820 960 720",
95
133
  description: nil,
96
134
  url: "//images.contentful.com/1sjfpsn7l90g/2WGPppy4laAWWgUiWG02SA/3951271109e19ae45b21bb044b24b3ec/background-image-967820_960_720.jpg"
97
135
  },
98
- :zh=>{
136
+ :zh=> {
137
+ :_meta=> {
138
+ :updated_at=> "2016-09-29T14:53:26+00:00",
139
+ :created_at=> "2016-09-29T14:53:26+00:00",
140
+ :id=>"6zkhmrCizKuQUG0UmYKe4W"
141
+ },
99
142
  title: "image-view-1139204 960 720",
100
143
  description: nil,
101
144
  url: "//images.contentful.com/1sjfpsn7l90g/6zkhmrCizKuQUG0UmYKe4W/a8f90059b5bfd620791814f2c3edfaa4/image-view-1139204_960_720.jpg"
@@ -123,14 +166,30 @@ describe ContentfulMiddleman::Mapper::Base do
123
166
  expect(context.hashize).to eq({})
124
167
 
125
168
  expected = {
169
+ :_meta => {
170
+ :content_type_id=>"test",
171
+ :updated_at=>"2016-10-05T14:32:07+00:00",
172
+ :created_at=>"2016-10-05T14:32:07+00:00",
173
+ :id=>"2HjFERK39eeCYegCayUkMK"
174
+ },
126
175
  id: "2HjFERK39eeCYegCayUkMK",
127
176
  image: {
128
177
  :"en-US" => {
178
+ :_meta=> {
179
+ :updated_at=> "2016-10-05T14:31:36+00:00",
180
+ :created_at=> "2016-10-05T14:31:36+00:00",
181
+ :id=>"14bZJKTr6AoaGyeg4kYiWq"
182
+ },
129
183
  title: "EN Title",
130
184
  description: "EN Description",
131
185
  url: "//assets.contentful.com/bht13amj0fva/14bZJKTr6AoaGyeg4kYiWq/13f00bdf75c1320061ce471a3881e831/Flag_of_the_United_States.svg"
132
186
  },
133
187
  es: {
188
+ :_meta=> {
189
+ :updated_at=> "2016-10-05T14:31:36+00:00",
190
+ :created_at=> "2016-10-05T14:31:36+00:00",
191
+ :id=>"14bZJKTr6AoaGyeg4kYiWq"
192
+ },
134
193
  title: "ES Title",
135
194
  description: "ES Description",
136
195
  url: "//assets.contentful.com/bht13amj0fva/14bZJKTr6AoaGyeg4kYiWq/5501c98c296af77b9acba1146ea3e211/Flag_of_Spain.svg"
@@ -167,7 +226,13 @@ describe ContentfulMiddleman::Mapper::Base do
167
226
  context = ContentfulMiddleman::Context.new
168
227
 
169
228
  expect { subject.map(context, entry) }.not_to raise_error
170
- expect(context.hashize).to eq(id: 'foo')
229
+ expect(context.hashize).to eq({
230
+ :_meta=> {
231
+ :content_type_id=> 'foo_ct',
232
+ :id=> 'foo'
233
+ },
234
+ id: 'foo'
235
+ })
171
236
  end
172
237
 
173
238
  it 'should not fail on missing asset file - #85' do
@@ -185,7 +250,7 @@ describe ContentfulMiddleman::Mapper::Base do
185
250
  expect(entry_with_nil_file.one_media.file).to be_nil
186
251
 
187
252
  expect { subject.map(context, entry_with_nil_file) }.not_to raise_error
188
- expect(context.hashize[:oneMedia].keys.map(&:to_s)).not_to include('url')
253
+ expect(context.hashize[:one_media].keys.map(&:to_s)).not_to include('url')
189
254
  }
190
255
  end
191
256
 
@@ -203,11 +268,36 @@ describe ContentfulMiddleman::Mapper::Base do
203
268
  subject.map(context, entry_with_repeated_item)
204
269
  hash = YAML.load(context.to_yaml)
205
270
  expect(hash[:bars]).to match([
206
- { id: "1Xq3cu45qguO4Uiwc2yycY", name: "bar_1" },
207
- { id: "6jLRFVvafuM6E0QiCA8YMu", name: "bar_2" },
208
- { id: "1Xq3cu45qguO4Uiwc2yycY", name: "bar_1" }
271
+ {
272
+ :id=>"1Xq3cu45qguO4Uiwc2yycY",
273
+ :_meta=> {
274
+ :content_type_id=>"bar",
275
+ :updated_at=>"2016-12-12T13:40:58+00:00",
276
+ :created_at=>"2016-12-12T13:40:58+00:00",
277
+ :id=>"1Xq3cu45qguO4Uiwc2yycY"},
278
+ :name=>"bar_1"
279
+ },
280
+ {
281
+ :id=>"6jLRFVvafuM6E0QiCA8YMu",
282
+ :_meta=> {
283
+ :content_type_id=>"bar",
284
+ :updated_at=>"2016-12-12T13:41:05+00:00",
285
+ :created_at=>"2016-12-12T13:41:05+00:00",
286
+ :id=>"6jLRFVvafuM6E0QiCA8YMu"
287
+ },
288
+ :name=>"bar_2"
289
+ },
290
+ {
291
+ :id=>"1Xq3cu45qguO4Uiwc2yycY",
292
+ :_meta=> {
293
+ :content_type_id=>"bar",
294
+ :updated_at=>"2016-12-12T13:40:58+00:00",
295
+ :created_at=>"2016-12-12T13:40:58+00:00",
296
+ :id=>"1Xq3cu45qguO4Uiwc2yycY"
297
+ },
298
+ :name=>"bar_1"
299
+ }
209
300
  ])
210
-
211
301
  expect(hash[:bars].first).to eq(hash[:bars].last)
212
302
  }
213
303
  end