works_cited 0.1.14 → 0.1.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +27 -2
  3. data/VERSION +1 -1
  4. data/app/assets/javascripts/works_cited/addFields.js +44 -0
  5. data/app/assets/javascripts/works_cited/application.js +6 -1
  6. data/app/assets/javascripts/works_cited/loadPreview.js +45 -0
  7. data/app/assets/javascripts/works_cited/removeFields.js +37 -0
  8. data/app/assets/javascripts/works_cited/shared.js +59 -0
  9. data/app/assets/javascripts/works_cited/showFields.js +9 -0
  10. data/app/assets/javascripts/works_cited/submitForm.js +33 -0
  11. data/app/controllers/concerns/works_cited/params.rb +24 -0
  12. data/app/controllers/works_cited/citations_controller.rb +26 -13
  13. data/app/helpers/works_cited/application_helper.rb +46 -1
  14. data/app/models/works_cited/citation.rb +30 -34
  15. data/app/models/works_cited/contributor.rb +11 -28
  16. data/app/views/works_cited/citation_types/fields/_book.html.haml +1 -1
  17. data/app/views/works_cited/citation_types/fields/_electronic.html.haml +1 -1
  18. data/app/views/works_cited/citation_types/fields/_email.html.haml +1 -1
  19. data/app/views/works_cited/citation_types/fields/_interview.html.haml +1 -1
  20. data/app/views/works_cited/citation_types/fields/_periodical.html.haml +1 -1
  21. data/app/views/works_cited/citation_types/fields/_tweet.html.haml +1 -1
  22. data/app/views/works_cited/citations/_citation_fields.html.haml +28 -0
  23. data/app/views/works_cited/citations/_fields.html.haml +14 -0
  24. data/app/views/works_cited/citations/_form.html.haml +16 -53
  25. data/app/views/works_cited/citations/preview.html.haml +1 -1
  26. data/app/views/works_cited/contributors/_contributor_fields.html.haml +11 -0
  27. data/config/routes.rb +8 -2
  28. data/db/migrate/20210915160902_add_index_to_works_cited_contributors.rb +5 -1
  29. data/lib/works_cited/mixins/has_works_cited.rb +14 -1
  30. data/spec/dummy/app/controllers/doodads_controller.rb +2 -1
  31. data/spec/dummy/app/controllers/things_controller.rb +1 -1
  32. data/spec/dummy/app/views/doodads/_form.html.haml +2 -0
  33. data/spec/dummy/db/development.sqlite3 +0 -0
  34. data/spec/dummy/db/migrate/20210915161011_create_works_cited_citations.works_cited.rb +1 -0
  35. data/spec/dummy/db/migrate/20210915161012_create_works_cited_contributors.works_cited.rb +1 -0
  36. data/spec/dummy/db/migrate/20210915161013_add_index_to_works_cited_contributors.works_cited.rb +4 -1
  37. data/spec/dummy/db/schema.rb +60 -59
  38. data/spec/dummy/db/test.sqlite3 +0 -0
  39. data/spec/dummy/log/development.log +26893 -15
  40. data/spec/dummy/log/test.log +24838 -0
  41. data/spec/factories/things.rb +8 -0
  42. data/spec/models/doodad_spec.rb +13 -1
  43. data/spec/models/thing_spec.rb +13 -1
  44. data/spec/models/works_cited/citation_spec.rb +12 -0
  45. data/spec/requests/works_cited/citations_spec.rb +10 -8
  46. data/spec/routing/works_cited/citations_routing_spec.rb +5 -1
  47. data/works_cited.gemspec +14 -4
  48. metadata +13 -3
  49. data/app/views/works_cited/contributors/_fields.html.haml +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aa5b9e63b43e8210682d2664ea515311f6e752fe7d2149b46dfeea134db70427
4
- data.tar.gz: 81b60e1bcb0fbddb8fd370dc1878998cedb7d2d8f22e3f1bd8a7c73cc681a940
3
+ metadata.gz: 5db67f2a964c0433ad04e7e3fba13605d8bd38cf6d743a56f3eebf71e08a016d
4
+ data.tar.gz: f61b70f80049f28cbb897e2e4c9b1d107237838460266a71ae0c54b318d80cc5
5
5
  SHA512:
