govuk_content_models 6.3.0 → 6.4.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.
- data/CHANGELOG.md +4 -0
- data/app/models/artefact.rb +1 -0
- data/app/models/specialist_document_edition.rb +16 -0
- data/app/validators/slug_validator.rb +130 -34
- data/lib/govuk_content_models/test_helpers/factories.rb +6 -0
- data/lib/govuk_content_models/version.rb +1 -1
- data/test/models/specialist_document_edition_test.rb +33 -0
- data/test/validators/slug_validator_test.rb +35 -17
- metadata +7 -4
data/CHANGELOG.md
CHANGED
data/app/models/artefact.rb
CHANGED
|
@@ -78,6 +78,7 @@ class Artefact
|
|
|
78
78
|
"smartanswers" => ["smart-answer"],
|
|
79
79
|
"custom-application" => ["custom-application"], # In this case the owning_app is overriden. eg calendars, licencefinder
|
|
80
80
|
"travel-advice-publisher" => ["travel-advice"],
|
|
81
|
+
"specialist-publisher" => ["specialist-document"],
|
|
81
82
|
"whitehall" => ["case_study",
|
|
82
83
|
"consultation",
|
|
83
84
|
"detailed_guide",
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class SpecialistDocumentEdition < Edition
|
|
2
|
+
field :summary, type: String
|
|
3
|
+
field :body, type: String
|
|
4
|
+
field :opened_date, type: Date
|
|
5
|
+
field :closed_date, type: Date
|
|
6
|
+
field :case_type, type: String
|
|
7
|
+
field :case_state, type: String
|
|
8
|
+
field :market_sector, type: String
|
|
9
|
+
field :outcome_type, type: String
|
|
10
|
+
|
|
11
|
+
GOVSPEAK_FIELDS = Edition::GOVSPEAK_FIELDS + [:body]
|
|
12
|
+
|
|
13
|
+
def whole_body
|
|
14
|
+
self.body
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -1,53 +1,149 @@
|
|
|
1
1
|
class SlugValidator < ActiveModel::EachValidator
|
|
2
2
|
# implement the method called during validation
|
|
3
3
|
def validate_each(record, attribute, value)
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
validators = [
|
|
5
|
+
DonePageValidator,
|
|
6
|
+
ForeignTravelAdvicePageValidator,
|
|
7
|
+
HelpPageValidator,
|
|
8
|
+
DetailedGuidePageValidator,
|
|
9
|
+
GovernmentPageValidator,
|
|
10
|
+
SpecialistDocumentPageValidator,
|
|
11
|
+
DefaultValidator
|
|
12
|
+
].map { |klass| klass.new(record, attribute, value) }
|
|
13
|
+
|
|
14
|
+
validators.find(&:applicable?).validate!
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
protected
|
|
18
|
+
class InstanceValidator < Struct.new(:record, :attribute, :value)
|
|
19
|
+
def starts_with?(expected_prefix)
|
|
20
|
+
value.to_s[0...expected_prefix.size] == expected_prefix
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def of_kind?(expected_kind)
|
|
24
|
+
record.respond_to?(:kind) && [*expected_kind].include?(record.kind)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def url_after_first_slash
|
|
28
|
+
value.to_s.split('/', 2)[1]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def url_after_first_slash_is_valid_slug!
|
|
32
|
+
if !valid_slug?(url_after_first_slash)
|
|
33
|
+
record.errors[attribute] << "must be usable in a url"
|
|
14
34
|
end
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def url_parts
|
|
38
|
+
value.to_s.split("/")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def valid_slug?(url_part)
|
|
42
|
+
ActiveSupport::Inflector.parameterize(url_part.to_s) == url_part.to_s
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class DonePageValidator < InstanceValidator
|
|
47
|
+
def applicable?
|
|
48
|
+
starts_with?("done/")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def validate!
|
|
52
|
+
url_after_first_slash_is_valid_slug!
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class ForeignTravelAdvicePageValidator < InstanceValidator
|
|
57
|
+
def applicable?
|
|
58
|
+
starts_with?("foreign-travel-advice/") && of_kind?('travel-advice')
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def validate!
|
|
62
|
+
url_after_first_slash_is_valid_slug!
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
class HelpPageValidator < InstanceValidator
|
|
67
|
+
def applicable?
|
|
68
|
+
of_kind?('help_page')
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def validate!
|
|
72
|
+
record.errors[attribute] << "Help page slugs must have a help/ prefix" unless starts_with?("help/")
|
|
73
|
+
url_after_first_slash_is_valid_slug!
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class WhitehallFormatValidator < InstanceValidator
|
|
78
|
+
def url_parts
|
|
79
|
+
normalize_last_part_for_friendly_id(super)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def validate!
|
|
83
|
+
unless url_parts.all? { |url_part| valid_slug?(url_part) }
|
|
84
|
+
record.errors[attribute] << "must be usable in a URL"
|
|
28
85
|
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
protected
|
|
29
89
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
end
|
|
90
|
+
def normalize_last_part_for_friendly_id(url_parts)
|
|
91
|
+
url_parts[0...-1] + url_parts[-1..-1].map do |url_part|
|
|
92
|
+
normalize_for_friendly_id(url_part)
|
|
34
93
|
end
|
|
35
94
|
end
|
|
36
95
|
|
|
37
|
-
|
|
38
|
-
|
|
96
|
+
def normalize_for_friendly_id(url_part)
|
|
97
|
+
url_part.sub('--', '-')
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
class DetailedGuidePageValidator < WhitehallFormatValidator
|
|
103
|
+
def applicable?
|
|
104
|
+
of_kind?('detailed_guide')
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
class GovernmentPageValidator < WhitehallFormatValidator
|
|
109
|
+
def applicable?
|
|
110
|
+
record.respond_to?(:kind) && prefixed_whitehall_format_names.include?(record.kind)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def validate!
|
|
114
|
+
record.errors[attribute] << "Inside Government slugs must have a government/ prefix" unless starts_with?('government/')
|
|
115
|
+
super
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
protected
|
|
119
|
+
def prefixed_whitehall_format_names
|
|
120
|
+
Artefact::FORMATS_BY_DEFAULT_OWNING_APP["whitehall"] - ["detailed_guide"]
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
class SpecialistDocumentPageValidator < WhitehallFormatValidator
|
|
125
|
+
def applicable?
|
|
126
|
+
of_kind?('specialist-document')
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def validate!
|
|
130
|
+
unless url_parts.size == 2
|
|
131
|
+
record.errors[attribute] << "must be of form <finder-slug>/<specialist-document-slug>"
|
|
132
|
+
end
|
|
133
|
+
unless url_parts.all? { |url_part| valid_slug?(url_part) }
|
|
39
134
|
record.errors[attribute] << "must be usable in a URL"
|
|
40
135
|
end
|
|
41
136
|
end
|
|
42
137
|
end
|
|
43
138
|
|
|
44
|
-
|
|
45
|
-
def
|
|
46
|
-
|
|
139
|
+
class DefaultValidator < InstanceValidator
|
|
140
|
+
def applicable?
|
|
141
|
+
true
|
|
47
142
|
end
|
|
48
143
|
|
|
49
|
-
def
|
|
50
|
-
|
|
144
|
+
def validate!
|
|
145
|
+
record.errors[attribute] << "must be usable in a url" unless valid_slug?(value)
|
|
51
146
|
end
|
|
147
|
+
end
|
|
52
148
|
|
|
53
149
|
end
|
|
@@ -213,6 +213,12 @@ FactoryGirl.define do
|
|
|
213
213
|
end
|
|
214
214
|
end
|
|
215
215
|
|
|
216
|
+
factory :specialist_document_edition do
|
|
217
|
+
sequence(:slug) {|n| "test-specialist-document-#{n}" }
|
|
218
|
+
sequence(:title) {|n| "Test Specialist Document #{n}" }
|
|
219
|
+
summary "My summary"
|
|
220
|
+
end
|
|
221
|
+
|
|
216
222
|
factory :simple_smart_answer_edition do
|
|
217
223
|
panopticon_id {
|
|
218
224
|
a = create(:artefact)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class SpecialistDocumentEditionTest < ActiveSupport::TestCase
|
|
6
|
+
should "have correct fields" do
|
|
7
|
+
fields = {
|
|
8
|
+
slug: 'cma-cases/merger-investigation-2014',
|
|
9
|
+
title: "Merger Investigation 2014",
|
|
10
|
+
summary: "This is the summary of stuff going on in the Merger Investigation 2014",
|
|
11
|
+
state: "published"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
edition = SpecialistDocumentEdition.new(fields)
|
|
15
|
+
|
|
16
|
+
assert_equal fields[:title], edition.title
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
should "be persistable" do
|
|
20
|
+
artefact = FactoryGirl.create(:artefact)
|
|
21
|
+
edition = SpecialistDocumentEdition.create!(
|
|
22
|
+
slug: 'cma-cases/merger-investigation-2014',
|
|
23
|
+
title: "Merger Investigation 2014",
|
|
24
|
+
summary: "This is the summary of stuff going on in the Merger Investigation 2014",
|
|
25
|
+
state: "published",
|
|
26
|
+
panopticon_id: artefact.id
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
found = SpecialistDocumentEdition.where(slug: edition.slug).first
|
|
30
|
+
assert_equal found.attributes, edition.attributes
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
@@ -12,31 +12,49 @@ class SlugTest < ActiveSupport::TestCase
|
|
|
12
12
|
validates :slug, presence: true, uniqueness: true, slug: true
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
def document_with_slug(slug, override_options = {})
|
|
16
|
+
default_options = {
|
|
17
|
+
name: "Test",
|
|
18
|
+
slug: slug
|
|
19
|
+
}
|
|
20
|
+
Dummy.new(default_options.merge(override_options))
|
|
21
|
+
end
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
context "default slugs" do
|
|
24
|
+
should "reject url paths" do
|
|
25
|
+
refute document_with_slug("path/not-allowed").valid?
|
|
26
|
+
end
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
assert
|
|
28
|
+
should "allow a normal slug" do
|
|
29
|
+
assert document_with_slug("normal-slug").valid?
|
|
27
30
|
end
|
|
31
|
+
end
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
context "Help pages" do
|
|
34
|
+
should "must start with help/" do
|
|
35
|
+
refute document_with_slug("test", kind: "help_page").valid?
|
|
36
|
+
assert document_with_slug("help/test", kind: "help_page").valid?
|
|
37
|
+
end
|
|
38
|
+
end
|
|
32
39
|
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
context "Inside government slugs" do
|
|
41
|
+
should "allow slug starting government/" do
|
|
42
|
+
refute document_with_slug("test", kind: "policy").valid?
|
|
43
|
+
assert document_with_slug("government/test", kind: "policy").valid?
|
|
35
44
|
end
|
|
36
45
|
|
|
37
46
|
should "allow friendly_id suffixes to pass" do
|
|
38
|
-
|
|
39
|
-
|
|
47
|
+
assert document_with_slug("government/policy/test--3", kind: "policy").valid?
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
context "Specialist documents" do
|
|
52
|
+
should "all url nested one level deep" do
|
|
53
|
+
assert document_with_slug("some-finder/my-specialist-document", kind: "specialist-document").valid?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
should "not allow deeper nesting" do
|
|
57
|
+
refute document_with_slug("some-finder/my-specialist-document/not-allowed", kind: "specialist-document").valid?
|
|
40
58
|
end
|
|
41
59
|
end
|
|
42
60
|
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: 6.
|
|
4
|
+
version: 6.4.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2014-02-
|
|
12
|
+
date: 2014-02-11 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: bson_ext
|
|
@@ -377,6 +377,7 @@ files:
|
|
|
377
377
|
- app/models/simple_smart_answer_edition.rb
|
|
378
378
|
- app/models/simple_smart_answer_edition/node.rb
|
|
379
379
|
- app/models/simple_smart_answer_edition/node/option.rb
|
|
380
|
+
- app/models/specialist_document_edition.rb
|
|
380
381
|
- app/models/tag.rb
|
|
381
382
|
- app/models/transaction_edition.rb
|
|
382
383
|
- app/models/travel_advice_edition.rb
|
|
@@ -426,6 +427,7 @@ files:
|
|
|
426
427
|
- test/models/simple_smart_answer_edition_test.rb
|
|
427
428
|
- test/models/simple_smart_answer_node_test.rb
|
|
428
429
|
- test/models/simple_smart_answer_option_test.rb
|
|
430
|
+
- test/models/specialist_document_edition_test.rb
|
|
429
431
|
- test/models/tag_test.rb
|
|
430
432
|
- test/models/time_zone_test.rb
|
|
431
433
|
- test/models/transaction_edition_test.rb
|
|
@@ -454,7 +456,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
454
456
|
version: '0'
|
|
455
457
|
segments:
|
|
456
458
|
- 0
|
|
457
|
-
hash:
|
|
459
|
+
hash: 2506025069756162727
|
|
458
460
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
459
461
|
none: false
|
|
460
462
|
requirements:
|
|
@@ -463,7 +465,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
463
465
|
version: '0'
|
|
464
466
|
segments:
|
|
465
467
|
- 0
|
|
466
|
-
hash:
|
|
468
|
+
hash: 2506025069756162727
|
|
467
469
|
requirements: []
|
|
468
470
|
rubyforge_project:
|
|
469
471
|
rubygems_version: 1.8.23
|
|
@@ -498,6 +500,7 @@ test_files:
|
|
|
498
500
|
- test/models/simple_smart_answer_edition_test.rb
|
|
499
501
|
- test/models/simple_smart_answer_node_test.rb
|
|
500
502
|
- test/models/simple_smart_answer_option_test.rb
|
|
503
|
+
- test/models/specialist_document_edition_test.rb
|
|
501
504
|
- test/models/tag_test.rb
|
|
502
505
|
- test/models/time_zone_test.rb
|
|
503
506
|
- test/models/transaction_edition_test.rb
|