curation_concerns 1.7.3 → 1.7.4

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.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/app/actors/curation_concerns/optimistic_lock_validator.rb +28 -0
  3. data/app/assets/javascripts/curation_concerns/file_manager/sorting.es6 +17 -7
  4. data/app/controllers/concerns/curation_concerns/curation_concern_controller.rb +6 -1
  5. data/app/forms/curation_concerns/forms/file_manager_form.rb +27 -0
  6. data/app/forms/curation_concerns/forms/work_form.rb +8 -0
  7. data/app/models/concerns/curation_concerns/ability.rb +1 -0
  8. data/app/models/concerns/curation_concerns/work_behavior.rb +6 -0
  9. data/app/presenters/curation_concerns/member_presenter_factory.rb +70 -0
  10. data/app/presenters/curation_concerns/work_show_presenter.rb +7 -53
  11. data/app/services/curation_concerns/actors/actor_factory.rb +2 -1
  12. data/app/views/curation_concerns/base/_file_manager_member_resource_options.html.erb +2 -2
  13. data/app/views/curation_concerns/base/_file_manager_members.html.erb +13 -5
  14. data/app/views/curation_concerns/base/_file_manager_resource_form.html.erb +1 -1
  15. data/app/views/curation_concerns/base/_form.html.erb +3 -0
  16. data/app/views/curation_concerns/base/file_manager.html.erb +2 -2
  17. data/app/views/curation_concerns/base/show.json.jbuilder +2 -1
  18. data/config/locales/curation_concerns.en.yml +4 -0
  19. data/lib/curation_concerns/version.rb +1 -1
  20. data/spec/actors/curation_concerns/optimistic_lock_validator_spec.rb +50 -0
  21. data/spec/controllers/curation_concerns/generic_works_controller_spec.rb +1 -1
  22. data/spec/forms/work_form_spec.rb +8 -0
  23. data/spec/presenters/curation_concerns/member_presenter_factory_spec.rb +25 -0
  24. data/spec/presenters/curation_concerns/work_show_presenter_spec.rb +1 -21
  25. data/spec/views/curation_concerns/base/_form.html.erb_spec.rb +35 -0
  26. data/spec/views/curation_concerns/base/file_manager.html.erb_spec.rb +10 -8
  27. data/spec/views/curation_concerns/base/show.json.jbuilder_spec.rb +3 -1
  28. metadata +12 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b8e3a51d73c77e6ee4533319b2e0dbc7b8afed1c
4
- data.tar.gz: c1cb7a95bc11873894ec02b198c0925db1e1ff27
3
+ metadata.gz: 0264d3ffe1409c035fd9849f11c7ade0dc92c3c1
4
+ data.tar.gz: 912f6765b87b3ebe922ccc17ea4689925fe03acb
5
5
  SHA512:
6
- metadata.gz: 7617b194446b007e5b23f6f58d9f5ba08f57f6bf26a46b07264141688c751aec136adf36a409f52d73b87280c24e012652cbb94798ff613938edffb1b420c33d
7
- data.tar.gz: e542b1d43ab5205cd042a4fa6c7398a7bcbfd2956c7fbd92a092d3ef3fcec932b40503bc6e9122125080e2ecd34595ccf1d85e5117a37f9f9388edd9a295e50f
6
+ metadata.gz: 24a1f4ca059a85819570ea0d2df252ab03e340fccb56e33d342f9af452272d60ac43677adb4b5aa374391fc07b01b36c3b30b060739941bc64c66294937dc2aa
7
+ data.tar.gz: a8ecc0679718f7c9f4f9e942c0fd2d97a25564a085e6bf7e5f6c1daf0714348a2ca95627af4218f61ea633dc8f4535ebc01fc83f5e8b05a560fa8fe71515691d
@@ -0,0 +1,28 @@
1
+ module CurationConcerns
2
+ # Validates that the submitted version is the most recent version in the datastore.
3
+ # Caveat: we are not detecting if the version is changed by a different process between
4
+ # the time this validator is run and when the object is saved
5
+ class OptimisticLockValidator < Actors::AbstractActor
6
+ class_attribute :version_field
7
+ self.version_field = 'version'
8
+
9
+ def update(attributes)
10
+ validate_lock(version_attribute(attributes)) && next_actor.update(attributes)
11
+ end
12
+
13
+ private
14
+
15
+ # @return [Boolean] returns true if the lock is missing or
16
+ # if it matches the current object version.
17
+ def validate_lock(version)
18
+ return true if version.blank? || version == curation_concern.etag
19
+ curation_concern.errors.add(:base, :conflict)
20
+ false
21
+ end
22
+
23
+ # Removes the version attribute
24
+ def version_attribute(attributes)
25
+ attributes.delete(version_field)
26
+ end
27
+ end
28
+ end
@@ -15,18 +15,14 @@ export default class SortManager {
15
15
  }