6
- metadata.gz: '09189d70a8ab33d4d956ce4372495df050768fc96f844a9cdfd7ba0422bba2b710e96d08366fc899aef1f010cc0154209951eb3179835c3ceb62d44293e352d3'
7
- data.tar.gz: 39f009806bde960422e5e192e3a36f6b40d34a616eea0f919f3cdc174fc4fa9ec26d5adc5731d5561fdeb2c3010372effcbaced13af46fb2f7da5f5618355fe9
6
+ metadata.gz: bab946554a8189670c7a8b5de7b15cd208ddc0df3c0d7539ad8200324cb2091e5b44452a3b3380b692e7a720f142cd4cec4b3c1a41b6118bb7630469f3be4d4f
7
+ data.tar.gz: 6989928293676bce8baf3332ad34c837394cb51e0b096e2997b980d8a54e51801536e259b355548f1a8a22fdc94338bdc9b22c7ee19212a47bca5f91127edd71
data/README.md CHANGED
@@ -3,8 +3,6 @@ Works Cited allows you to add a list of the works cited in ActiveRecord objects,
3
3
 
4
4
  Works Cited uses CanCanCan to authorize the editing of citations. This makes it easy for you to control access.
5
5
 
6
- Works Cited is compatible with, but does not require, Rails Admin.
7
-
8
6
  ## Installation
9
7
  Add this line to your application's Gemfile:
10
8
 
@@ -85,6 +83,33 @@ Add the helpers to the relevant views
85
83
  = works_cited_list @record
