govuk_content_models 6.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.ruby-version +1 -0
- data/.travis.yml +14 -0
- data/CONTRIBUTING.md +22 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +5 -0
- data/Rakefile +46 -0
- data/app/models/action.rb +60 -0
- data/app/models/answer_edition.rb +13 -0
- data/app/models/artefact.rb +341 -0
- data/app/models/artefact_action.rb +27 -0
- data/app/models/artefact_external_link.rb +15 -0
- data/app/models/business_support/business_size.rb +14 -0
- data/app/models/business_support/business_type.rb +14 -0
- data/app/models/business_support/location.rb +14 -0
- data/app/models/business_support/purpose.rb +14 -0
- data/app/models/business_support/sector.rb +14 -0
- data/app/models/business_support/stage.rb +14 -0
- data/app/models/business_support/support_type.rb +14 -0
- data/app/models/business_support_edition.rb +69 -0
- data/app/models/campaign_edition.rb +72 -0
- data/app/models/completed_transaction_edition.rb +14 -0
- data/app/models/curated_list.rb +32 -0
- data/app/models/edition.rb +286 -0
- data/app/models/expectant.rb +21 -0
- data/app/models/expectation.rb +12 -0
- data/app/models/guide_edition.rb +19 -0
- data/app/models/help_page_edition.rb +13 -0
- data/app/models/licence_edition.rb +35 -0
- data/app/models/local_authority.rb +58 -0
- data/app/models/local_interaction.rb +20 -0
- data/app/models/local_service.rb +49 -0
- data/app/models/local_transaction_edition.rb +49 -0
- data/app/models/overview_dashboard.rb +25 -0
- data/app/models/part.rb +28 -0
- data/app/models/parted.rb +32 -0
- data/app/models/place_edition.rb +20 -0
- data/app/models/programme_edition.rb +26 -0
- data/app/models/simple_smart_answer_edition.rb +66 -0
- data/app/models/simple_smart_answer_edition/node.rb +40 -0
- data/app/models/simple_smart_answer_edition/node/option.rb +31 -0
- data/app/models/tag.rb +88 -0
- data/app/models/transaction_edition.rb +28 -0
- data/app/models/travel_advice_edition.rb +177 -0
- data/app/models/user.rb +54 -0
- data/app/models/video_edition.rb +24 -0
- data/app/models/workflow.rb +217 -0
- data/app/models/workflow_actor.rb +141 -0
- data/app/traits/attachable.rb +60 -0
- data/app/traits/govspeak_smart_quotes_fixer.rb +19 -0
- data/app/traits/taggable.rb +113 -0
- data/app/validators/safe_html.rb +33 -0
- data/app/validators/slug_validator.rb +53 -0
- data/config/mongoid.yml +5 -0
- data/govuk_content_models.gemspec +42 -0
- data/jenkins.sh +7 -0
- data/lib/fact_check_address.rb +36 -0
- data/lib/govuk_content_models.rb +12 -0
- data/lib/govuk_content_models/require_all.rb +14 -0
- data/lib/govuk_content_models/test_helpers/factories.rb +213 -0
- data/lib/govuk_content_models/test_helpers/local_services.rb +24 -0
- data/lib/govuk_content_models/version.rb +4 -0
- data/test/fixtures/contactotron_api_response.json +1 -0
- data/test/fixtures/uploads/image.jpg +0 -0
- data/test/models/artefact_action_test.rb +123 -0
- data/test/models/artefact_external_link_test.rb +32 -0
- data/test/models/artefact_tag_test.rb +52 -0
- data/test/models/artefact_test.rb +583 -0
- data/test/models/business_support/business_size_test.rb +25 -0
- data/test/models/business_support/business_type_test.rb +25 -0
- data/test/models/business_support/location_test.rb +25 -0
- data/test/models/business_support/purpose_test.rb +29 -0
- data/test/models/business_support/sector_test.rb +25 -0
- data/test/models/business_support/stage_test.rb +25 -0
- data/test/models/business_support/support_type_test.rb +25 -0
- data/test/models/business_support_edition_test.rb +186 -0
- data/test/models/campaign_edition_test.rb +90 -0
- data/test/models/curated_list_test.rb +32 -0
- data/test/models/edition_test.rb +826 -0
- data/test/models/fact_check_address_test.rb +36 -0
- data/test/models/help_page_edition_test.rb +38 -0
- data/test/models/licence_edition_test.rb +104 -0
- data/test/models/local_authority_test.rb +113 -0
- data/test/models/local_service_test.rb +199 -0
- data/test/models/local_transaction_edition_test.rb +78 -0
- data/test/models/overview_dashboard_test.rb +47 -0
- data/test/models/simple_smart_answer_edition_test.rb +169 -0
- data/test/models/simple_smart_answer_node_test.rb +134 -0
- data/test/models/simple_smart_answer_option_test.rb +90 -0
- data/test/models/tag_test.rb +92 -0
- data/test/models/time_zone_test.rb +48 -0
- data/test/models/transaction_edition_test.rb +20 -0
- data/test/models/travel_advice_edition_test.rb +480 -0
- data/test/models/user_test.rb +114 -0
- data/test/models/video_edition_test.rb +64 -0
- data/test/models/workflow_actor_test.rb +61 -0
- data/test/models/workflow_test.rb +307 -0
- data/test/test_helper.rb +47 -0
- data/test/traits/attachable_test.rb +143 -0
- data/test/traits/taggable_test.rb +114 -0
- data/test/validators/safe_html_validator_test.rb +86 -0
- data/test/validators/slug_validator_test.rb +42 -0
- metadata +511 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
module Expectant
|
2
|
+
|
3
|
+
module ClassMethods
|
4
|
+
def expectation_choices
|
5
|
+
Hash[Expectation.all.order_by(text: :asc).map {|e| [e.text, e._id.to_s] }]
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
included do
|
12
|
+
field :expectation_ids, type: Array, default: []
|
13
|
+
field :minutes_to_complete, type: String
|
14
|
+
field :uses_government_gateway, type: Boolean
|
15
|
+
end
|
16
|
+
|
17
|
+
def expectations
|
18
|
+
Expectation.criteria.in(_id: self.expectation_ids)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "edition"
|
2
|
+
require "parted"
|
3
|
+
|
4
|
+
class GuideEdition < Edition
|
5
|
+
include Parted
|
6
|
+
|
7
|
+
field :video_url, type: String
|
8
|
+
field :video_summary, type: String
|
9
|
+
|
10
|
+
@fields_to_clone = [:video_url, :video_summary]
|
11
|
+
|
12
|
+
def has_video?
|
13
|
+
video_url.present?
|
14
|
+
end
|
15
|
+
|
16
|
+
def safe_to_preview?
|
17
|
+
parts.any? and parts.first.slug.present?
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "edition"
|
2
|
+
|
3
|
+
class LicenceEdition < Edition
|
4
|
+
|
5
|
+
field :licence_identifier, :type => String
|
6
|
+
field :licence_short_description, :type => String
|
7
|
+
field :licence_overview, :type => String
|
8
|
+
field :will_continue_on, :type => String
|
9
|
+
field :continuation_link, :type => String
|
10
|
+
|
11
|
+
GOVSPEAK_FIELDS = Edition::GOVSPEAK_FIELDS + [:licence_overview]
|
12
|
+
|
13
|
+
validates :licence_identifier, :presence => true
|
14
|
+
validate :licence_identifier_unique
|
15
|
+
validates_format_of :continuation_link, :with => URI::regexp(%w(http https)), :allow_blank => true
|
16
|
+
|
17
|
+
@fields_to_clone = [:licence_identifier, :licence_short_description,
|
18
|
+
:licence_overview, :will_continue_on, :continuation_link]
|
19
|
+
|
20
|
+
def whole_body
|
21
|
+
[licence_short_description, licence_overview].join("\n\n")
|
22
|
+
end
|
23
|
+
|
24
|
+
def indexable_content
|
25
|
+
"#{super} #{licence_short_description} #{Govspeak::Document.new(licence_overview).to_text}".strip
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def licence_identifier_unique
|
30
|
+
if self.class.without_state('archived').where(:licence_identifier => licence_identifier,
|
31
|
+
:panopticon_id.ne => panopticon_id).any?
|
32
|
+
errors.add(:licence_identifier, :taken)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "csv"
|
2
|
+
require "local_interaction"
|
3
|
+
require "safe_html"
|
4
|
+
|
5
|
+
class LocalAuthority
|
6
|
+
include Mongoid::Document
|
7
|
+
|
8
|
+
embeds_many :local_interactions
|
9
|
+
|
10
|
+
field :name, type: String
|
11
|
+
field :snac, type: String
|
12
|
+
field :local_directgov_id, type: Integer
|
13
|
+
field :tier, type: String
|
14
|
+
field :contact_address, type: Array
|
15
|
+
field :contact_url, type: String
|
16
|
+
field :contact_phone, type: String
|
17
|
+
field :contact_email, type: String
|
18
|
+
|
19
|
+
GOVSPEAK_FIELDS = []
|
20
|
+
|
21
|
+
validates_uniqueness_of :snac
|
22
|
+
validates_presence_of :snac, :local_directgov_id, :name, :tier
|
23
|
+
validates_with SafeHtml
|
24
|
+
|
25
|
+
scope :for_snacs, ->(snacs) { any_in(snac: snacs) }
|
26
|
+
|
27
|
+
def self.find_by_snac(snac)
|
28
|
+
for_snacs([snac]).first
|
29
|
+
end
|
30
|
+
|
31
|
+
def provides_service?(lgsl_code, lgil_code = nil)
|
32
|
+
interactions_for(lgsl_code, lgil_code).any?
|
33
|
+
end
|
34
|
+
|
35
|
+
def interactions_for(lgsl_code, lgil_code = nil)
|
36
|
+
interactions = local_interactions.where(lgsl_code: lgsl_code)
|
37
|
+
if lgil_code
|
38
|
+
interactions.where(lgil_code: lgil_code)
|
39
|
+
else
|
40
|
+
interactions
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def preferred_interaction_for(lgsl_code, lgil_code = nil)
|
45
|
+
interactions = local_interactions.where(lgsl_code: lgsl_code)
|
46
|
+
if lgil_code
|
47
|
+
interactions.where(lgil_code: lgil_code).first
|
48
|
+
else
|
49
|
+
interactions.excludes(
|
50
|
+
lgil_code: LocalInteraction::LGIL_CODE_PROVIDING_INFORMATION
|
51
|
+
).order_by([:lgil_code, :asc]).first ||
|
52
|
+
interactions.where(
|
53
|
+
lgil_code: LocalInteraction::LGIL_CODE_PROVIDING_INFORMATION
|
54
|
+
).first
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "csv"
|
2
|
+
require "safe_html"
|
3
|
+
|
4
|
+
class LocalInteraction
|
5
|
+
include Mongoid::Document
|
6
|
+
|
7
|
+
LGIL_CODE_PROVIDING_INFORMATION = 8
|
8
|
+
|
9
|
+
field :lgsl_code, type: Integer
|
10
|
+
field :lgil_code, type: Integer
|
11
|
+
field :url, type: String
|
12
|
+
|
13
|
+
GOVSPEAK_FIELDS = []
|
14
|
+
|
15
|
+
embedded_in :local_authority
|
16
|
+
|
17
|
+
validates_presence_of :url, :lgil_code, :lgsl_code
|
18
|
+
validates_uniqueness_of :lgil_code, :scope => :lgsl_code
|
19
|
+
validates_with SafeHtml
|
20
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "csv"
|
2
|
+
require "local_authority"
|
3
|
+
require "safe_html"
|
4
|
+
|
5
|
+
class LocalService
|
6
|
+
include Mongoid::Document
|
7
|
+
|
8
|
+
field :description, type: String
|
9
|
+
field :lgsl_code, type: Integer
|
10
|
+
field :providing_tier, type: Array
|
11
|
+
|
12
|
+
GOVSPEAK_FIELDS = []
|
13
|
+
|
14
|
+
validates_presence_of :lgsl_code, :providing_tier
|
15
|
+
validates_uniqueness_of :lgsl_code
|
16
|
+
validates :providing_tier, inclusion: {
|
17
|
+
in: [%w{county unitary}, %w{district unitary}, %w{district unitary county}]
|
18
|
+
}
|
19
|
+
validates_with SafeHtml
|
20
|
+
|
21
|
+
def self.find_by_lgsl_code(lgsl_code)
|
22
|
+
LocalService.where(lgsl_code: lgsl_code).first
|
23
|
+
end
|
24
|
+
|
25
|
+
def preferred_interaction(snac_or_snac_list, lgil_override = nil)
|
26
|
+
provider = preferred_provider(snac_or_snac_list)
|
27
|
+
provider && provider.preferred_interaction_for(lgsl_code, lgil_override)
|
28
|
+
end
|
29
|
+
|
30
|
+
def preferred_provider(snac_or_snac_list)
|
31
|
+
snac_list = [*snac_or_snac_list]
|
32
|
+
providers = LocalAuthority.for_snacs(snac_list)
|
33
|
+
select_tier(providers)
|
34
|
+
end
|
35
|
+
|
36
|
+
def provided_by
|
37
|
+
LocalAuthority.where("local_interactions.lgsl_code" => lgsl_code)
|
38
|
+
.any_in(tier: providing_tier)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def select_tier(authorities)
|
44
|
+
by_tier = Hash[authorities.map {|a| [a.tier, a]}]
|
45
|
+
tier = providing_tier.find { |t| by_tier.has_key?(t) }
|
46
|
+
tier && by_tier[tier]
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "expectant"
|
2
|
+
require "local_service"
|
3
|
+
require "edition"
|
4
|
+
|
5
|
+
class LocalTransactionEdition < Edition
|
6
|
+
include Expectant
|
7
|
+
|
8
|
+
field :lgsl_code, type: Integer
|
9
|
+
field :lgil_override, type: Integer
|
10
|
+
field :introduction, type: String
|
11
|
+
field :more_information, type: String
|
12
|
+
|
13
|
+
GOVSPEAK_FIELDS = Edition::GOVSPEAK_FIELDS + [:introduction, :more_information]
|
14
|
+
|
15
|
+
@fields_to_clone = [
|
16
|
+
:lgsl_code, :introduction, :more_information,
|
17
|
+
:minutes_to_complete, :expectation_ids
|
18
|
+
]
|
19
|
+
|
20
|
+
validate :valid_lgsl_code
|
21
|
+
|
22
|
+
def valid_lgsl_code
|
23
|
+
if ! self.service
|
24
|
+
errors.add(:lgsl_code, "#{lgsl_code} not recognised")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def format_name
|
29
|
+
"Local transaction"
|
30
|
+
end
|
31
|
+
|
32
|
+
def search_format
|
33
|
+
"transaction"
|
34
|
+
end
|
35
|
+
|
36
|
+
def service
|
37
|
+
LocalService.find_by_lgsl_code(lgsl_code)
|
38
|
+
end
|
39
|
+
|
40
|
+
def service_provided_by?(snac)
|
41
|
+
authority = LocalAuthority.find_by_snac(snac)
|
42
|
+
authority && authority.provides_service?(lgsl_code)
|
43
|
+
end
|
44
|
+
|
45
|
+
def whole_body
|
46
|
+
self.introduction
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "safe_html"
|
2
|
+
|
3
|
+
class OverviewDashboard
|
4
|
+
include Mongoid::Document
|
5
|
+
|
6
|
+
TOTAL_KEY = "**TOTAL**"
|
7
|
+
UNASSIGNED_KEY = "**UNASSIGNED**"
|
8
|
+
|
9
|
+
field :dashboard_type, type: String
|
10
|
+
field :result_group, type: Integer
|
11
|
+
field :count, type: Integer
|
12
|
+
field :lined_up, type: Integer
|
13
|
+
field :draft, type: Integer
|
14
|
+
field :amends_needed, type: Integer
|
15
|
+
field :in_review, type: Integer
|
16
|
+
field :ready, type: Integer
|
17
|
+
field :fact_check_received, type: Integer
|
18
|
+
field :fact_check, type: Integer
|
19
|
+
field :published, type: Integer
|
20
|
+
field :archived, type: Integer
|
21
|
+
|
22
|
+
GOVSPEAK_FIELDS = []
|
23
|
+
|
24
|
+
validates_with SafeHtml
|
25
|
+
end
|
data/app/models/part.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require "safe_html"
|
2
|
+
require 'govspeak_smart_quotes_fixer'
|
3
|
+
|
4
|
+
class Part
|
5
|
+
include Mongoid::Document
|
6
|
+
|
7
|
+
embedded_in :guide_edition
|
8
|
+
embedded_in :programme_edition
|
9
|
+
embedded_in :business_support_edition
|
10
|
+
|
11
|
+
scope :in_order, order_by(:order, :asc)
|
12
|
+
|
13
|
+
field :order, type: Integer
|
14
|
+
field :title, type: String
|
15
|
+
field :body, type: String
|
16
|
+
field :slug, type: String
|
17
|
+
field :created_at, type: DateTime, default: lambda { Time.zone.now }
|
18
|
+
|
19
|
+
GOVSPEAK_FIELDS = [:body]
|
20
|
+
|
21
|
+
include GovspeakSmartQuotesFixer
|
22
|
+
|
23
|
+
validates_presence_of :title
|
24
|
+
validates_presence_of :slug
|
25
|
+
validates_exclusion_of :slug, in: ["video"], message: "Can not be video"
|
26
|
+
validates_format_of :slug, with: /^[a-z0-9\-]+$/i
|
27
|
+
validates_with SafeHtml
|
28
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "part"
|
2
|
+
|
3
|
+
module Parted
|
4
|
+
def self.included(klass)
|
5
|
+
klass.embeds_many :parts
|
6
|
+
klass.accepts_nested_attributes_for :parts, allow_destroy: true,
|
7
|
+
reject_if: proc { |attrs| attrs["title"].blank? and attrs["body"].blank? }
|
8
|
+
end
|
9
|
+
|
10
|
+
def build_clone(edition_class=nil)
|
11
|
+
new_edition = super
|
12
|
+
|
13
|
+
# If the new edition is of the same type or another type that has parts,
|
14
|
+
# copy over the parts from this edition
|
15
|
+
if edition_class.nil? or edition_class.include? Parted
|
16
|
+
new_edition.parts = self.parts.map {|p| p.dup }
|
17
|
+
end
|
18
|
+
|
19
|
+
new_edition
|
20
|
+
end
|
21
|
+
|
22
|
+
def order_parts
|
23
|
+
ordered_parts = parts.sort_by { |p| p.order ? p.order : 99999 }
|
24
|
+
ordered_parts.each_with_index do |obj, i|
|
25
|
+
obj.order = i + 1
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def whole_body
|
30
|
+
self.parts.map {|i| %Q{\# #{i.title}\n\n#{i.body}} }.join("\n\n")
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "edition"
|
2
|
+
require "expectant"
|
3
|
+
|
4
|
+
class PlaceEdition < Edition
|
5
|
+
include Expectant
|
6
|
+
|
7
|
+
field :introduction, type: String
|
8
|
+
field :more_information, type: String
|
9
|
+
field :place_type, type: String
|
10
|
+
|
11
|
+
GOVSPEAK_FIELDS = Edition::GOVSPEAK_FIELDS + [:introduction, :more_information]
|
12
|
+
|
13
|
+
@fields_to_clone = [:introduction, :more_information, :place_type,
|
14
|
+
:expectation_ids]
|
15
|
+
|
16
|
+
def whole_body
|
17
|
+
self.introduction
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "edition"
|
2
|
+
require "parted"
|
3
|
+
|
4
|
+
class ProgrammeEdition < Edition
|
5
|
+
include Parted
|
6
|
+
|
7
|
+
before_save :setup_default_parts, on: :create
|
8
|
+
|
9
|
+
@fields_to_clone = []
|
10
|
+
|
11
|
+
DEFAULT_PARTS = [
|
12
|
+
{title: "Overview", slug: "overview"},
|
13
|
+
{title: "What you'll get", slug: "what-youll-get"},
|
14
|
+
{title: "Eligibility", slug: "eligibility"},
|
15
|
+
{title: "How to claim", slug: "how-to-claim"},
|
16
|
+
{title: "Further information", slug: "further-information"},
|
17
|
+
]
|
18
|
+
|
19
|
+
def setup_default_parts
|
20
|
+
if parts.empty?
|
21
|
+
DEFAULT_PARTS.each { |part|
|
22
|
+
parts.build(title: part[:title], slug: part[:slug], body: "")
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "edition"
|
2
|
+
require_relative 'simple_smart_answer_edition/node'
|
3
|
+
require_relative 'simple_smart_answer_edition/node/option'
|
4
|
+
|
5
|
+
class SimpleSmartAnswerEdition < Edition
|
6
|
+
include Mongoid::Document
|
7
|
+
|
8
|
+
field :body, type: String
|
9
|
+
|
10
|
+
embeds_many :nodes, :class_name => "SimpleSmartAnswerEdition::Node"
|
11
|
+
|
12
|
+
accepts_nested_attributes_for :nodes, allow_destroy: true
|
13
|
+
|
14
|
+
GOVSPEAK_FIELDS = Edition::GOVSPEAK_FIELDS + [:body]
|
15
|
+
@fields_to_clone = [:body]
|
16
|
+
|
17
|
+
def whole_body
|
18
|
+
body
|
19
|
+
end
|
20
|
+
|
21
|
+
def build_clone(edition_class=nil)
|
22
|
+
new_edition = super(edition_class)
|
23
|
+
new_edition.body = self.body
|
24
|
+
|
25
|
+
if new_edition.is_a?(SimpleSmartAnswerEdition)
|
26
|
+
self.nodes.each {|n| new_edition.nodes << n.clone }
|
27
|
+
end
|
28
|
+
|
29
|
+
new_edition
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
# Workaround mongoid conflicting mods error
|
34
|
+
# See https://github.com/mongoid/mongoid/issues/1219
|
35
|
+
# Override update_attributes so that nested nodes are updated individually.
|
36
|
+
# This get around the problem of mongoid issuing a query with conflicting modifications
|
37
|
+
# to the same document.
|
38
|
+
alias_method :original_update_attributes, :update_attributes
|
39
|
+
|
40
|
+
def update_attributes(attributes)
|
41
|
+
if nodes_attrs = attributes.delete(:nodes_attributes)
|
42
|
+
nodes_attrs.each do |index, node_attrs|
|
43
|
+
if node_id = node_attrs['id']
|
44
|
+
node = nodes.find(node_id)
|
45
|
+
if destroy_in_attrs?(node_attrs)
|
46
|
+
node.destroy
|
47
|
+
else
|
48
|
+
node.update_attributes(node_attrs)
|
49
|
+
end
|
50
|
+
else
|
51
|
+
nodes << Node.new(node_attrs) unless destroy_in_attrs?(node_attrs)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
original_update_attributes(attributes)
|
57
|
+
end
|
58
|
+
|
59
|
+
def initial_node
|
60
|
+
self.nodes.first
|
61
|
+
end
|
62
|
+
|
63
|
+
def destroy_in_attrs?(attrs)
|
64
|
+
attrs['_destroy'] == '1'
|
65
|
+
end
|
66
|
+
end
|