govuk_content_models 29.1.1 → 29.1.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/app/models/artefact.rb +12 -0
- data/app/models/prerendered_entity.rb +13 -0
- data/app/models/rendered_manual.rb +17 -0
- data/app/models/rendered_specialist_document.rb +19 -0
- data/app/validators/slug_validator.rb +59 -0
- data/lib/govuk_content_models/test_helpers/factories.rb +19 -0
- data/lib/govuk_content_models/version.rb +1 -1
- data/test/fixtures/specialist_document_fixtures.rb +16 -0
- data/test/models/artefact_test.rb +9 -0
- data/test/models/prerendered_entity_tests.rb +46 -0
- data/test/models/rendered_manual_test.rb +10 -0
- data/test/models/rendered_specialist_document_test.rb +12 -0
- data/test/validators/slug_validator_test.rb +26 -0
- metadata +12 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ab1abf4484762f870e3d8e538b706e5cd426a66d
|
|
4
|
+
data.tar.gz: b6545c3d24622aaf2b7d912bec0612063dc7efad
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7e6a87f32f108d5ad7fde9fbcd35a962ff99f15cc93fddb9bc72fb02e8ef3b10f7a803eb6d30c2be0da930b434a125e6313188a13509fc43041a2de2ca965be1
|
|
7
|
+
data.tar.gz: d264ff8d7bdca85773d58504fcd3e2e4dbafc09df865638bad3386e0715bea87e779ce8e227eca83c8b93443fd2f8d17262699afcc1ad2267b07ac8f9e237d87
|
data/CHANGELOG.md
CHANGED
data/app/models/artefact.rb
CHANGED
|
@@ -37,6 +37,7 @@ class Artefact
|
|
|
37
37
|
field "publication_id", type: String
|
|
38
38
|
field "description", type: String
|
|
39
39
|
field "state", type: String, default: "draft"
|
|
40
|
+
field "specialist_body", type: String
|
|
40
41
|
field "language", type: String, default: "en"
|
|
41
42
|
field "need_extended_font", type: Boolean, default: false
|
|
42
43
|
field "latest_change_note", type: String
|
|
@@ -74,6 +75,17 @@ class Artefact
|
|
|
74
75
|
"smartanswers" => ["smart-answer"],
|
|
75
76
|
"custom-application" => ["custom-application"], # In this case the owning_app is overriden. eg calendars, licencefinder
|
|
76
77
|
"travel-advice-publisher" => ["travel-advice"],
|
|
78
|
+
"specialist-publisher" => ["aaib_report",
|
|
79
|
+
"cma_case",
|
|
80
|
+
"countryside_stewardship_grant",
|
|
81
|
+
"drug_safety_update",
|
|
82
|
+
"european_structural_investment_fund",
|
|
83
|
+
"international_development_fund",
|
|
84
|
+
"maib_report",
|
|
85
|
+
"manual",
|
|
86
|
+
"medical_safety_alert",
|
|
87
|
+
"raib_report",
|
|
88
|
+
"vehicle_recalls_and_faults_alert"],
|
|
77
89
|
"finder-api" => ["finder",
|
|
78
90
|
"finder_email_signup"],
|
|
79
91
|
"whitehall" => ["announcement",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module PrerenderedEntity
|
|
2
|
+
def create_or_update_by_slug!(attributes)
|
|
3
|
+
find_or_initialize_by(
|
|
4
|
+
slug: attributes.fetch(:slug)
|
|
5
|
+
).tap do |doc|
|
|
6
|
+
doc.update_attributes!(attributes)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def find_by_slug(slug)
|
|
11
|
+
where(slug: slug).first
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require "prerendered_entity"
|
|
2
|
+
|
|
3
|
+
class RenderedManual
|
|
4
|
+
include Mongoid::Document
|
|
5
|
+
include Mongoid::Timestamps
|
|
6
|
+
extend PrerenderedEntity
|
|
7
|
+
|
|
8
|
+
field :manual_id, type: String
|
|
9
|
+
field :slug, type: String
|
|
10
|
+
field :title, type: String
|
|
11
|
+
field :summary, type: String
|
|
12
|
+
field :section_groups, type: Array
|
|
13
|
+
|
|
14
|
+
index "slug", unique: true
|
|
15
|
+
|
|
16
|
+
validates_uniqueness_of :slug
|
|
17
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require "prerendered_entity"
|
|
2
|
+
|
|
3
|
+
class RenderedSpecialistDocument
|
|
4
|
+
include Mongoid::Document
|
|
5
|
+
include Mongoid::Timestamps
|
|
6
|
+
extend PrerenderedEntity
|
|
7
|
+
|
|
8
|
+
field :slug, type: String
|
|
9
|
+
field :title, type: String
|
|
10
|
+
field :summary, type: String
|
|
11
|
+
field :body, type: String
|
|
12
|
+
field :published_at, type: DateTime
|
|
13
|
+
|
|
14
|
+
field :details, type: Hash
|
|
15
|
+
|
|
16
|
+
index "slug", unique: true
|
|
17
|
+
|
|
18
|
+
validates :slug, uniqueness: true
|
|
19
|
+
end
|
|
@@ -7,6 +7,8 @@ class SlugValidator < ActiveModel::EachValidator
|
|
|
7
7
|
HelpPageValidator,
|
|
8
8
|
FinderEmailSignupValidator,
|
|
9
9
|
GovernmentPageValidator,
|
|
10
|
+
ManualPageValidator,
|
|
11
|
+
SpecialistDocumentPageValidator,
|
|
10
12
|
BrowsePageValidator,
|
|
11
13
|
DetailedGuideValidator,
|
|
12
14
|
DefaultValidator
|
|
@@ -115,6 +117,63 @@ protected
|
|
|
115
117
|
end
|
|
116
118
|
end
|
|
117
119
|
|
|
120
|
+
class ManualPageValidator < InstanceValidator
|
|
121
|
+
def applicable?
|
|
122
|
+
of_kind?('manual')
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def validate!
|
|
126
|
+
validate_number_of_parts!
|
|
127
|
+
validate_guidance_prefix!
|
|
128
|
+
validate_parts_as_slugs!
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
private
|
|
132
|
+
def validate_number_of_parts!
|
|
133
|
+
unless [2, 3].include?(url_parts.size)
|
|
134
|
+
record.errors[attribute] << 'must contains two or three path parts'
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def validate_guidance_prefix!
|
|
139
|
+
unless starts_with?('guidance/')
|
|
140
|
+
record.errors[attribute] << 'must have a guidance/ prefix'
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def validate_parts_as_slugs!
|
|
145
|
+
unless url_parts.all? { |url_part| valid_slug?(url_part) }
|
|
146
|
+
record.errors[attribute] << 'must be usable in a URL'
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
class SpecialistDocumentPageValidator < InstanceValidator
|
|
152
|
+
def applicable?
|
|
153
|
+
of_kind?(acceptable_formats)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def validate!
|
|
157
|
+
unless url_parts.size == 2
|
|
158
|
+
record.errors[attribute] << "must be of form <finder-slug>/<specialist-document-slug>"
|
|
159
|
+
end
|
|
160
|
+
unless url_parts.all? { |url_part| valid_slug?(url_part) }
|
|
161
|
+
record.errors[attribute] << "must be usable in a URL"
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
private
|
|
166
|
+
def acceptable_formats
|
|
167
|
+
Artefact::FORMATS_BY_DEFAULT_OWNING_APP["specialist-publisher"] - unacceptable_formats
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def unacceptable_formats
|
|
171
|
+
[
|
|
172
|
+
"manual",
|
|
173
|
+
]
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
118
177
|
class BrowsePageValidator < InstanceValidator
|
|
119
178
|
def applicable?
|
|
120
179
|
of_kind?('specialist_sector')
|
|
@@ -252,6 +252,25 @@ FactoryGirl.define do
|
|
|
252
252
|
end
|
|
253
253
|
end
|
|
254
254
|
|
|
255
|
+
factory :rendered_specialist_document do
|
|
256
|
+
sequence(:slug) {|n| "test-rendered-specialist-document-#{n}" }
|
|
257
|
+
sequence(:title) {|n| "Test Rendered Specialist Document #{n}" }
|
|
258
|
+
summary "My summary"
|
|
259
|
+
body "<p>My body</p>"
|
|
260
|
+
details({
|
|
261
|
+
"opened_date" => "2013-04-20",
|
|
262
|
+
"market_sector" => "some-market-sector",
|
|
263
|
+
"case_type" => "a-case-type",
|
|
264
|
+
"case_state" => "open",
|
|
265
|
+
})
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
factory :rendered_manual do
|
|
269
|
+
sequence(:slug) {|n| "test-rendered-manual-#{n}" }
|
|
270
|
+
sequence(:title) {|n| "Test Rendered Manual #{n}" }
|
|
271
|
+
summary "My summary"
|
|
272
|
+
end
|
|
273
|
+
|
|
255
274
|
factory :simple_smart_answer_edition, :parent => :edition, :class => "SimpleSmartAnswerEdition" do
|
|
256
275
|
title "Simple smart answer"
|
|
257
276
|
body "Introduction to the smart answer"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module SpecialistDocumentFixtures
|
|
2
|
+
def basic_specialist_document_fields
|
|
3
|
+
{
|
|
4
|
+
slug: 'cma-cases/merger-investigation-2014',
|
|
5
|
+
title: "Merger Investigation 2014",
|
|
6
|
+
summary: "This is the summary of stuff going on in the Merger Investigation 2014",
|
|
7
|
+
state: "published",
|
|
8
|
+
body: "A body",
|
|
9
|
+
opened_date: '2012-04-21',
|
|
10
|
+
document_id: 'a-document-id',
|
|
11
|
+
market_sector: 'oil-and-gas',
|
|
12
|
+
case_type: 'some-case-type',
|
|
13
|
+
case_state: 'open'
|
|
14
|
+
}
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -424,6 +424,15 @@ class ArtefactTest < ActiveSupport::TestCase
|
|
|
424
424
|
assert artefact.any_editions_published?
|
|
425
425
|
end
|
|
426
426
|
|
|
427
|
+
test "should have a specialist_body field present for markdown content" do
|
|
428
|
+
artefact = Artefact.create!(slug: "parent", name: "Harry Potter", kind: "guide", owning_app: "x")
|
|
429
|
+
refute_includes artefact.attributes, "specialist_body"
|
|
430
|
+
|
|
431
|
+
artefact.specialist_body = "Something wicked this way comes"
|
|
432
|
+
assert_includes artefact.attributes, "specialist_body"
|
|
433
|
+
assert_equal "Something wicked this way comes", artefact.specialist_body
|
|
434
|
+
end
|
|
435
|
+
|
|
427
436
|
test "should have 'video' as a supported FORMAT" do
|
|
428
437
|
assert_includes Artefact::FORMATS, "video"
|
|
429
438
|
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# include in a test class and define a #model_class instance method
|
|
2
|
+
|
|
3
|
+
module PrerenderedEntityTests
|
|
4
|
+
def test_duplicate_slug_not_allowed
|
|
5
|
+
model_class.create(slug: "my-slug")
|
|
6
|
+
second = model_class.create(slug: "my-slug")
|
|
7
|
+
|
|
8
|
+
refute second.valid?
|
|
9
|
+
assert_equal 1, model_class.count
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_has_no_govspeak_fields
|
|
13
|
+
refute model_class.const_defined?(:GOVSPEAK_FIELDS)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_create_or_update_by_slug
|
|
17
|
+
slug = "a-slug"
|
|
18
|
+
original_body = "Original body"
|
|
19
|
+
|
|
20
|
+
version1_attrs= {
|
|
21
|
+
slug: slug,
|
|
22
|
+
body: original_body,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
created = model_class.create_or_update_by_slug!(version1_attrs)
|
|
26
|
+
|
|
27
|
+
assert created.is_a?(model_class)
|
|
28
|
+
assert created.persisted?
|
|
29
|
+
|
|
30
|
+
version2_attrs = version1_attrs.merge(
|
|
31
|
+
body: "Updated body",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
version2 = model_class.create_or_update_by_slug!(version2_attrs)
|
|
35
|
+
|
|
36
|
+
assert version2.persisted?
|
|
37
|
+
assert_equal "Updated body", version2.body
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_find_by_slug
|
|
41
|
+
created = model_class.create!(slug: "find-by-this-slug")
|
|
42
|
+
found = model_class.find_by_slug("find-by-this-slug")
|
|
43
|
+
|
|
44
|
+
assert_equal created, found
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
require "fixtures/specialist_document_fixtures"
|
|
3
|
+
require "models/prerendered_entity_tests"
|
|
4
|
+
|
|
5
|
+
class RenderedSpecialistDocumentTest < ActiveSupport::TestCase
|
|
6
|
+
include SpecialistDocumentFixtures
|
|
7
|
+
include PrerenderedEntityTests
|
|
8
|
+
|
|
9
|
+
def model_class
|
|
10
|
+
RenderedSpecialistDocument
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -76,6 +76,16 @@ class SlugTest < ActiveSupport::TestCase
|
|
|
76
76
|
end
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
+
context "Specialist documents" do
|
|
80
|
+
should "all url nested one level deep" do
|
|
81
|
+
assert document_with_slug("some-finder/my-specialist-document", kind: "cma_case").valid?;
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
should "not allow deeper nesting" do
|
|
85
|
+
refute document_with_slug("some-finder/my-specialist-document/not-allowed", kind: "cma_case").valid?
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
79
89
|
context "Specialist sector browse pages" do
|
|
80
90
|
should "allow a single path part" do
|
|
81
91
|
assert document_with_slug("oil-and-gas", kind: "specialist_sector").valid?
|
|
@@ -108,4 +118,20 @@ class SlugTest < ActiveSupport::TestCase
|
|
|
108
118
|
end
|
|
109
119
|
#TODO: disallow this once guidance migration has been complete
|
|
110
120
|
end
|
|
121
|
+
|
|
122
|
+
context "Manual pages" do
|
|
123
|
+
should "allow slugs starting guidance/" do
|
|
124
|
+
refute document_with_slug("manuals/a-manual", kind: "manual").valid?
|
|
125
|
+
assert document_with_slug("guidance/a-manual", kind: "manual").valid?
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
should "allow two or three path parts" do
|
|
129
|
+
refute document_with_slug("guidance", kind: "manual").valid?
|
|
130
|
+
assert document_with_slug("guidance/a-manual", kind: "manual").valid?
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
should "not allow invalid path segments" do
|
|
134
|
+
refute document_with_slug("guidance/bad.manual.slug", kind: "manual").valid?
|
|
135
|
+
end
|
|
136
|
+
end
|
|
111
137
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: govuk_content_models
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 29.1.
|
|
4
|
+
version: 29.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Paul Battley
|
|
@@ -307,7 +307,10 @@ files:
|
|
|
307
307
|
- app/models/part.rb
|
|
308
308
|
- app/models/parted.rb
|
|
309
309
|
- app/models/place_edition.rb
|
|
310
|
+
- app/models/prerendered_entity.rb
|
|
310
311
|
- app/models/programme_edition.rb
|
|
312
|
+
- app/models/rendered_manual.rb
|
|
313
|
+
- app/models/rendered_specialist_document.rb
|
|
311
314
|
- app/models/simple_smart_answer_edition.rb
|
|
312
315
|
- app/models/simple_smart_answer_edition/node.rb
|
|
313
316
|
- app/models/simple_smart_answer_edition/node/option.rb
|
|
@@ -357,6 +360,7 @@ files:
|
|
|
357
360
|
- lib/govuk_content_models/version.rb
|
|
358
361
|
- lib/mongoid/monkey_patches.rb
|
|
359
362
|
- test/fixtures/contactotron_api_response.json
|
|
363
|
+
- test/fixtures/specialist_document_fixtures.rb
|
|
360
364
|
- test/fixtures/uploads/image.jpg
|
|
361
365
|
- test/models/action_test.rb
|
|
362
366
|
- test/models/artefact_action_test.rb
|
|
@@ -384,6 +388,9 @@ files:
|
|
|
384
388
|
- test/models/local_transaction_edition_test.rb
|
|
385
389
|
- test/models/overview_dashboard_test.rb
|
|
386
390
|
- test/models/parted_test.rb
|
|
391
|
+
- test/models/prerendered_entity_tests.rb
|
|
392
|
+
- test/models/rendered_manual_test.rb
|
|
393
|
+
- test/models/rendered_specialist_document_test.rb
|
|
387
394
|
- test/models/simple_smart_answer_edition_test.rb
|
|
388
395
|
- test/models/simple_smart_answer_node_test.rb
|
|
389
396
|
- test/models/simple_smart_answer_option_test.rb
|
|
@@ -429,6 +436,7 @@ specification_version: 4
|
|
|
429
436
|
summary: Shared models for Panopticon and Publisher, as a Rails Engine
|
|
430
437
|
test_files:
|
|
431
438
|
- test/fixtures/contactotron_api_response.json
|
|
439
|
+
- test/fixtures/specialist_document_fixtures.rb
|
|
432
440
|
- test/fixtures/uploads/image.jpg
|
|
433
441
|
- test/models/action_test.rb
|
|
434
442
|
- test/models/artefact_action_test.rb
|
|
@@ -456,6 +464,9 @@ test_files:
|
|
|
456
464
|
- test/models/local_transaction_edition_test.rb
|
|
457
465
|
- test/models/overview_dashboard_test.rb
|
|
458
466
|
- test/models/parted_test.rb
|
|
467
|
+
- test/models/prerendered_entity_tests.rb
|
|
468
|
+
- test/models/rendered_manual_test.rb
|
|
469
|
+
- test/models/rendered_specialist_document_test.rb
|
|
459
470
|
- test/models/simple_smart_answer_edition_test.rb
|
|
460
471
|
- test/models/simple_smart_answer_node_test.rb
|
|
461
472
|
- test/models/simple_smart_answer_option_test.rb
|