86
84
  ```
87
85
 
86
+ To add routes so that you can edit citations on their own pages:
87
+
88
+ ```ruby
89
+ mount WorksCited::Engine => '/works_cited'
90
+ ```
91
+
92
+ To add the fields, nested inside your forms
93
+
94
+ ```haml
95
+ = form_for(@doodad) do |f|
96
+ = works_cited_citations_fields f
97
+ ```
98
+
99
+ Don't forget to add the controller concern to enable nested attributes with strong parameters
100
+
101
+ ```ruby
102
+ class RecipesController < ApplicationController
103
+ include Cookbook::Params
104
+
105
+ #...
106
+
107
+ def recipe_params
108
+ params.require(:doodad).permit(:name, :description, works_cited_params)
109
+ end
110
+ end
111
+ ```
112
+
88
113
  ## Contributing
89
114
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
90
115
  * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.14
1
+ 0.1.15
@@ -0,0 +1,44 @@
1
+ class addFields {
2
+ // This executes when the function is instantiated.
3
+ constructor() {
4
+ this.links = document.querySelectorAll('.add_fields')
5
+ this.iterateLinks()
6
+ const event = new Event('works_cited:load')
7
+ window.dispatchEvent(event);
8
+ }
9
+
10
+ iterateLinks() {
11
+ // If there are no links on the page, stop the function from executing.
12
+ if (this.links.length === 0) return
13
+ // Loop over each link on the page. A page could have multiple nested forms.
14
+ this.links.forEach(link => {
15
+ link.addEventListener('click', e => {
16
+ this.handleClick(link, e)
17
+ })
18
+ })
19
+ }
20
+
21
+ handleClick(link, e) {
22
+ // Stop the function from executing if a link or event were not passed into the function.
23
+ if (!link || !e) return
24
+ // Prevent the browser from following the URL.
25
+ e.preventDefault()
26
+ // Save a unique timestamp to ensure the key of the associated array is unique.
27
+ let time = new Date().getTime()
28
+ // Save the data id attribute into a variable. This corresponds to `new_object.object_id`.
29
+ let linkId = link.dataset.id
30
+ // Create a new regular expression needed to find any instance of the `new_object.object_id` used in the fields data attribute if there's a value in `linkId`.
31
+ let regexp = linkId ? new RegExp(linkId, 'g') : null
32
+ // Replace all instances of the `new_object.object_id` with `time`, and save markup into a variable if there's a value in `regexp`.
33
+ let newFields = regexp ? link.dataset.fields.replace(regexp, time) : null
34
+ // Add the new markup to the form if there are fields to add.
35
+ newFields ? link.insertAdjacentHTML('beforebegin', newFields) : null
36
+ // Tell it new fields are there
37
+ const event = new Event('works_cited:add_fields')
38
+ window.dispatchEvent(event);
39
+ }
40
+ }
41
+
42
+ // Wait for turbolinks to load, otherwise `document.querySelectorAll()` won't work
43
+ window.addEventListener('load', () => new addFields())
44
+ window.addEventListener('works_cited:add_fields', () => new addFields())
@@ -1 +1,6 @@
1
- //= require vanilla_nested
1
+ //= require works_cited/shared.js
2
+ //= require works_cited/addFields.js
3
+ //= require works_cited/removeFields.js
4
+ //= require works_cited/showFields.js
5
+ //= require works_cited/loadPreview.js
6
+ //= require works_cited/submitForm.js
@@ -0,0 +1,45 @@
1
+ function startPreview(url, id, citationLocator) {
2
+ var element = document.getElementById(id)
3
+ var fields = element.querySelectorAll('input, select');
4
+ var index = 0;
5
+
6
+ for (index = 0; index < fields.length; ++index) {
7
+ var textField = fields[index];
8
+ textField.addEventListener('change', (e) => loadPreview(url, citationLocator))
9
+ }
10
+ loadPreview(url, citationLocator)
11
+ }
12
+
13
+ function loadPreview(url, locator) {
14
+ // Build formData object.
15
+ var formData = new FormData(document.querySelector('form'))
16
+ var data = {}
17
+ data.citation = getFieldByLocator(formData, locator)
18
+ var authenticity_token = document.head.querySelector('meta[name="csrf-token"]').content
19
+ var preview = document.getElementById('preview')
20
+ var container = document.getElementById('preview-html')
21
+
22
+ fetch(url + '.json',
23
+ {
24
+ method: 'post',
25
+ headers: {
26
+ 'X-Requested-With': 'XMLHttpRequest',
27
+ 'X-CSRF-Token': authenticity_token,
28
+ 'Content-type': 'application/json'
29
+ },
30
+ body: JSON.stringify(data),
31
+ credentials: 'same-origin'
32
+ })
33
+ .then(function(response) {
34
+ // When the page is loaded convert it to json
35
+ return response.json()
36
+ })
37
+ .then(function(json) {
38
+ // get the html value for it
39
+ return json.html
40
+ })
41
+ .then((html) => {
42
+ preview.style.display = 'block'
43
+ container.innerHTML = html
44
+ })
45
+ }
@@ -0,0 +1,37 @@
1
+ class removeFields {
2
+ // This executes when the function is instantiated.
3
+ constructor() {
4
+ this.iterateLinks()
5
+ }
6
+
7
+ iterateLinks() {
8
+ document.querySelectorAll('.remove_fields').forEach(link => {
9
+ link.addEventListener('click', e => {
10
+ this.handleClick(link, e)
11
+ })
12
+ })
13
+ }
14
+
15
+ handleClick(link, e) {
16
+ // Stop the function from executing if a link or event were not passed into the function.
17
+ if (!link || !e) return
18
+ // Prevent the browser from following the URL.
19
+ e.preventDefault()
20
+ // Find the parent wrapper for the set of nested fields.
21
+ let fieldParent = link.closest('.nested-fields')
22
+ // If there is a parent wrapper, find the hidden delete field.
23
+ let deleteField = fieldParent
24
+ ? fieldParent.querySelector('input[type="hidden"]')
25
+ : null
26
+ // If there is a delete field, update the value to `1` and hide the corresponding nested fields.
27
+ if (deleteField) {
28
+ deleteField.value = 1
29
+ fieldParent.style.display = 'none'
30
+ }
31
+ }
32
+ }
33
+
34
+ // Wait for turbolinks to load, otherwise `document.querySelectorAll()` won't work
35
+ window.addEventListener('load', () => new removeFields())
36
+ window.addEventListener('works_cited:load', () => new removeFields())
37
+ window.addEventListener('works_cited:add_fields', () => new removeFields())
@@ -0,0 +1,59 @@
1
+ function packInContainer(container, keyParts, value) {
2
+ var part = keyParts.shift()
3
+ var packed = value
4
+ if(keyParts.length > 0) {
5
+ var previous = container[part] || {}
6
+ packed = packInContainer(previous, keyParts, value)
7
+ }
8
+
9
+ return {...container, [part]: packed}
10
+ }
11
+
12
+ function getWritableField(key) {
13
+ var fields = document.getElementsByName(key)
14
+ var found
15
+
16
+ fields.forEach((field) => {
17
+ if(field.readonly === undefined || !field.readonly) {
18
+ if(field.value) {
19
+ found = field.value
20
+ return
21
+ }
22
+ }
23
+ })
24
+
25
+ return found || ''
26
+ }
27
+
28
+ function getFieldByLocator(data, locator) {
29
+ var regex = /^\[(.*)\]$/
30
+ var container = {}
31
+
32
+ // Display the key/value pairs
33
+ for(var pair of data.entries()) {
34
+ // We want to avoid using pair[1] even though it's there because some fields are readonly
35
+ var key = pair[0]
36
+ if(key.startsWith(locator)) {
37
+ var strippedKeyString = key.replace(locator, '').replace(regex, "$1")
38
+ var keyParts = strippedKeyString.split('][')
39
+ var value = getWritableField(key)
40
+ console.warn(strippedKeyString)
41
+ container = packInContainer(container, keyParts, value)
42
+ }
43
+ }
44
+
45
+ return container
46
+ }
47
+
48
+ function getFormDataPacked(data) {
49
+ var container = {}
50
+
51
+ // Display the key/value pairs
52
+ for(var pair of data.entries()) {
53
+ // We want to avoid using pair[1] even though it's there because some fields are readonly
54
+ var key = pair[0]
55
+ container[key] = getWritableField(key)
56
+ }
57
+
58
+ return container
59
+ }
@@ -0,0 +1,9 @@
1
+ function showFields(citation_id, citation_type) {
2
+ citation = document.getElementById(`citation-${citation_id}`)
3
+ for (let element of citation.getElementsByClassName('fields')){
4
+ element.style.display='none'
5
+ element.readonly=true
6
+ }
7
+ console.warn(citation.getElementsByClassName(`fields-${citation_type}`)[0])
8
+ citation.getElementsByClassName(`fields-${citation_type}`)[0].style.display = 'block'
9
+ }
@@ -0,0 +1,33 @@
1
+ function post(path, params, method = 'post') {
2
+
3
+ // The rest of this code assumes you are not using a library.
4
+ // It can be made less verbose if you use one.
5
+ const form = document.createElement('form')
6
+ form.method = method
7
+ form.action = path
8
+
9
+ for (const key in params) {
10
+ if (params.hasOwnProperty(key)) {
11
+ const hiddenField = document.createElement('input')
12
+ hiddenField.type = 'hidden'
13
+ hiddenField.name = key
14
+ hiddenField.value = params[key]
15
+
16
+ form.appendChild(hiddenField)
17
+ }
18
+ }
19
+
20
+ document.body.appendChild(form)
21
+ form.submit()
22
+ }
23
+
24
+ function addFilterToSubmit() {
25
+ var form = document.querySelector('form')
26
+
27
+ form.onsubmit = function (e) {
28
+ e.preventDefault()
29
+ var formData = new FormData(form)
30
+ var revisedData = getFormDataPacked(formData)
31
+ return post(form.action, revisedData)
32
+ }
33
+ }
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WorksCited
4
+ # Concern to allow strong parameters when using accepts_nested_attributes_for to edit Cookbook Uses
5
+ module Params
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ # rescue_from Pundit::NotAuthorizedError, with: :authorization_error
10
+ # include Pundit
11
+ end
12
+
13
+ def works_cited_params
14
+ {
15
+ works_cited_citations_attributes: [
16
+ :id, :citation_type, :title, :container_title, :publisher, :city, :edition, :volume,
17
+ :number, :series, :year, :record, :media, :url, :pages, :published_at, :online_database, :doi,
18
+ :accessed_at, :_destroy,
19
+ { works_cited_contributors_attributes: %i[id contributor_role first middle last suffix handle _destroy] }
20
+ ]
21
+ }
22
+ end
23
+ end
24
+ end
@@ -16,11 +16,10 @@ module WorksCited
16
16
 
17
17
  # GET /citations/preview
18
18
  def preview
19
- @citation = Citation.new(citation_params)
20
- authorize! :preview, @citation
21
-
22
- @contributors = contributors_from_params
23
- render :preview, layout: false
19
+ authorize! :preview, Citation
20
+ locals = { citation: citation_for_preview, contributors: contributors_for_preview }
21
+ html = render_to_string('preview', formats: [:html], layout: false, locals: locals)
22
+ render json: { html: html }
24
23
  end
25
24
 
26
25
  # GET /citations/new
@@ -55,9 +54,15 @@ module WorksCited
55
54
 
56
55
  private
57
56
 
58
- def contributors_from_params
57
+ def citation_for_preview
58
+ return Citation.new(preview_params) unless params[:id].present?
59
+
60
+ Citation.find(params[:id]).assign_attributes(preview_params)
61
+ end
62
+
63
+ def contributors_for_preview
59
64
  contributors_array = []
60
- raw_contributors = citation_params[:works_cited_contributors_attributes]
65
+ raw_contributors = preview_params[:works_cited_contributors_attributes]
61
66
  raw_contributors&.each do |_index, contributor|
62
67
  destroy = contributor.delete(:_destroy)
63
68
  next if destroy == '1'
@@ -92,12 +97,8 @@ module WorksCited
92
97
 
93
98
  def model_option_group(items, model)
94
99
  model_name = model.model_name
95
- OpenStruct.new(
96
- {
97
- record_type: model_name,
98
- records: items.map { |item| item_option(item, model_name) }
99
- }
100
- )
100
+ records = items.map { |item| item_option(item, model_name) }
101
+ OpenStruct.new({ record_type: model_name, records: records })
101
102
  end
102
103
 
103
104
  def set_records
@@ -109,6 +110,18 @@ module WorksCited
109
110
  end.compact
110
111
  end
111
112
 
113
+ # Only allow a list of trusted parameters through.
114
+ def preview_params
115
+ json = JSON.parse(request.raw_post)
116
+ json_params = ActionController::Parameters.new(json)
117
+ json_params.require(:citation).permit(
118
+ :id, :citation_type, :title, :container_title, :publisher, :city, :edition, :volume,
119
+ :number, :series, :year, :record, :media, :url, :pages, :published_at, :online_database, :doi,
120
+ :accessed_at,
121
+ works_cited_contributors_attributes: %i[id contributor_role first middle last suffix handle _destroy]
122
+ )
123
+ end
124
+
112
125
  # Only allow a list of trusted parameters through.
113
126
  def citation_params
114
127
  params.require(:citation).permit(
@@ -18,11 +18,15 @@ module WorksCited
18
18
  render path, citation: citation, contributors: contributors
19
19
  end
20
20
 
21
- def works_cited_citation_fields(form, citation_type)
21
+ def works_cited_type_fields(form, citation_type)
22
22
  path = partial_path_or_default('fields', citation_type)
23
23
  render path, f: form, citation_type: citation_type
24
24
  end
25
25
 
26
+ def works_cited_citations_fields(form)
27
+ render 'works_cited/citations/fields', form: form
28
+ end
29
+
26
30
  def list_names(names)
27
31
  first = first_contributor(names)
28
32
  first_contributor_name = first&.full_name(first.name_order)
@@ -36,6 +40,47 @@ module WorksCited
36
40
  end
37
41
  end
38
42
 
43
+ # This method creates a link with `data-id` `data-fields` attributes. These attributes are
44
+ # used to create new instances of the nested fields through Javascript.
45
+ def works_cited_link_to_add_fields(name, form, association, partial = nil)
46
+ # Takes an object (@person) and creates a new instance of its associated model (:addresses)
47
+ # To better understand, run the following in your terminal:
48
+ # rails c --sandbox
49
+ # @person = Person.new
50
+ # new_object = @person.send(:addresses).klass.new
51
+ new_object = form.object.send(association).klass.new
52
+
53
+ # Saves the unique ID of the object into a variable.
54
+ # This is needed to ensure the key of the associated array is unique. This is makes
55
+ # parsing the content in the `data-fields` attribute easier through Javascript.
56
+ # We could use another method to achive this.
57
+ id = new_object.object_id
58
+
59
+ # https://api.rubyonrails.org/ fields_for(record_name, record_object = nil, fields_options = {}, &block)
60
+ # record_name = :addresses
61
+ # record_object = new_object
62
+ # fields_options = { child_index: id }
63
+ # child_index` is used to ensure the key of the associated array is unique, and that it
64
+ # matched the value in the `data-id` attribute.
65
+ # `person[addresses_attributes][child_index_value][_destroy]`
66
+ fields = form.simple_fields_for(association, new_object, child_index: id) do |builder|
67
+ # `association.to_s.singularize + "_fields"` ends up evaluating to `address_fields`
68
+ # The render function will then look for `views/people/_address_fields.html.erb`
69
+ # The render function also needs to be passed the value of 'builder', because
70
+ # `views/people/_address_fields.html.erb` needs this to render the form tags.
71
+ partial ||= "#{association.to_s.singularize}_fields"
72
+ render(partial, record: new_object, f: builder)
73
+ end
74
+
75
+ # This renders a simple link, but passes information into `data` attributes.
76
+ # This info can be named anything we want, but in this case we chose `data-id:` and `data-fields:`.
77
+ # The `id:` is from `new_object.object_id`.
78
+ # The `fields:` are rendered from the `fields` blocks.
79
+ # We use `gsub("\n", "")` to remove anywhite space from the rendered partial.
80
+ # The `id:` value needs to match the value used in `child_index: id`.
81
+ link_to(name, '#', class: 'add_fields button button-action', data: { id: id, fields: fields.gsub("\n", '') })
82
+ end
83
+
39
84
  private
