cff 0.2.0 → 0.9.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.
- checksums.yaml +5 -5
- data/CHANGES.md +252 -0
- data/CITATION.cff +83 -0
- data/Gemfile +3 -1
- data/LICENCE +1 -1
- data/README.md +177 -22
- data/Rakefile +17 -12
- data/bin/console +4 -3
- data/cff.gemspec +41 -22
- data/lib/cff.rb +25 -11
- data/lib/cff/entity.rb +35 -25
- data/lib/cff/errors.rb +53 -0
- data/lib/cff/file.rb +209 -18
- data/lib/cff/formatter/apa_formatter.rb +77 -0
- data/lib/cff/formatter/bibtex_formatter.rb +122 -0
- data/lib/cff/formatter/formatter.rb +63 -0
- data/lib/cff/identifier.rb +72 -0
- data/lib/cff/licensable.rb +45 -0
- data/lib/cff/model.rb +223 -89
- data/lib/cff/{model-part.rb → model_part.rb} +17 -9
- data/lib/cff/person.rb +45 -24
- data/lib/cff/reference.rb +564 -0
- data/lib/cff/util.rb +48 -17
- data/lib/cff/validatable.rb +54 -0
- data/lib/cff/version.rb +7 -4
- data/lib/schema/1.2.0.json +1882 -0
- metadata +124 -29
- data/.gitignore +0 -30
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/.travis.yml +0 -20
data/Rakefile
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2018-2021 The Ruby Citation File Format Developers.
|
2
4
|
#
|
3
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
6
|
# you may not use this file except in compliance with the License.
|
@@ -12,22 +14,25 @@
|
|
12
14
|
# See the License for the specific language governing permissions and
|
13
15
|
# limitations under the License.
|
14
16
|
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
17
|
+
require 'bundler/gem_tasks'
|
18
|
+
require 'rake/testtask'
|
19
|
+
require 'rdoc/task'
|
20
|
+
require 'rubocop/rake_task'
|
18
21
|
|
19
|
-
task :
|
22
|
+
task default: :test
|
20
23
|
|
21
24
|
Rake::TestTask.new(:test) do |t|
|
22
|
-
t.libs <<
|
23
|
-
t.libs <<
|
24
|
-
t.test_files = FileList[
|
25
|
+
t.libs << 'test'
|
26
|
+
t.libs << 'lib'
|
27
|
+
t.test_files = FileList['test/**/*_test.rb']
|
25
28
|
end
|
26
29
|
|
27
30
|
RDoc::Task.new do |r|
|
28
|
-
r.main =
|
29
|
-
r.rdoc_files.include(
|
30
|
-
r.options <<
|
31
|
-
r.options <<
|
31
|
+
r.main = 'README.md'
|
32
|
+
r.rdoc_files.include('README.md', 'LICENCE', 'CHANGES.md', 'lib/**/*.rb')
|
33
|
+
r.options << '--markup=markdown'
|
34
|
+
r.options << '--tab-width=2'
|
32
35
|
r.options << "-t Ruby CFF Library version #{::CFF::VERSION}"
|
33
36
|
end
|
37
|
+
|
38
|
+
RuboCop::RakeTask.new
|
data/bin/console
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require
|
4
|
-
require
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'cff'
|
5
6
|
|
6
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
8
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +11,5 @@ require "cff"
|
|
10
11
|
# require "pry"
|
11
12
|
# Pry.start
|
12
13
|
|
13
|
-
require
|
14
|
+
require 'irb'
|
14
15
|
IRB.start(__FILE__)
|
data/cff.gemspec
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2018-2021 The Ruby Citation File Format Developers.
|
2
4
|
#
|
3
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
6
|
# you may not use this file except in compliance with the License.
|
@@ -12,34 +14,51 @@
|
|
12
14
|
# See the License for the specific language governing permissions and
|
13
15
|
# limitations under the License.
|
14
16
|
|
15
|
-
|
16
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
17
|
-
require "cff/version"
|
17
|
+
require_relative 'lib/cff/version'
|
18
18
|
|
19
19
|
Gem::Specification.new do |spec|
|
20
|
-
spec.name =
|
20
|
+
spec.name = 'cff'
|
21
21
|
spec.version = CFF::VERSION
|
22
|
-
spec.authors = [
|
23
|
-
|
22
|
+
spec.authors = [
|
23
|
+
'Robert Haines',
|
24
|
+
'The Ruby Citation File Format Developers'
|
25
|
+
]
|
26
|
+
spec.email = ['robert.haines@manchester.ac.uk']
|
27
|
+
|
28
|
+
spec.summary = 'A Ruby library for manipulating CITATION.cff files.'
|
29
|
+
spec.description = 'See https://citation-file-format.github.io/ ' \
|
30
|
+
'for more info.'
|
31
|
+
spec.homepage = 'https://github.com/citation-file-format/ruby-cff'
|
32
|
+
spec.license = 'Apache-2.0'
|
24
33
|
|
25
|
-
spec.
|
26
|
-
|
27
|
-
|
28
|
-
|
34
|
+
spec.metadata = {
|
35
|
+
'bug_tracker_uri' => 'https://github.com/citation-file-format/ruby-cff/issues',
|
36
|
+
'changelog_uri' => 'https://github.com/citation-file-format/ruby-cff/blob/main/CHANGES.md',
|
37
|
+
'documentation_uri' => 'https://citation-file-format.github.io/ruby-cff/',
|
38
|
+
'source_code_uri' => 'https://github.com/citation-file-format/ruby-cff'
|
39
|
+
}
|
29
40
|
|
30
|
-
spec.files
|
31
|
-
f.match(%r{^(test|spec|features)
|
41
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
42
|
+
f.match(%r{^((test|spec|features)/|\.)})
|
32
43
|
end
|
33
|
-
|
44
|
+
|
45
|
+
spec.bindir = 'exe'
|
34
46
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
35
|
-
spec.require_paths = [
|
47
|
+
spec.require_paths = ['lib']
|
48
|
+
|
49
|
+
spec.required_ruby_version = '>= 2.6'
|
36
50
|
|
37
|
-
spec.
|
51
|
+
spec.add_runtime_dependency 'json_schema', '~> 0.20.0'
|
52
|
+
spec.add_runtime_dependency 'language_list', '~> 1.2'
|
38
53
|
|
39
|
-
spec.add_development_dependency
|
40
|
-
spec.add_development_dependency
|
41
|
-
spec.add_development_dependency
|
42
|
-
spec.add_development_dependency
|
43
|
-
spec.add_development_dependency
|
44
|
-
spec.add_development_dependency
|
54
|
+
spec.add_development_dependency 'minitest', '~> 5.14'
|
55
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
56
|
+
spec.add_development_dependency 'rdoc', '~> 6.3'
|
57
|
+
spec.add_development_dependency 'rubocop', '~> 1.15'
|
58
|
+
spec.add_development_dependency 'rubocop-minitest', '~> 0.13'
|
59
|
+
spec.add_development_dependency 'rubocop-performance', '~> 1.11.0'
|
60
|
+
spec.add_development_dependency 'rubocop-rake', '~> 0.5.0'
|
61
|
+
spec.add_development_dependency 'simplecov', '~> 0.20.0'
|
62
|
+
spec.add_development_dependency 'simplecov-lcov', '~> 0.8.0'
|
63
|
+
spec.add_development_dependency 'test_construct', '~> 2.0'
|
45
64
|
end
|
data/lib/cff.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2018-2021 The Ruby Citation File Format Developers.
|
2
4
|
#
|
3
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
6
|
# you may not use this file except in compliance with the License.
|
@@ -12,16 +14,11 @@
|
|
12
14
|
# See the License for the specific language governing permissions and
|
13
15
|
# limitations under the License.
|
14
16
|
|
15
|
-
require
|
16
|
-
require
|
17
|
+
require 'date'
|
18
|
+
require 'json'
|
19
|
+
require 'yaml'
|
17
20
|
|
18
|
-
require
|
19
|
-
require "cff/util"
|
20
|
-
require "cff/model-part"
|
21
|
-
require "cff/person"
|
22
|
-
require "cff/entity"
|
23
|
-
require "cff/model"
|
24
|
-
require "cff/file"
|
21
|
+
require 'language_list'
|
25
22
|
|
26
23
|
# This library provides a Ruby interface to manipulate CITATION.cff files. The
|
27
24
|
# primary entry points are Model and File.
|
@@ -29,5 +26,22 @@ require "cff/file"
|
|
29
26
|
# See the [CITATION.cff documentation](https://citation-file-format.github.io/)
|
30
27
|
# for more details.
|
31
28
|
module CFF
|
32
|
-
|
29
|
+
SCHEMA_PATH = ::File.join(__dir__, 'schema', '1.2.0.json') # :nodoc:
|
30
|
+
SCHEMA_FILE = JSON.parse(::File.read(SCHEMA_PATH)) # :nodoc:
|
33
31
|
end
|
32
|
+
|
33
|
+
require 'cff/version'
|
34
|
+
require 'cff/errors'
|
35
|
+
require 'cff/util'
|
36
|
+
require 'cff/licensable'
|
37
|
+
require 'cff/validatable'
|
38
|
+
require 'cff/model_part'
|
39
|
+
require 'cff/person'
|
40
|
+
require 'cff/entity'
|
41
|
+
require 'cff/identifier'
|
42
|
+
require 'cff/reference'
|
43
|
+
require 'cff/model'
|
44
|
+
require 'cff/file'
|
45
|
+
require 'cff/formatter/formatter'
|
46
|
+
require 'cff/formatter/apa_formatter'
|
47
|
+
require 'cff/formatter/bibtex_formatter'
|
data/lib/cff/entity.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2018-2021 The Ruby Citation File Format Developers.
|
2
4
|
#
|
3
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
6
|
# you may not use this file except in compliance with the License.
|
@@ -12,42 +14,55 @@
|
|
12
14
|
# See the License for the specific language governing permissions and
|
13
15
|
# limitations under the License.
|
14
16
|
|
15
|
-
|
17
|
+
##
|
16
18
|
module CFF
|
17
19
|
|
18
20
|
# An Entity can represent different types of entities, e.g., a publishing
|
19
21
|
# company, or conference. Like a Person, an Entity might have a number of
|
20
22
|
# roles, such as author, contact, editor, etc.
|
23
|
+
#
|
24
|
+
# Entity implements all of the fields listed in the
|
25
|
+
# [CFF standard](https://citation-file-format.github.io/). All fields
|
26
|
+
# are simple strings and can be set as such. A field which has not been set
|
27
|
+
# will return the empty string. The simple fields are (with defaults in
|
28
|
+
# parentheses):
|
29
|
+
#
|
30
|
+
# * `address`
|
31
|
+
# * `city`
|
32
|
+
# * `country`
|
33
|
+
# * `email`
|
34
|
+
# * `date_end` - *Note:* returns a `Date` object
|
35
|
+
# * `date_start` - *Note:* returns a `Date` object
|
36
|
+
# * `fax`
|
37
|
+
# * `location`
|
38
|
+
# * `name`
|
39
|
+
# * `orcid`
|
40
|
+
# * `post_code`
|
41
|
+
# * `region`
|
42
|
+
# * `tel`
|
43
|
+
# * `website`
|
21
44
|
class Entity < ModelPart
|
22
45
|
|
23
46
|
ALLOWED_FIELDS = [
|
24
|
-
'address',
|
25
|
-
'
|
26
|
-
'country',
|
27
|
-
'email',
|
28
|
-
'date-end',
|
29
|
-
'date-start',
|
30
|
-
'fax',
|
31
|
-
'location',
|
32
|
-
'name',
|
33
|
-
'orcid',
|
34
|
-
'post-code',
|
35
|
-
'region',
|
36
|
-
'tel',
|
37
|
-
'website'
|
47
|
+
'address', 'city', 'country', 'email', 'date-end', 'date-start', 'fax',
|
48
|
+
'location', 'name', 'orcid', 'post-code', 'region', 'tel', 'website'
|
38
49
|
].freeze # :nodoc:
|
39
50
|
|
40
51
|
# :call-seq:
|
41
52
|
# new(name) -> Entity
|
53
|
+
# new(name) { |entity| block } -> Entity
|
42
54
|
#
|
43
55
|
# Create a new Entity with the supplied name.
|
44
56
|
def initialize(param)
|
45
|
-
if Hash
|
46
|
-
|
57
|
+
if param.is_a?(Hash)
|
58
|
+
@fields = param
|
59
|
+
@fields.default = ''
|
47
60
|
else
|
48
61
|
@fields = Hash.new('')
|
49
62
|
@fields['name'] = param
|
50
63
|
end
|
64
|
+
|
65
|
+
yield self if block_given?
|
51
66
|
end
|
52
67
|
|
53
68
|
# :call-seq:
|
@@ -56,9 +71,7 @@ module CFF
|
|
56
71
|
# Set the `date-end` field. If a non-Date object is passed in it will
|
57
72
|
# be parsed into a Date.
|
58
73
|
def date_end=(date)
|
59
|
-
|
60
|
-
date = Date.parse(date)
|
61
|
-
end
|
74
|
+
date = Date.parse(date) unless date.is_a?(Date)
|
62
75
|
|
63
76
|
@fields['date-end'] = date
|
64
77
|
end
|
@@ -69,12 +82,9 @@ module CFF
|
|
69
82
|
# Set the `date-start` field. If a non-Date object is passed in it will
|
70
83
|
# be parsed into a Date.
|
71
84
|
def date_start=(date)
|
72
|
-
|
73
|
-
date = Date.parse(date)
|
74
|
-
end
|
85
|
+
date = Date.parse(date) unless date.is_a?(Date)
|
75
86
|
|
76
87
|
@fields['date-start'] = date
|
77
88
|
end
|
78
|
-
|
79
89
|
end
|
80
90
|
end
|
data/lib/cff/errors.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2018-2021 The Ruby Citation File Format Developers.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
##
|
18
|
+
module CFF
|
19
|
+
|
20
|
+
# Error is the base class for all errors raised by this library.
|
21
|
+
class Error < RuntimeError
|
22
|
+
|
23
|
+
def initialize(message = nil) # :nodoc:
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# ValidationError is raised when a CFF file fails validation. It contains
|
29
|
+
# details of each failure that was detected by the underlying JsonSchema
|
30
|
+
# library, which is used to perform the validation.
|
31
|
+
#
|
32
|
+
# Additionally, the `invalid_filename` flag is used to indicate whether the
|
33
|
+
# CFF file is named correctly. This is only used when validating a File;
|
34
|
+
# validating a Model directly will not set this flag to `true`.
|
35
|
+
class ValidationError < Error
|
36
|
+
|
37
|
+
# The list of JsonSchema::ValidationErrors found by the validator.
|
38
|
+
attr_reader :errors
|
39
|
+
|
40
|
+
# If a File was validated, was its filename invalid?
|
41
|
+
attr_reader :invalid_filename
|
42
|
+
|
43
|
+
def initialize(errors, invalid_filename: false) # :nodoc:
|
44
|
+
super('Validation error')
|
45
|
+
@errors = errors
|
46
|
+
@invalid_filename = invalid_filename
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s # :nodoc:
|
50
|
+
"#{super}: (Invalid filename: #{@invalid_filename}) #{@errors.join(' ')}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/cff/file.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2018-2021 The Ruby Citation File Format Developers.
|
2
4
|
#
|
3
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
6
|
# you may not use this file except in compliance with the License.
|
@@ -12,56 +14,245 @@
|
|
12
14
|
# See the License for the specific language governing permissions and
|
13
15
|
# limitations under the License.
|
14
16
|
|
15
|
-
|
17
|
+
##
|
16
18
|
module CFF
|
17
19
|
|
18
20
|
# File provides direct access to a CFF Model, with the addition of some
|
19
21
|
# filesystem utilities.
|
22
|
+
#
|
23
|
+
# To be a fully compliant and valid CFF file its filename should be
|
24
|
+
# 'CITATION.cff'. This class allows you to create files with any filename,
|
25
|
+
# and to validate the contents of those files independently of the preferred
|
26
|
+
# filename.
|
20
27
|
class File
|
21
28
|
|
29
|
+
# A comment to be inserted at the top of the resultant CFF file.
|
30
|
+
attr_reader :comment
|
31
|
+
|
32
|
+
# The filename of this CFF file.
|
33
|
+
attr_reader :filename
|
34
|
+
|
22
35
|
YAML_HEADER = "---\n" # :nodoc:
|
36
|
+
CFF_COMMENT = [
|
37
|
+
"This CITATION.cff file was created by ruby-cff (v #{CFF::VERSION}).",
|
38
|
+
'Gem: https://rubygems.org/gems/cff',
|
39
|
+
'CFF: https://citation-file-format.github.io/'
|
40
|
+
].freeze # :nodoc:
|
41
|
+
CFF_VALID_FILENAME = 'CITATION.cff' # :nodoc:
|
23
42
|
|
24
43
|
# :call-seq:
|
25
|
-
# new(title) -> File
|
26
|
-
# new(model) -> File
|
44
|
+
# new(filename, title) -> File
|
45
|
+
# new(filename, model) -> File
|
27
46
|
#
|
28
47
|
# Create a new File. Either a pre-existing Model can be passed in or, as
|
29
48
|
# with Model itself, a title can be supplied to initalize a new File.
|
30
49
|
#
|
31
50
|
# All methods provided by Model are also available directly on File
|
32
51
|
# objects.
|
33
|
-
def initialize(param)
|
34
|
-
|
35
|
-
param = Model.new(param)
|
36
|
-
end
|
52
|
+
def initialize(filename, param, comment = CFF_COMMENT, create: false)
|
53
|
+
param = Model.new(param) unless param.is_a?(Model)
|
37
54
|
|
55
|
+
@filename = filename
|
38
56
|
@model = param
|
57
|
+
@comment = comment
|
58
|
+
@dirty = create
|
39
59
|
end
|
40
60
|
|
41
61
|
# :call-seq:
|
42
|
-
# read(
|
62
|
+
# read(filename) -> File
|
43
63
|
#
|
44
64
|
# Read a file and parse it for subsequent manipulation.
|
45
65
|
def self.read(file)
|
46
|
-
|
66
|
+
content = ::File.read(file)
|
67
|
+
comment = File.parse_comment(content)
|
68
|
+
|
69
|
+
new(
|
70
|
+
file, YAML.safe_load(content, permitted_classes: [Date, Time]), comment
|
71
|
+
)
|
47
72
|
end
|
48
73
|
|
49
74
|
# :call-seq:
|
50
|
-
#
|
51
|
-
#
|
75
|
+
# open(filename) -> File
|
76
|
+
# open(filename) { |cff| block }
|
52
77
|
#
|
53
|
-
#
|
54
|
-
|
55
|
-
|
56
|
-
|
78
|
+
# With no associated block, File.open is a synonym for ::read. If the
|
79
|
+
# optional code block is given, it will be passed the opened file as an
|
80
|
+
# argument and the File object will automatically be written (if edited)
|
81
|
+
# and closed when the block terminates.
|
82
|
+
#
|
83
|
+
# File.open will create a new file if one does not already exist with the
|
84
|
+
# provided file name.
|
85
|
+
def self.open(file)
|
86
|
+
if ::File.exist?(file)
|
87
|
+
content = ::File.read(file)
|
88
|
+
comment = File.parse_comment(content)
|
89
|
+
yaml = YAML.safe_load(content, permitted_classes: [Date, Time])
|
90
|
+
else
|
91
|
+
comment = CFF_COMMENT
|
92
|
+
yaml = ''
|
93
|
+
end
|
94
|
+
|
95
|
+
cff = new(file, yaml, comment, create: (yaml == ''))
|
96
|
+
return cff unless block_given?
|
97
|
+
|
98
|
+
begin
|
99
|
+
yield cff
|
100
|
+
ensure
|
101
|
+
cff.write
|
57
102
|
end
|
103
|
+
end
|
58
104
|
|
59
|
-
|
105
|
+
# :call-seq:
|
106
|
+
# validate(filename, fail_on_filename: true) -> Array
|
107
|
+
#
|
108
|
+
# Read a file and return an array with the result. The result array is a
|
109
|
+
# three-element array, with `true`/`false` at index 0 to indicate
|
110
|
+
# pass/fail, an array of schema validation errors at index 1 (if any), and
|
111
|
+
# `true`/`false` at index 2 to indicate whether the filename passed/failed
|
112
|
+
# validation.
|
113
|
+
#
|
114
|
+
# You can choose whether filename validation failure should cause overall
|
115
|
+
# validation failure with the `fail_on_filename` parameter (default: true).
|
116
|
+
def self.validate(file, fail_on_filename: true)
|
117
|
+
File.read(file).validate(fail_on_filename: fail_on_filename)
|
118
|
+
end
|
119
|
+
|
120
|
+
# :call-seq:
|
121
|
+
# validate!(filename, fail_on_filename: true)
|
122
|
+
#
|
123
|
+
# Read a file and raise a ValidationError upon failure. If an error is
|
124
|
+
# raised it will contain the detected validation failures for further
|
125
|
+
# inspection.
|
126
|
+
#
|
127
|
+
# You can choose whether filename validation failure should cause overall
|
128
|
+
# validation failure with the `fail_on_filename` parameter (default: true).
|
129
|
+
def self.validate!(file, fail_on_filename: true)
|
130
|
+
File.read(file).validate!(fail_on_filename: fail_on_filename)
|
131
|
+
end
|
132
|
+
|
133
|
+
# :call-seq:
|
134
|
+
# write(filename, File)
|
135
|
+
# write(filename, Model)
|
136
|
+
# write(filename, yaml)
|
137
|
+
#
|
138
|
+
# Write the supplied File, Model or yaml string to `file`.
|
139
|
+
def self.write(file, cff, comment = '')
|
140
|
+
comment = cff.comment if cff.respond_to?(:comment)
|
141
|
+
cff = cff.to_yaml unless cff.is_a?(String)
|
142
|
+
content = File.format_comment(comment) + cff[YAML_HEADER.length...-1]
|
143
|
+
|
144
|
+
::File.write(file, content)
|
145
|
+
end
|
146
|
+
|
147
|
+
# :call-seq:
|
148
|
+
# validate(fail_fast: false, fail_on_filename: true) -> Array
|
149
|
+
#
|
150
|
+
# Validate this file and return an array with the result. The result array
|
151
|
+
# is a three-element array, with `true`/`false` at index 0 to indicate
|
152
|
+
# pass/fail, an array of schema validation errors at index 1 (if any), and
|
153
|
+
# `true`/`false` at index 2 to indicate whether the filename passed/failed
|
154
|
+
# validation.
|
155
|
+
#
|
156
|
+
# You can choose whether filename validation failure should cause overall
|
157
|
+
# validation failure with the `fail_on_filename` parameter (default: true).
|
158
|
+
def validate(fail_fast: false, fail_on_filename: true)
|
159
|
+
valid_filename = (::File.basename(@filename) == CFF_VALID_FILENAME)
|
160
|
+
result = (@model.validate(fail_fast: fail_fast) << valid_filename)
|
161
|
+
result[0] &&= valid_filename if fail_on_filename
|
162
|
+
|
163
|
+
result
|
164
|
+
end
|
165
|
+
|
166
|
+
# :call-seq:
|
167
|
+
# validate!(fail_fast: false, fail_on_filename: true)
|
168
|
+
#
|
169
|
+
# Validate this file and raise a ValidationError upon failure. If an error
|
170
|
+
# is raised it will contain the detected validation failures for further
|
171
|
+
# inspection.
|
172
|
+
#
|
173
|
+
# You can choose whether filename validation failure should cause overall
|
174
|
+
# validation failure with the `fail_on_filename` parameter (default: true).
|
175
|
+
def validate!(fail_fast: false, fail_on_filename: true)
|
176
|
+
result = validate(
|
177
|
+
fail_fast: fail_fast, fail_on_filename: fail_on_filename
|
178
|
+
)
|
179
|
+
return if result[0]
|
180
|
+
|
181
|
+
raise ValidationError.new(result[1], invalid_filename: !result[2])
|
182
|
+
end
|
183
|
+
|
184
|
+
# :call-seq:
|
185
|
+
# write(save_as: filename)
|
186
|
+
#
|
187
|
+
# Write this CFF File. The `save_as` parameter can be used to save a new
|
188
|
+
# copy of this CFF File under a different filename, leaving the original
|
189
|
+
# file untouched. If `save_as` is used then the internal filename of the
|
190
|
+
# File will be updated to the supplied filename.
|
191
|
+
def write(save_as: nil)
|
192
|
+
unless save_as.nil?
|
193
|
+
@filename = save_as
|
194
|
+
@dirty = true
|
195
|
+
end
|
196
|
+
|
197
|
+
File.write(@filename, @model, @comment) if @dirty
|
198
|
+
@dirty = false
|
199
|
+
end
|
200
|
+
|
201
|
+
# :call-seq:
|
202
|
+
# comment = string or array
|
203
|
+
#
|
204
|
+
# A comment to be inserted at the top of the resultant CFF file. This can
|
205
|
+
# be supplied as a simple string or an array of strings. When the file is
|
206
|
+
# saved this comment is formatted as follows:
|
207
|
+
#
|
208
|
+
# * a simple string is split into 75 character lines and `'# '` is prepended
|
209
|
+
# to each line;
|
210
|
+
# * an array of strings is joined into a single string with `'\n'` and
|
211
|
+
# `'# '` is prepended to each line;
|
212
|
+
#
|
213
|
+
# If you care about formatting, use an array of strings for your comment,
|
214
|
+
# if not, use a single string.
|
215
|
+
def comment=(comment)
|
216
|
+
@dirty = true
|
217
|
+
@comment = comment
|
218
|
+
end
|
219
|
+
|
220
|
+
def to_yaml # :nodoc:
|
221
|
+
@model.to_yaml
|
60
222
|
end
|
61
223
|
|
62
224
|
def method_missing(name, *args) # :nodoc:
|
63
|
-
@model.
|
225
|
+
if @model.respond_to?(name)
|
226
|
+
@dirty = true if name.to_s.end_with?('=') # Remove to_s when Ruby >2.6.
|
227
|
+
@model.send(name, *args)
|
228
|
+
else
|
229
|
+
super
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def respond_to_missing?(name, *all) # :nodoc:
|
234
|
+
@model.respond_to?(name, *all)
|
64
235
|
end
|
65
236
|
|
237
|
+
def self.format_comment(comment) # :nodoc:
|
238
|
+
return '' if comment.empty?
|
239
|
+
|
240
|
+
comment = comment.scan(/.{1,75}/) if comment.is_a?(String)
|
241
|
+
c = comment.map do |l|
|
242
|
+
l.empty? ? '#' : "# #{l}"
|
243
|
+
end.join("\n")
|
244
|
+
|
245
|
+
"#{c}\n\n"
|
246
|
+
end
|
247
|
+
|
248
|
+
def self.parse_comment(content) # :nodoc:
|
249
|
+
content = content.split("\n")
|
250
|
+
|
251
|
+
content.reduce([]) do |acc, line|
|
252
|
+
break acc unless line.start_with?('#')
|
253
|
+
|
254
|
+
acc << line.sub(/^#+/, '').strip
|
255
|
+
end
|
256
|
+
end
|
66
257
|
end
|
67
258
|
end
|