16
16
 
17
17
  persist() {
18
- let params = {}
19
- params[this.singular_class_name] = {
20
- "ordered_member_ids": this.order
21
- }
22
- params["_method"] = "PATCH"
23
18
  this.element.addClass("pending")
24
19
  this.element.removeClass("success")
25
20
  this.element.removeClass("failure")
26
21
  let persisting = $.post(
27
22
  `/concern/${this.class_name}/${this.id}.json`,
28
- params
29
- ).done(() => {
23
+ this.params()
24
+ ).done((response) => {
25
+ this.element.data('version', response.version)
30
26
  this.element.data("current-order", this.order)
31
27
  this.element.addClass("success")
32
28
  this.element.removeClass("failure")
@@ -39,6 +35,16 @@ export default class SortManager {
39
35
  return persisting
40
36
  }
41
37
 
38
+ params() {
39
+ let params = {}
40
+ params[this.singular_class_name] = {
41
+ "version": this.version,
42
+ "ordered_member_ids": this.order
43
+ }
44
+ params["_method"] = "PATCH"
45
+ return params
46
+ }
47
+
42
48
  get_sort_position(item) {
43
49
  return this.element.children().index(item)
44
50
  }
@@ -88,6 +94,10 @@ export default class SortManager {
88
94
  ).toArray()
89
95
  }
90
96
 
97
+ get version() {
98
+ return this.element.data('version')
99
+ }
100
+
91
101
  get alpha_sort_button() {
92
102
  return $("*[data-action='alpha-sort-action']")
93
103
  }
@@ -22,6 +22,11 @@ module CurationConcerns
22
22
  module ClassMethods
23
23
  def curation_concern_type=(curation_concern_type)
24
24
  load_and_authorize_resource class: curation_concern_type, instance_name: :curation_concern, except: [:show, :file_manager, :inspect_work]
25
+
26
+ # Load the fedora resource to get the etag.
27
+ # No need to authorize for the file manager, because it does authorization via the presenter.
28
+ load_resource class: curation_concern_type, instance_name: :curation_concern, only: :file_manager
29
+
25
30
  self._curation_concern_type = curation_concern_type
26
31
  end
27
32
 
@@ -104,7 +109,7 @@ module CurationConcerns
104
109
  end
105
110
 
106
111
  def file_manager
107
- presenter
112
+ @form = Forms::FileManagerForm.new(curation_concern, current_ability)
108
113
  end
109
114
 
110
115
  def inspect_work
@@ -0,0 +1,27 @@
1
+ module CurationConcerns
2
+ module Forms
3
+ class FileManagerForm
4
+ include HydraEditor::Form
5
+ self.terms = []
6
+ delegate :id, :thumbnail_id, :representative_id, :to_s, to: :model
7
+ attr_reader :current_ability, :request
8
+ def initialize(work, ability)
9
+ super(work)
10
+ @current_ability = ability
11
+ @request = nil
12
+ end
13
+
14
+ def version
15
+ model.etag
16
+ end
17
+
18
+ delegate :member_presenters, to: :member_presenter_factory
19
+
20
+ private
21
+
22
+ def member_presenter_factory
23
+ MemberPresenterFactory.new(work, ability)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -26,6 +26,10 @@ module CurationConcerns
26
26
  super(model)
27
27
  end
28
28
 
29
+ def version
30
+ model.etag
31
+ end
32
+
29
33
  # The value for embargo_relase_date and lease_expiration_date should not
30
34
  # be initialized to empty string
31
35
  def initialize_field(key)
@@ -51,6 +55,10 @@ module CurationConcerns
51
55
  super
52
56
  end
53
57
  end
58
+
59
+ def build_permitted_params
60
+ super + [:version]
61
+ end
54
62
  end
55
63
 
56
64
  private
@@ -14,6 +14,7 @@ module CurationConcerns
14
14
 
15
15
  # user can version if they can edit
16
16
  alias_action :versions, to: :update
17
+ alias_action :file_manager, to: :update
17
18
 
18
19
  if admin?
19
20
  admin_permissions
@@ -24,6 +24,12 @@ module CurationConcerns::WorkBehavior
24
24
  self.indexer = CurationConcerns::WorkIndexer
25
25
  end
26
26
 
27
+ # TODO: Move this into ActiveFedora
28
+ def etag
29
+ raise "Unable to produce an etag for a unsaved object" unless persisted?
30
+ ldp_source.head.etag
31
+ end
32
+
27
33
  module ClassMethods
28
34
  # This governs which partial to draw when you render this type of object
29
35
  def _to_partial_path #:nodoc:
@@ -0,0 +1,70 @@
1
+ module CurationConcerns
2
+ # Creates the presenters of the members (member works and file sets) of a specific object
3
+ class MemberPresenterFactory
4
+ class_attribute :file_presenter_class, :work_presenter_class
5
+ # modify this attribute to use an alternate presenter class for the files
6
+ self.file_presenter_class = FileSetPresenter
7
+
8
+ # modify this attribute to use an alternate presenter class for the child works
9
+ self.work_presenter_class = WorkShowPresenter
10
+
11
+ def initialize(work, ability, request = nil)
12
+ @work = work
13
+ @current_ability = ability
14
+ @request = request
15
+ end
16
+
17
+ delegate :id, to: :@work
18
+ attr_reader :current_ability, :request
19
+
20
+ # @param [Array<String>] ids a list of ids to build presenters for
21
+ # @param [Class] presenter_class the type of presenter to build
22
+ # @return [Array<presenter_class>] presenters for the ordered_members (not filtered by class)
23
+ def member_presenters(ids = ordered_ids, presenter_class = composite_presenter_class)
24
+ PresenterFactory.build_presenters(ids, presenter_class, *presenter_factory_arguments)
25
+ end
26
+
27
+ # @return [Array<FileSetPresenter>] presenters for the orderd_members that are FileSets
28
+ def file_set_presenters
29
+ @file_set_presenters ||= member_presenters(ordered_ids & file_set_ids)
30
+ end
31
+
32
+ # @return [Array<WorkShowPresenter>] presenters for the ordered_members that are not FileSets
33
+ def work_presenters
34
+ @work_presenters ||= member_presenters(ordered_ids - file_set_ids, work_presenter_class)
35
+ end
36
+
37
+ private
38
+
39
+ # TODO: Extract this to ActiveFedora::Aggregations::ListSource
40
+ def ordered_ids
41
+ @ordered_ids ||= begin
42
+ ActiveFedora::SolrService.query("proxy_in_ssi:#{id}",
43
+ rows: 10_000,
44
+ fl: "ordered_targets_ssim")
45
+ .flat_map { |x| x.fetch("ordered_targets_ssim", []) }
46
+ end
47
+ end
48
+
49
+ # These are the file sets that belong to this work, but not necessarily
50
+ # in order.
51
+ # Arbitrarily maxed at 10 thousand; had to specify rows due to solr's default of 10
52
+ def file_set_ids
53
+ @file_set_ids ||= begin
54
+ ActiveFedora::SolrService.query("{!field f=has_model_ssim}FileSet",
55
+ rows: 10_000,
56
+ fl: ActiveFedora.id_field,
57
+ fq: "{!join from=ordered_targets_ssim to=id}id:\"#{id}/list_source\"")
58
+ .flat_map { |x| x.fetch(ActiveFedora.id_field, []) }
59
+ end
60
+ end
61
+
62
+ def presenter_factory_arguments
63
+ [current_ability, request]
64
+ end
65
+
66
+ def composite_presenter_class
67
+ CompositePresenterFactory.new(file_presenter_class, work_presenter_class, ordered_ids & file_set_ids)
68
+ end
69
+ end
70
+ end
@@ -4,17 +4,11 @@ module CurationConcerns
4
4
  include PresentsAttributes
5
5
  attr_accessor :solr_document, :current_ability, :request
6
6
 
7
- class_attribute :collection_presenter_class, :file_presenter_class, :work_presenter_class
7
+ class_attribute :collection_presenter_class
8
8
 
9
9
  # modify this attribute to use an alternate presenter class for the collections
10
10
  self.collection_presenter_class = CollectionPresenter
11
11
 
12
- # modify this attribute to use an alternate presenter class for the files
13
- self.file_presenter_class = FileSetPresenter
14
-
15
- # modify this attribute to use an alternate presenter class for the child works
16
- self.work_presenter_class = self
17
-
18
12
  # Methods used by blacklight helpers
19
13
  delegate :has?, :first, :fetch, :export_formats, :export_as, to: :solr_document
20
14
 
@@ -41,11 +35,6 @@ module CurationConcerns
41
35
  :lease_expiration_date, :rights, :source, :thumbnail_id, :representative_id,
42
36
  to: :solr_document
43
37
 
44
- # @return [Array<FileSetPresenter>] presenters for the orderd_members that are FileSets
45
- def file_set_presenters
46
- @file_set_presenters ||= member_presenters(ordered_ids & file_set_ids)
47
- end
48
-
49
38
  def workflow
50
39
  @workflow ||= WorkflowPresenter.new(solr_document, current_ability)
51
40
  end
@@ -68,24 +57,6 @@ module CurationConcerns
68
57
  end
69
58
  end
70
59
 
71
- # @return [Array<WorkShowPresenter>] presenters for the ordered_members that are not FileSets
72
- def work_presenters
73
- @work_presenters ||= member_presenters(ordered_ids - file_set_ids, work_presenter_class)
74
- end
75
-
76
- # @param [Array<String>] ids a list of ids to build presenters for
77
- # @param [Class] presenter_class the type of presenter to build
78
- # @return [Array<presenter_class>] presenters for the ordered_members (not filtered by class)
79
- def member_presenters(ids = ordered_ids, presenter_class = composite_presenter_class)
80
- PresenterFactory.build_presenters(ids,
81
- presenter_class,
82
- *presenter_factory_arguments)
83
- end
84
-
85
- def composite_presenter_class
86
- CompositePresenterFactory.new(file_presenter_class, work_presenter_class, ordered_ids & file_set_ids)
87
- end
88
-
89
60
  # @return [Array<CollectionPresenter>] presenters for the collections that this work is a member of
90
61
  def collection_presenters
91
62
  PresenterFactory.build_presenters(in_collection_ids,
@@ -109,11 +80,9 @@ module CurationConcerns
109
80
  graph.dump(:ttl)
110
81
  end
111
82
 
112
- private
83
+ delegate :member_presenters, :file_set_presenters, :work_presenters, to: :member_presenter_factory
113
84
 
114
- def graph
115
- GraphExporter.new(solr_document, request).fetch
116
- end
85
+ private
117
86
 
118
87
  def presenter_factory_arguments
119
88
  [current_ability, request]
@@ -126,27 +95,12 @@ module CurationConcerns
126
95
  .map { |x| x.fetch(ActiveFedora.id_field) }
127
96
  end
128
97
 
129
- # TODO: Extract this to ActiveFedora::Aggregations::ListSource
130
- def ordered_ids
131
- @ordered_ids ||= begin
132
- ActiveFedora::SolrService.query("proxy_in_ssi:#{id}",
133
- rows: 10_000,
134
- fl: "ordered_targets_ssim")
135
- .flat_map { |x| x.fetch("ordered_targets_ssim", []) }
136
- end
98
+ def member_presenter_factory
99
+ MemberPresenterFactory.new(solr_document, current_ability, request)
137
100
  end
138
101
 
139
- # These are the file sets that belong to this work, but not necessarily
140
- # in order.
141
- # Arbitrarily maxed at 10 thousand; had to specify rows due to solr's default of 10
142
- def file_set_ids
143
- @file_set_ids ||= begin
144
- ActiveFedora::SolrService.query("{!field f=has_model_ssim}FileSet",
145
- rows: 10_000,
146
- fl: ActiveFedora.id_field,
147
- fq: "{!join from=ordered_targets_ssim to=id}id:\"#{id}/list_source\"")
148
- .flat_map { |x| x.fetch(ActiveFedora.id_field, []) }
149
- end
102
+ def graph
103
+ GraphExporter.new(solr_document, request).fetch
150
104
  end
151
105
  end
152
106
  end
@@ -8,7 +8,8 @@ module CurationConcerns
8
8
  end
9
9
 
10
10
  def self.stack_actors(curation_concern)
11
- [AddToCollectionActor,
11
+ [OptimisticLockValidator,
12
+ AddToCollectionActor,
12
13
  AddToWorkActor,
13
14
  AssignRepresentativeActor,
14
15
  AttachFilesActor,
@@ -1,10 +1,10 @@
1
1
  <div class="form-group radio_buttons member_resource_options">
2
2
  <span class="radio">
3
- <%= radio_button_tag "thumbnail_id", node.id, @presenter.thumbnail_id == node.id, id: "thumbnail_id_#{node.id}", class: "radio_buttons" %>
3
+ <%= radio_button_tag "thumbnail_id", node.id, @form.thumbnail_id == node.id, id: "thumbnail_id_#{node.id}", class: "radio_buttons" %>
4
4
  <%= label_tag "thumbnail_id_#{node.id}", "Thumbnail" %>
5
5
  </span>
6
6
  <span class="radio">
7
- <%= radio_button_tag "representative_id", node.id, @presenter.representative_id == node.id, id: "representative_id_#{node.id}", class: "radio_buttons" %>
7
+ <%= radio_button_tag "representative_id", node.id, @form.representative_id == node.id, id: "representative_id_#{node.id}", class: "radio_buttons" %>
8
8
  <%= label_tag "representative_id_#{node.id}", "Representative Media" %>
9
9
  </span>
10
10
  </div>
@@ -1,5 +1,13 @@
1
- <ul id="sortable" data-id="<%= @presenter.id %>" data-class-name="<%= @presenter.model_name.plural %>" data-singular-class-name="<%= @presenter.model_name.singular %>" class="list-unstyled grid clearfix">
2
- <% @presenter.member_presenters.each do |member| %>
3
- <%= render "file_manager_member", node: member %>
4
- <% end %>
5
- </ul>
1
+ <%= content_tag :ul,
2
+ id: "sortable",
3
+ class: "list-unstyled grid clearfix",
4
+ data: {
5
+ id: @form.id,
6
+ "class-name" => @form.model_name.plural,
7
+ "singular-class-name" => @form.model_name.singular,
8
+ version: @form.version
9
+ } do %>
10
+ <% @form.member_presenters.each do |member| %>
11
+ <%= render "file_manager_member", node: member %>
12
+ <% end %>
13
+ <% end %>
@@ -1,5 +1,5 @@
1
1
  <div class="resource-form-container">
2
- <%= simple_form_for [main_app, @presenter], remote: true, html: { id: 'resource-form', 'data-type': 'json' } do |f| %>
2
+ <%= simple_form_for [main_app, @form], remote: true, html: { id: 'resource-form', 'data-type': 'json' } do |f| %>
3
3
  <%= f.input :thumbnail_id, as: :hidden, input_html: { data: {member_link: 'thumbnail_id'}} %>
4
4
  <%= f.input :representative_id, as: :hidden, input_html: { data: {member_link: 'representative_id'}} %>
5
5
  <% end %>
@@ -12,6 +12,9 @@
12
12
 
13
13
  <div class="row">
14
14
  <div class="col-md-12 form-actions">
15
+ <%# TODO: If we start using ActionCable, we could listen for object updates and
16
+ alert the user that the object has changed by someone else %>
17
+ <%= f.input CurationConcerns::OptimisticLockValidator.version_field, as: :hidden unless f.object.new_record? %>
15
18
  <%= f.submit class: 'btn btn-primary require-contributor-agreement' %>
16
19
  <% if curation_concern.new_record? %>
17
20
  <%= link_to 'Cancel', main_app.root_path, class: 'btn btn-link' %>
@@ -1,11 +1,11 @@
1
1
  <h1><%= t("file_manager.link_text") %></h1>
2
2
  <ul class="breadcrumb">
3
3
  <li>
4
- Back to <%= link_to @presenter.to_s, [main_app, @presenter] %>
4
+ Back to <%= link_to @form.to_s, [main_app, @form] %>
5
5
  </li>
6
6
  </ul>
7
7
 
8
- <% if !@presenter.member_presenters.empty? %>
8
+ <% if !@form.member_presenters.empty? %>
9
9
  <div data-action="file-manager">
10
10
  <div class="col-md-3" id="file-manager-tools">
11
11
  <h2>Toolbar</h2>
@@ -1 +1,2 @@
1
- json.extract! @curation_concern, *[:id] + @curation_concern.class.fields.select {|f| ![:has_model].include? f}
1
+ json.extract! @curation_concern, *[:id] + @curation_concern.class.fields.select {|f| ![:has_model].include? f}
2
+ json.version @curation_concern.etag
@@ -1,4 +1,8 @@
1
1
  en:
2
+ activemodel:
3
+ errors:
4
+ messages:
5
+ conflict: "Another user has made a change to that %{model} since you accessed the edit form."
2
6
  curation_concerns:
3
7
  admin:
4
8
  workflow_roles:
@@ -1,3 +1,3 @@
1
1
  module CurationConcerns
2
- VERSION = '1.7.3'.freeze
2
+ VERSION = '1.7.4'.freeze
3
3
  end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe CurationConcerns::OptimisticLockValidator do
4
+ let(:update_actor) do
5
+ double('update actor', update: true,
6
+ curation_concern: work,
7
+ user: depositor)
8
+ end
9
+
10
+ let(:actor) do
11
+ CurationConcerns::Actors::ActorStack.new(work, depositor, [described_class])
12
+ end
13
+
14
+ let(:depositor) { create(:user) }
15
+ let(:work) { create(:generic_work) }
16
+
17
+ describe "update" do
18
+ before do
19
+ allow(CurationConcerns::Actors::RootActor).to receive(:new).and_return(update_actor)
20
+ allow(update_actor).to receive(:update).and_return(true)
21
+ end
22
+
23
+ subject { actor.update(attributes) }
24
+
25
+ context "when version is blank" do
26
+ let(:attributes) { { version: '' } }
27
+ it { is_expected.to be true }
28
+ end
29
+
30
+ context "when version is provided" do
31
+ context "and the version is current" do
32
+ let(:attributes) { { version: work.etag } }
33
+
34
+ it "returns true and calls the next actor without the version attribute" do
35
+ expect(update_actor).to receive(:update).with({}).and_return(true)
36
+ expect(subject).to be true
37
+ end
38
+ end
39
+
40
+ context "and the version is not current" do
41
+ let(:attributes) { { version: "W/\"ab2e8552cb5f7f00f91d2b223eca45849c722301\"" } }
42
+
43
+ it "returns false and sets an error" do
44
+ expect(subject).to be false
45
+ expect(work.errors[:base]).to include "Another user has made a change to that Generic work since you accessed the edit form."
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -341,7 +341,7 @@ describe CurationConcerns::GenericWorksController do
341
341
  it "is successful" do
342
342
  get :file_manager, params: { id: work.id }
343
343
  expect(response).to be_success
344
- expect(assigns(:presenter)).not_to be_blank
344
+ expect(assigns(:form)).not_to be_blank
345
345
  end
346
346
  end
347
347
 
@@ -22,6 +22,14 @@ describe CurationConcerns::Forms::WorkForm do
22
22
  let(:ability) { nil }
23
23
  let(:form) { PirateShipForm.new(curation_concern, ability) }
24
24
 
25
+ describe "#version" do
26
+ before do
27
+ allow(curation_concern).to receive(:etag).and_return('123456')
28
+ end
29
+ subject { form.version }
30
+ it { is_expected.to eq '123456' }
31
+ end
32
+
25
33
  describe "#select_files" do
26
34
  let(:curation_concern) { create(:work_with_one_file) }
27
35
  let(:title) { curation_concern.file_sets.first.title.first }
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe CurationConcerns::MemberPresenterFactory do
4
+ describe "#file_set_presenters" do
5
+ describe "getting presenters from factory" do
6
+ let(:solr_document) { SolrDocument.new(attributes) }
7
+ let(:attributes) { {} }
8
+ let(:ability) { double }
9
+ let(:request) { double }
10
+ let(:factory) { described_class.new(solr_document, ability, request) }
11
+ let(:presenter_class) { double }
12
+ before do
13
+ allow(factory).to receive(:composite_presenter_class).and_return(presenter_class)
14
+ allow(factory).to receive(:ordered_ids).and_return(['12', '33'])
15
+ allow(factory).to receive(:file_set_ids).and_return(['33', '12'])
16
+ end
17
+
18
+ it "uses the set class" do
19
+ expect(CurationConcerns::PresenterFactory).to receive(:build_presenters)
20
+ .with(['12', '33'], presenter_class, ability, request)
21
+ factory.file_set_presenters
22
+ end
23
+ end
24
+ end
25
+ end
@@ -102,34 +102,14 @@ describe CurationConcerns::WorkShowPresenter do
102
102
  expect(presenter.file_set_presenters.map(&:id)).not_to include another_work.id
103
103
  end
104
104
  end
105
-
106
- describe "getting presenters from factory" do
107
- let(:attributes) { {} }
108
- let(:presenter_class) { double }
109
- before do
110
- allow(presenter).to receive(:composite_presenter_class).and_return(presenter_class)
111
- allow(presenter).to receive(:ordered_ids).and_return(['12', '33'])
112
- allow(presenter).to receive(:file_set_ids).and_return(['33', '12'])
113
- end
114
-
115
- it "uses the set class" do
116
- expect(CurationConcerns::PresenterFactory).to receive(:build_presenters)
117
- .with(['12', '33'], presenter_class, ability, request)
118
- presenter.file_set_presenters
119
- end
120
- end
121
105
  end
122
106
 
123
107
  describe "#representative_presenter" do
124
108
  let(:obj) { create(:work_with_representative_file) }
125
109
  let(:attributes) { obj.to_solr }
126
- let(:presenter_class) { double }
127
- before do
128
- allow(presenter).to receive(:composite_presenter_class).and_return(presenter_class)
129
- end
130
110
  it "has a representative" do
131
111
  expect(CurationConcerns::PresenterFactory).to receive(:build_presenters)
132
- .with([obj.members[0].id], presenter_class, ability, request).and_return ["abc"]
112
+ .with([obj.members[0].id], CurationConcerns::CompositePresenterFactory, ability, request).and_return ["abc"]
133
113
  expect(presenter.representative_presenter).to eq("abc")
134
114
  end
135
115
  end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'curation_concerns/base/_form.html.erb', type: :view do
4
+ let(:ability) { double }
5
+ let(:user) { stub_model(User) }
6
+ let(:form) do
7
+ CurationConcerns::GenericWorkForm.new(work, ability)
8
+ end
9
+
10
+ before do
11
+ # view.lookup_context.view_paths.push 'app/views/curation_concerns'
12
+ # allow(controller).to receive(:current_user).and_return(user)
13
+ allow(view).to receive(:curation_concern).and_return(work)
14
+ end
15
+
16
+ let(:page) do
17
+ view.simple_form_for form do |f|
18
+ render 'curation_concerns/base/form', f: f
19
+ end
20
+ Capybara::Node::Simple.new(rendered)
21
+ end
22
+
23
+ context "when the work has been saved before" do
24
+ before do
25
+ allow(work).to receive(:new_record?).and_return(false)
26
+ assign(:form, form)
27
+ end
28
+
29
+ let(:work) { stub_model(GenericWork, id: '456', etag: '123456') }
30
+
31
+ it "renders the form with the version" do
32
+ expect(page).to have_selector("input#generic_work_version[value=\"123456\"]", visible: false)
33
+ end
34
+ end
35
+ end
@@ -26,19 +26,21 @@ RSpec.describe "curation_concerns/base/file_manager.html.erb" do
26
26
  end
27
27
  let(:resource) { FactoryGirl.build(:file_set) }
28
28
 
29
- let(:parent) { FactoryGirl.build(:generic_work) }
30
- let(:parent_solr_doc) do
31
- SolrDocument.new(parent.to_solr.merge(id: "resource"), nil)
32
- end
33
- let(:parent_presenter) do
34
- CurationConcerns::WorkShowPresenter.new(parent_solr_doc, nil, view)
29
+ let(:parent) { build(:generic_work) }
30
+
31
+ let(:form) do
32
+ CurationConcerns::Forms::FileManagerForm.new(parent, nil)
35
33
  end
36
34
 
37
35
  let(:blacklight_config) { CatalogController.new.blacklight_config }
38
36
 
39
37
  before do
40
- allow(parent_presenter).to receive(:member_presenters).and_return([file_set, member])
41
- assign(:presenter, parent_presenter)
38
+ allow(parent).to receive(:etag).and_return("123456")
39
+ allow(parent).to receive(:persisted?).and_return(true)
40
+ allow(parent).to receive(:id).and_return('resource')
41
+
42
+ allow(form).to receive(:member_presenters).and_return([file_set, member])
43
+ assign(:form, form)
42
44
  # Blacklight nonsense
43
45
  allow(view).to receive(:dom_class) { '' }
44
46
  allow(view).to receive(:blacklight_config).and_return(blacklight_config)
@@ -1,9 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe 'curation_concerns/base/show.json.jbuilder' do
4
- let(:curation_concern) { FactoryGirl.create(:generic_work) }
4
+ let(:curation_concern) { create(:generic_work) }
5
5
 
6
6
  before do
7
+ allow(curation_concern).to receive(:etag).and_return('W/"87f79d2244ded4239ad1f0e822c8429b1e72b66c"')
7
8
  assign(:curation_concern, curation_concern)
8
9
  render
9
10
  end
@@ -17,5 +18,6 @@ describe 'curation_concerns/base/show.json.jbuilder' do
17
18
  expected_fields.each do |field_symbol|
18
19
  expect(json).to have_key(field_symbol.to_s)
19
20
  end
21
+ expect(json['version']).to eq 'W/"87f79d2244ded4239ad1f0e822c8429b1e72b66c"'
20
22
  end
21
23
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: curation_concerns
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.3
4
+ version: 1.7.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Zumwalt
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2017-02-03 00:00:00.000000000 Z
13
+ date: 2017-02-07 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: hydra-head
@@ -771,6 +771,7 @@ files:
771
771
  - app/actors/curation_concerns/actors/interpret_visibility_actor.rb
772
772
  - app/actors/curation_concerns/actors/lease_actor.rb
773
773
  - app/actors/curation_concerns/actors/root_actor.rb
774
+ - app/actors/curation_concerns/optimistic_lock_validator.rb
774
775
  - app/assets/images/audio.png
775
776
  - app/assets/images/default.png
776
777
  - app/assets/images/loading.gif
@@ -863,6 +864,7 @@ files:
863
864
  - app/conversions/power_converters/sipity_workflow_state.rb
864
865
  - app/forms/curation_concerns/forms.rb
865
866
  - app/forms/curation_concerns/forms/collection_edit_form.rb
867
+ - app/forms/curation_concerns/forms/file_manager_form.rb
866
868
  - app/forms/curation_concerns/forms/file_set_edit_form.rb
867
869
  - app/forms/curation_concerns/forms/work_form.rb
868
870
  - app/forms/curation_concerns/forms/workflow_action_form.rb
@@ -958,6 +960,7 @@ files:
958
960
  - app/presenters/curation_concerns/file_set_presenter.rb
959
961
  - app/presenters/curation_concerns/inspect_work_presenter.rb
960
962
  - app/presenters/curation_concerns/lease_presenter.rb
963
+ - app/presenters/curation_concerns/member_presenter_factory.rb
961
964
  - app/presenters/curation_concerns/model_proxy.rb
962
965
  - app/presenters/curation_concerns/permission_badge.rb
963
966
  - app/presenters/curation_concerns/presenter_factory.rb
@@ -1318,6 +1321,7 @@ files:
1318
1321
  - spec/actors/curation_concerns/initialize_workflow_actor_spec.rb
1319
1322
  - spec/actors/curation_concerns/interpret_visibility_actor_spec.rb
1320
1323
  - spec/actors/curation_concerns/lease_actor_spec.rb
1324
+ - spec/actors/curation_concerns/optimistic_lock_validator_spec.rb
1321
1325
  - spec/actors/curation_concerns/work_actor_spec.rb
1322
1326
  - spec/controllers/accepts_batches_controller_spec.rb
1323
1327
  - spec/controllers/catalog_controller_spec.rb
@@ -1457,6 +1461,7 @@ files:
1457
1461
  - spec/presenters/curation_concerns/collection_presenter_spec.rb
1458
1462
  - spec/presenters/curation_concerns/file_set_presenter_spec.rb
1459
1463
  - spec/presenters/curation_concerns/inspect_work_presenter_spec.rb
1464
+ - spec/presenters/curation_concerns/member_presenter_factory_spec.rb
1460
1465
  - spec/presenters/curation_concerns/permission_badge_spec.rb
1461
1466
  - spec/presenters/curation_concerns/presenter_factory_spec.rb
1462
1467
  - spec/presenters/curation_concerns/single_use_link_presenter_spec.rb
@@ -1544,6 +1549,7 @@ files:
1544
1549
  - spec/views/curation_concerns/admin/index.html.erb_spec.rb
1545
1550
  - spec/views/curation_concerns/admin/widgets/_pie.html.erb_spec.rb
1546
1551
  - spec/views/curation_concerns/base/_attributes.html.erb_spec.rb
1552
+ - spec/views/curation_concerns/base/_form.html.erb_spec.rb
1547
1553
  - spec/views/curation_concerns/base/_form_rights_spec.rb
1548
1554
  - spec/views/curation_concerns/base/_member.html.erb_spec.rb
1549
1555
  - spec/views/curation_concerns/base/_show_actions.html.erb_spec.rb
@@ -1591,7 +1597,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1591
1597
  version: '0'
1592
1598
  requirements: []
1593
1599
  rubyforge_project:
1594
- rubygems_version: 2.5.2
1600
+ rubygems_version: 2.6.8
1595
1601
  signing_key:
1596
1602
  specification_version: 4
1597
1603
  summary: A Rails Engine that allows an application to CRUD CurationConcern objects
@@ -1612,6 +1618,7 @@ test_files:
1612
1618
  - spec/actors/curation_concerns/initialize_workflow_actor_spec.rb
1613
1619
  - spec/actors/curation_concerns/interpret_visibility_actor_spec.rb
1614
1620
  - spec/actors/curation_concerns/lease_actor_spec.rb
1621
+ - spec/actors/curation_concerns/optimistic_lock_validator_spec.rb
1615
1622
  - spec/actors/curation_concerns/work_actor_spec.rb
1616
1623
  - spec/controllers/accepts_batches_controller_spec.rb
1617
1624
  - spec/controllers/catalog_controller_spec.rb
@@ -1751,6 +1758,7 @@ test_files:
1751
1758
  - spec/presenters/curation_concerns/collection_presenter_spec.rb
1752
1759
  - spec/presenters/curation_concerns/file_set_presenter_spec.rb
1753
1760
  - spec/presenters/curation_concerns/inspect_work_presenter_spec.rb
1761
+ - spec/presenters/curation_concerns/member_presenter_factory_spec.rb
1754
1762
  - spec/presenters/curation_concerns/permission_badge_spec.rb
1755
1763
  - spec/presenters/curation_concerns/presenter_factory_spec.rb
1756
1764
  - spec/presenters/curation_concerns/single_use_link_presenter_spec.rb
@@ -1838,6 +1846,7 @@ test_files:
1838
1846
  - spec/views/curation_concerns/admin/index.html.erb_spec.rb
1839
1847
  - spec/views/curation_concerns/admin/widgets/_pie.html.erb_spec.rb
1840
1848
  - spec/views/curation_concerns/base/_attributes.html.erb_spec.rb
1849
+ - spec/views/curation_concerns/base/_form.html.erb_spec.rb
1841
1850
  - spec/views/curation_concerns/base/_form_rights_spec.rb
1842
1851
  - spec/views/curation_concerns/base/_member.html.erb_spec.rb
1843
1852
  - spec/views/curation_concerns/base/_show_actions.html.erb_spec.rb