40
85
 
41
86
  def partial_path_or_default(purpose, citation_type)
@@ -17,20 +17,24 @@ module WorksCited
17
17
 
18
18
  # Relationships
19
19
  belongs_to :record, polymorphic: true
20
- has_many :works_cited_contributors, -> { order(:last, :first, :middle, :suffix, :handle) }, inverse_of: :works_cited_citation, class_name: 'WorksCited::Contributor',
21
- foreign_key: :works_cited_citation_id
22
- has_many :works_cited_authors, -> { authors.order(:last, :first, :middle, :suffix, :handle) }, inverse_of: :works_cited_citation, class_name: 'WorksCited::Contributor',
23
- foreign_key: :works_cited_citation_id
24
- accepts_nested_attributes_for :works_cited_contributors, allow_destroy: true
20
+ has_many :works_cited_contributors, lambda {
21
+ order(:last, :first, :middle, :suffix, :handle)
22
+ }, inverse_of: :works_cited_citation, class_name: 'WorksCited::Contributor',
23
+ foreign_key: :works_cited_citation_id, dependent: :destroy
24
+ has_many :works_cited_authors, lambda {
25
+ authors.order(:last, :first, :middle, :suffix, :handle)
26
+ }, inverse_of: :works_cited_citation, class_name: 'WorksCited::Contributor',
27
+ foreign_key: :works_cited_citation_id
28
+ accepts_nested_attributes_for :works_cited_contributors, reject_if: :all_blank, allow_destroy: true
25
29
 
