-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
293 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
class DocumentsS3File < ApplicationRecord | ||
belongs_to :document | ||
belongs_to :s3_file | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
module SlateUtils | ||
class SlateChildren < Array | ||
def to_json | ||
map(&:to_h).to_json | ||
end | ||
|
||
def self.from_a(array) | ||
new(array.map { |h| SlateNode.from_h(h) }) | ||
end | ||
|
||
def traverse(&block) | ||
each do |node| | ||
node.traverse(&block) | ||
end | ||
end | ||
end | ||
|
||
class SlateNode | ||
SPECIAL_ATTRIBUTES = %i[type text children].freeze | ||
|
||
attr_accessor :type, :text, :children, :attributes | ||
|
||
def initialize(type: nil, text: nil, attributes: {}, children: nil) | ||
@type = type | ||
@text = text | ||
@attributes = attributes | ||
@children = children | ||
end | ||
|
||
def element? | ||
!@type.nil? | ||
end | ||
|
||
def text? | ||
!@text.nil? | ||
end | ||
|
||
def traverse(&block) | ||
yield self | ||
children&.traverse(&block) | ||
end | ||
|
||
def self.from_h(hash) | ||
new( | ||
type: hash[:type], | ||
text: hash[:text], | ||
attributes: hash.reject { |k, _| SPECIAL_ATTRIBUTES.include?(k) }, | ||
children: hash[:children]&.then { |children| SlateChildren.from_a(children) } | ||
) | ||
end | ||
|
||
def self.parse(json) | ||
parsed = JSON.parse(json, symbolize_names: true) | ||
|
||
if parsed.is_a?(Array) | ||
SlateChildren.from_a(parsed) | ||
else | ||
from_h(parsed) | ||
end | ||
end | ||
|
||
def to_h | ||
{}.tap do |h| | ||
h[:type] = type unless type.nil? | ||
h[:text] = text unless text.nil? | ||
h.merge!(attributes) | ||
h[:children] = children.map(&:to_h) unless children.nil? | ||
end | ||
end | ||
|
||
def to_json | ||
to_h.to_json | ||
end | ||
end | ||
end |
16 changes: 16 additions & 0 deletions
16
db/migrate/20231125151117_create_join_table_documents_s3_files.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
class CreateJoinTableDocumentsS3Files < ActiveRecord::Migration[7.0] | ||
def up | ||
create_join_table :documents, :s3_files do |t| | ||
t.index [:document_id, :s3_file_id] | ||
t.index [:s3_file_id, :document_id] | ||
end | ||
|
||
Document.find_each do |document| | ||
document.update_linked_s3_files | ||
end | ||
end | ||
|
||
def down | ||
drop_join_table :documents, :s3_files | ||
end | ||
end |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
require 'test_helper' | ||
|
||
class DocumentsS3FileTest < ActiveSupport::TestCase | ||
include SlateJSONHelper | ||
|
||
def setup | ||
@user_1 = create(:user) | ||
@user_2 = create(:user) | ||
|
||
@s3_file_1 = create(:s3_file, owner: @user_1) | ||
@s3_file_2 = create(:s3_file, owner: @user_1) | ||
|
||
@user_1_project = create(:project, owner: @user_1) | ||
@user_2_project = create(:project, owner: @user_2) | ||
end | ||
|
||
test 'document with no attachments has no linked s3_files' do | ||
document = create_document_with_attachment_ids([], project: @user_1_project) | ||
assert_empty document.s3_files | ||
end | ||
|
||
test 'document with one attachment has one linked s3_file' do | ||
document = create_document_with_attachment_ids([@s3_file_1.id], project: @user_1_project) | ||
assert_equal [@s3_file_1], document.s3_files | ||
end | ||
|
||
test 'linked s3_files are updated when document is updated' do | ||
document = create_document_with_attachment_ids([@s3_file_1.id], project: @user_1_project) | ||
document.update!(body: document_body_with_attachment_ids([@s3_file_2.id])) | ||
assert_equal [@s3_file_2], document.s3_files | ||
end | ||
|
||
test 'no error when s3 file does not exist' do | ||
fake_s3_file_id = 123456789 | ||
assert_nil S3File.find_by(id: fake_s3_file_id) | ||
document = create_document_with_attachment_ids([fake_s3_file_id], project: @user_1_project) | ||
assert_empty document.s3_files | ||
end | ||
|
||
test 'cannot link s3 file belonging to another user' do | ||
document = create_document_with_attachment_ids([@s3_file_1.id], project: @user_2_project) | ||
assert_empty document.s3_files | ||
end | ||
|
||
private | ||
|
||
def create_document_with_attachment_ids(s3_file_ids, **args) | ||
create( | ||
:document, | ||
body: document_body_with_attachment_ids(s3_file_ids), | ||
body_type: 'json/slate', | ||
**args | ||
) | ||
end | ||
|
||
def document_body_with_attachment_ids(s3_file_ids) | ||
create_document_body do | ||
p do | ||
text 'Hello, world!' | ||
end | ||
|
||
s3_file_ids.each do |s3_file_id| | ||
# Future proofing: Make sure attachments in descendants are counted | ||
p do | ||
p do | ||
attachment s3_file_id: s3_file_id, filename: 'test.txt' | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
require 'test_helper' | ||
|
||
class SlateUtilsTest < ActiveSupport::TestCase | ||
include SlateJSONHelper | ||
|
||
test 'SlateNode parses and serializes document bodies' do | ||
original_json = create_document_body do | ||
p do | ||
text 'Hello, world!', bold: true | ||
end | ||
|
||
blockquote do | ||
p do | ||
text 'This is a blockquote' | ||
end | ||
end | ||
end | ||
|
||
expected_json = create_document_body do | ||
p do | ||
text 'Goodbye, world!', bold: true | ||
end | ||
|
||
blockquote do | ||
p do | ||
text 'This is a blockquote' | ||
end | ||
end | ||
end | ||
|
||
slate_nodes = SlateUtils::SlateNode.parse(original_json) | ||
slate_nodes[0].children[0].text = 'Goodbye, world!' | ||
slate_nodes_json = slate_nodes.to_json | ||
|
||
assert_equal JSON.parse(expected_json), JSON.parse(slate_nodes_json) | ||
end | ||
|
||
test 'traverses document bodies' do | ||
json = create_document_body do | ||
p do | ||
text 'Hello, world!', bold: true | ||
end | ||
|
||
blockquote do | ||
p do | ||
text 'This is a blockquote' | ||
end | ||
end | ||
end | ||
|
||
slate_nodes = SlateUtils::SlateNode.parse(json) | ||
|
||
nodes = [] | ||
|
||
slate_nodes.traverse do |node| | ||
nodes << node | ||
end | ||
|
||
first_paragraph = nodes[0] | ||
first_text = nodes[1] | ||
blockquote = nodes[2] | ||
second_paragraph = nodes[3] | ||
second_text = nodes[4] | ||
|
||
assert_equal 'p', first_paragraph.type | ||
assert_equal 'Hello, world!', first_text.text | ||
assert first_text.attributes[:bold], 'first_text should be bold' | ||
assert_equal 'blockquote', blockquote.type | ||
assert_equal 'p', second_paragraph.type | ||
assert_equal 'This is a blockquote', second_text.text | ||
assert_nil second_text.attributes[:bold], 'second_text should not be bold' | ||
end | ||
end |
Oops, something went wrong.