26
30
  # Scopes
27
31
  scope :ordered_by_author, (lambda do
28
32
  joins(:works_cited_authors)
29
33
  .order('MIN(works_cited_contributors.last) ASC, '\
30
- 'MIN(works_cited_contributors.first) ASC, '\
31
- 'MIN(works_cited_contributors.middle) ASC, '\
32
- 'MIN(works_cited_contributors.suffix) ASC, '\
33
- 'MIN(works_cited_contributors.handle) ASC')
34
+ 'MIN(works_cited_contributors.first) ASC, '\
35
+ 'MIN(works_cited_contributors.middle) ASC, '\
36
+ 'MIN(works_cited_contributors.suffix) ASC, '\
37
+ 'MIN(works_cited_contributors.handle) ASC')
34
38
  .group(:id)
35
39
  end)
36
40
 
@@ -44,17 +48,31 @@ module WorksCited
44
48
  end
45
49
 
46
50
  # Instance Methods
51
+ def works_cited_contributors_attributes=(raw_contributors)
52
+ array = []
53
+ raw_contributors&.each do |_index, contributor|
54
+ destroy = contributor.delete(:_destroy)
55
+ if destroy == '1'
56
+ Contributor.find(contributor[:id]).destroy if contributor[:id]
57
+ next
58
+ end
59
+
60
+ array << contributor
61
+ end
62
+ super array
63
+ end
64
+
47
65
  def record=(value)
48
66
  unless value.is_a? String
49
67
  super(value)
50
68
  return
51
69
  end
52
70
 
53
- model_name, id = value.split(':')
54
- return unless model_name.present? && id.present?
71
+ model_name, my_id = value.split(':')
72
+ return unless model_name.present? && my_id.present?
55
73
 
56
74
  model = model_name.constantize
57
- super model.find(id)
75
+ super model.find(my_id)
58
76
  end
59
77
 
60
78
  def periodical?
@@ -76,27 +94,5 @@ module WorksCited
76
94
  def email?
77
95
  citation_type == 'email'
78
96
  end
79
-
80
- if defined?(RailsAdmin)
81
- rails_admin do
82
- visible false
83
- edit do
84
- field :citation_type, :enum do
85
- enum do
86
- WorksCited.configuration.valid_citation_types
87
- end
88
- end
89
- field :works_cited_contributors do
90
- label 'Contributors'
91
- end
92
- include_all_fields
93
- field :media
94
- field :record do
95
- # Can't remove this using :inverse_of because it's polymorphic
96
- visible false
97
- end
98
- end
99
- end
100
- end
101
97
  end
102
98
  end
@@ -57,28 +57,25 @@ module WorksCited
57
57
 
58
58
  def name_parts
59
59
  parts = []
60
- parts << first_name_or_initial
61
- parts << middle_initial
62
- parts << if suffix.present? && last.present?
63
- "#{last}, #{suffix}"
64
- else
65
- suffix.presence || last.presence
66
- end
60
+ parts << first_name_or_initial.presence
61
+ parts << middle_initial.presence
62
+ parts << [last.presence, suffix.presence].compact.join(', ').presence
67
63
  parts
68
64
  end
69
65
 
70
66
  def name_parts_reversed
71
67
  parts = []
72
- parts << "#{last}," if last.present?
73
- parts << first_name_or_initial
74
- parts << if suffix.present? && middle_initial.present?
75
- "#{middle_initial}, #{suffix}"
76
- else
77
- suffix.presence || middle_initial
78
- end
68
+ parts << [last.presence, name_other_parts.presence].compact.join(', ').presence
79
69
  parts
80
70
  end
81
71
 
72
+ def name_other_parts
73
+ other_parts = []
74
+ other_parts << first_name_or_initial.presence
75
+ other_parts << [middle_initial.presence, suffix.presence].compact.join(', ').presence
76
+ other_parts.compact.join(' ').presence
77
+ end
78
+
82
79
  def first_name_or_initial
83
80
  return nil unless first.present?
84
81
 
@@ -90,19 +87,5 @@ module WorksCited
90
87
 
91
88
  middle[0, 1]&.upcase
92
89
  end
93
-
94
- if defined?(RailsAdmin)
95
- rails_admin do
96
- visible false
97
- edit do
98
- field :contributor_role, :enum do
99
- enum do
100
- WorksCited.configuration.valid_contributor_roles
101
- end
102
- end
103
- include_all_fields
104
- end
105
- end
106
- end
107
90
  end
108
91
  end
@@ -1,4 +1,4 @@
1
- = f.input :url
1
+ = f.input :url, as: :string
2
2
  = f.input :title
3
3
  = f.input :edition, hint: 'like "1st ed."'
4
4
  = f.input :volume, hint: 'like "vol. 1"'
@@ -10,5 +10,5 @@
10
10
  = f.input :pages, hint: 'like "pp. 150-53"'
11
11
  = f.input :online_database
12
12
  = f.input :doi, label: 'Digital Object Identifier'
13
- = f.input :url
13
+ = f.input :url, as: :string
14
14
  = f.input :accessed_at, as: :string, hint: 'Will be parsed and formatted', input_html: { value: f.object.accessed_at&.mla_date }
@@ -10,4 +10,4 @@
10
10
  = f.input :pages, hint: 'like "pp. 150-53"'
11
11
  = f.input :online_database
12
12
  = f.input :doi, label: 'Digital Object Identifier'
13
- = f.input :url
13
+ = f.input :url, as: :string
@@ -9,4 +9,4 @@
9
9
  = f.input :series
10
10
  = f.input :published_at, as: :string, hint: 'Will be parsed and formatted', input_html: { value: f.object.published_at&.mla_date }
11
11
  = f.input :pages, hint: 'like "pp. 150-53"'
12
- = f.input :url
12
+ = f.input :url, as: :string
@@ -7,4 +7,4 @@
7
7
  = f.input :year
8
8
  = f.input :published_at, as: :string, hint: 'Will be parsed and formatted', input_html: { value: f.object.published_at&.mla_date }
9
9
  = f.input :pages, hint: 'like "pp. 150-53"'
10
- = f.input :url
10
+ = f.input :url, as: :string
@@ -1,4 +1,4 @@
1
1
  = f.input :title, label: 'Tweet text'
2
2
  = f.input :publisher, default: 'Twitter'
3
3
  = f.input :published_at, label: 'Time posted', as: :string, hint: 'Will be parsed and formatted', input_html: { value: f.object.published_at&.mla_datetime }
4
- = f.input :url
4
+ = f.input :url, as: :string