active-triples 1.1.0 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 550bad738fa73662ac83c8e3e352f7067f0339d2
4
- data.tar.gz: 0d0fd4a1dfe4e2ef3b86ee54d8cb7b353a9d904e
2
+ SHA256:
3
+ metadata.gz: 6c0e9d67f468229ed99433d744dde2215e21e5571e19b081bcb0a8edd5f42380
4
+ data.tar.gz: 4a33d1eeedc668e14402e1dbe5507b914427b3eef9b651d72226e53652465a9f
5
5
  SHA512:
6
- metadata.gz: 8c3d54cc99e550e9ad43b87399c6193f430ab9330c790eb36bd246c5dd63c1d16fc1ce23f71db2d3b41dea494106e59e9ba7b74d827e6d05ded76b9d0a62129d
7
- data.tar.gz: 0fbddfdec5678fc535816dcb47be0fb1a40bec5ef8e1e207311c9fc72a674c2e257bc0288b9e1ba53b84f570b1c6fac9a5cfd95d394c02b8db4ba1735fa4c49c
6
+ metadata.gz: c54cf20963fc02882da7848b480653392fe5051ada1311bf04572556507978c7eda8ce4b2102a035faf315b399d3708ba6cfcd02eafedcccccf470aed2c65ef3
7
+ data.tar.gz: 278a128edabab0162e5dea4df901b7ecfd83bbf3c62820f8b65d447304edcc662ab93115cb1f2b9527fa64377708e00c6c3f0e8accc7dd79a361e8a937136db0
data/.gitlab-ci.yml ADDED
@@ -0,0 +1,67 @@
1
+ .rspec:
2
+ script:
3
+ - bundle exec rspec
4
+
5
+ .jruby:
6
+ extends: .rspec
7
+ before_script:
8
+ - apk add git
9
+ - ruby -v
10
+ - bundle install --jobs $(nproc) "${FLAGS[@]}"
11
+
12
+ before_script:
13
+ - ruby -v
14
+ - bundle install --jobs $(nproc) "${FLAGS[@]}"
15
+
16
+ stages:
17
+ - test
18
+
19
+ ruby-2-4:
20
+ image: ruby:2.4
21
+ extends: .rspec
22
+ stage: test
23
+
24
+ ruby-2-5:
25
+ image: ruby:2.4
26
+ extends: .rspec
27
+ stage: test
28
+
29
+ ruby-2-6:
30
+ image: ruby:2.5
31
+ extends: .rspec
32
+ stage: test
33
+
34
+ ruby-2-7:
35
+ image: ruby:2.6
36
+ extends: .rspec
37
+ stage: test
38
+
39
+ ruby-3-0:
40
+ image: ruby:3.0
41
+ extends: .rspec
42
+ stage: test
43
+ before_script:
44
+ - ruby -v
45
+ - gem install bundler
46
+ - gem update --system
47
+ - bundle install --jobs $(nproc) "${FLAGS[@]}"
48
+
49
+ ruby-3-1:
50
+ image: ruby:3.1
51
+ extends: .rspec
52
+ stage: test
53
+
54
+ ruby-3-2:
55
+ image: ruby:3.2
56
+ extends: .rspec
57
+ stage: test
58
+
59
+ ruby-latest:
60
+ image: ruby:latest
61
+ extends: .rspec
62
+ stage: test
63
+
64
+ jruby-9:
65
+ image: jruby:9-alpine
66
+ extends: .jruby
67
+ stage: test
data/CHANGES.md CHANGED
@@ -1,3 +1,14 @@
1
+ 1.2.0
2
+ ----
3
+ - Add support for Ruby 3.0, 3.1 and 3.2.
4
+ - Implemented `RDFSource#term?` for compatibility with current
5
+ `RDF::Enumerable` behavior.
6
+
7
+ 1.1.1
8
+ ----
9
+ - Major performance improvements due to a minor change in how
10
+ properties are resolved.
11
+
1
12
  1.1.0
2
13
  ----
3
14
  - Add support for RDF.rb 3.0
data/Gemfile CHANGED
@@ -2,5 +2,11 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'activesupport', '< 5.0.0' if RUBY_VERSION =~ /2\.1\..*/
6
5
  gem 'pry-byebug' unless ENV["CI"]
6
+
7
+ group :test do
8
+ gem 'simplecov', require: false
9
+ end
10
+
11
+ Encoding.default_external = Encoding::UTF_8
12
+ Encoding.default_internal = Encoding::UTF_8
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  Description
2
2
  -----------
3
3
 
4
- [![Build Status](https://travis-ci.org/ActiveTriples/ActiveTriples.png?branch=develop)](https://travis-ci.org/ActiveTriples/ActiveTriples)
5
- [![Coverage Status](https://coveralls.io/repos/ActiveTriples/ActiveTriples/badge.svg?branch=develop)](https://coveralls.io/r/ActiveTriples/ActiveTriples?branch=develop)
4
+ [![Pipeline Status](https://gitlab.com/no_reply/ActiveTriples/badges/develop/pipeline.svg)](https://gitlab.com/no_reply/ActiveTriples/commits/develop)
5
+ [![Coverage Report](https://gitlab.com/no_reply/ActiveTriples/badges/develop/coverage.svg)](https://gitlab.com/no_reply/ActiveTriples/commits/develop)
6
6
  [![Gem Version](https://badge.fury.io/rb/active-triples.svg)](http://badge.fury.io/rb/active-triples)
7
7
 
8
8
  An ActiveModel-like interface for RDF data. Models graphs as RDFSources with property/attribute configuration, accessors, and other methods to support Linked Data in a Ruby/Rails enviornment. See [RDF Concepts and Abstract Syntax](http://www.w3.org/TR/2014/REC-rdf11-concepts-20140225/#change-over-time) for an informal definition of an RDF Source.
@@ -27,9 +27,9 @@ require 'rdf/vocab'
27
27
 
28
28
  class Thing
29
29
  include ActiveTriples::RDFSource
30
-
30
+
31
31
  configure type: RDF::OWL.Thing, base_uri: 'http://example.org/things#'
32
-
32
+
33
33
  property :title, predicate: RDF::Vocab::DC.title
34
34
  property :description, predicate: RDF::Vocab::DC.description
35
35
  end
@@ -48,10 +48,10 @@ Thing.property :creator, predicate: RDF::Vocab::DC.creator, class_name: 'Person'
48
48
 
49
49
  class Person
50
50
  include ActiveTriples::RDFSource
51
-
51
+
52
52
  configure type: RDF::Vocab::FOAF.Person,
53
53
  base_uri: 'http://example.org/people#'
54
-
54
+
55
55
  property :name, predicate: RDF::Vocab::FOAF.name
56
56
  end
57
57
 
@@ -171,7 +171,7 @@ ActiveTriples::Repositories.add_repository :people, RDF::Repository.new
171
171
 
172
172
  class Person
173
173
  include ActiveTriples::RDFSource
174
-
174
+
175
175
  configure type: RDF::Vocab::FOAF.Person,
176
176
  base_uri: 'http://example.org/people#',
177
177
  repository: :people
@@ -184,7 +184,7 @@ class Thing
184
184
  configure type: RDF::OWL.Thing,
185
185
  base_uri: 'http://example.org/things#',
186
186
  repository: :default
187
-
187
+
188
188
  property :title, predicate: RDF::Vocab::DC.title
189
189
  property :description, predicate: RDF::Vocab::DC.description
190
190
  property :creator, predicate: RDF::Vocab::DC.creator, class_name: 'Person'
@@ -6,9 +6,9 @@ Gem::Specification.new do |s|
6
6
  s.name = "active-triples"
7
7
  s.version = ActiveTriples::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
- s.authors = ["Tom Johnson", "Trey Terrell"]
10
- s.homepage = 'https://github.com/ActiveTriples/ActiveTriples'
11
- s.email = 'tom@curationexperts.com'
9
+ s.authors = ["Tamsin Johnson", "Trey Pendragon"]
10
+ s.homepage = 'https://gitlab.com/no_reply/ActiveTriples'
11
+ s.email = 'tomjohnson@ucsb.edu'
12
12
  s.summary = %q{RDF graphs in ActiveModel wrappers.}
13
13
  s.description = %q{ActiveTriples provides tools for modeling RDF as discrete resources.}
14
14
  s.license = 'Apache-2.0'
@@ -11,10 +11,11 @@ module ActiveTriples
11
11
 
12
12
  ##
13
13
  # @param item_factory [ItemFactory]
14
- # @param [Hash] options the configuration options.
15
- def initialize(item_factory: ItemFactory.new, **options)
14
+ # @param [Hash] options the configuration options. (Ruby 3+)
15
+ # @param [Hash] options2 the configuration options. (Ruby 2.x)
16
+ def initialize(options = {}, item_factory: ItemFactory.new, **options2)
16
17
  @item_factory = item_factory
17
- @inner_hash = Hash[options.to_a]
18
+ @inner_hash = Hash[options.to_a + options2.to_a]
18
19
  end
19
20
 
20
21
  ##
@@ -26,7 +27,7 @@ module ActiveTriples
26
27
  # result of merging.
27
28
  def merge(options)
28
29
  options = options.to_h
29
- new_config = self.class.new(**options)
30
+ new_config = self.class.new(options)
30
31
 
31
32
  new_config.items.each do |property, item|
32
33
  build_configuration_item(property).set item.value
@@ -52,7 +52,7 @@ module ActiveTriples
52
52
  #
53
53
  # @return [Boolean]
54
54
  def reload
55
- source << repository.query(subject: source)
55
+ source << repository.query([source, nil, nil])
56
56
  @persisted = true unless source.empty?
57
57
  true
58
58
  end
@@ -66,7 +66,7 @@ module ActiveTriples
66
66
  registered_preds = registered_predicates << RDF.type
67
67
  unregistered_preds = []
68
68
 
69
- query(subject: rdf_subject) do |stmt|
69
+ query([rdf_subject, nil, nil]) do |stmt|
70
70
  unregistered_preds << stmt.predicate unless
71
71
  registered_preds.include? stmt.predicate
72
72
  end
@@ -123,26 +123,26 @@ module ActiveTriples
123
123
  # @param [Hash] opts for this property, must include a :predicate
124
124
  # @yield [index] index sets solr behaviors for the property
125
125
  #
126
- # @return [Hash{String=>ActiveTriples::NodeConfig}] the full current
126
+ # @return [Hash{String=>ActiveTriples::NodeConfig}] the full current
127
127
  # property configuration for the class
128
128
  def property(name, opts={}, &block)
129
- raise ArgumentError, "#{name} is a keyword and not an acceptable property name." if protected_property_name? name
129
+ raise ArgumentError, "#{name} is a keyword and not an acceptable property name." if protected_property_name?(name.to_sym)
130
130
  reflection = PropertyBuilder.build(self, name, opts, &block)
131
131
  Reflection.add_reflection self, name, reflection
132
132
  end
133
133
 
134
134
  ##
135
- # Checks potential property names for conflicts with existing class
136
- # instance methods. We avoid setting properties with these names to
135
+ # Checks potential property names for conflicts with existing class
136
+ # instance methods. We avoid setting properties with these names to
137
137
  # prevent catastrophic method overwriting.
138
138
  #
139
139
  # @param [Symblol] name A potential property name.
140
- # @return [Boolean] true if the given name matches an existing instance
140
+ # @return [Boolean] true if the given name matches an existing instance
141
141
  # method which is not an ActiveTriples property.
142
142
  def protected_property_name?(name)
143
- reject = self.instance_methods.map! { |s| s.to_s.gsub(/=$/, '').to_sym }
144
- reject -= properties.keys.map { |k| k.to_sym }
145
- reject.include? name
143
+ return false if fields.include?(name)
144
+ return true if instance_methods.include?(name) || instance_methods.include?("#{name}=".to_sym)
145
+ false
146
146
  end
147
147
 
148
148
  ##
@@ -92,11 +92,9 @@ module ActiveTriples
92
92
  ##
93
93
  # @!method to_base
94
94
  # @return (see RDF::Term#to_base)
95
- # @!method term?
96
- # @return (see RDF::Term#term?)
97
95
  # @!method escape
98
96
  # @return (see RDF::Term#escape)
99
- delegate :to_base, :term?, :escape, to: :to_term
97
+ delegate :to_base, :escape, to: :to_term
100
98
 
101
99
  ##
102
100
  # Initialize an instance of this resource class. Defaults to a
@@ -110,7 +108,7 @@ module ActiveTriples
110
108
  # @see RDF::Graph
111
109
  # @todo move this logic out to a Builder?
112
110
  def initialize(*args, &block)
113
- @observers = Set.new
111
+ @observers = Set.new
114
112
 
115
113
  resource_uri = args.shift unless args.first.is_a?(Hash)
116
114
  @rdf_subject = get_uri(resource_uri) if resource_uri
@@ -122,7 +120,12 @@ module ActiveTriples
122
120
  persistence_strategy.parent = args.shift
123
121
  end
124
122
 
125
- persistence_strategy.graph = RDF::Graph.new(*args, &block)
123
+ graph_params = if args.empty? || args.first.nil?
124
+ {}
125
+ else
126
+ args.shift
127
+ end
128
+ persistence_strategy.graph = RDF::Graph.new(**graph_params, &block)
126
129
  reload
127
130
 
128
131
  # Append type to graph if necessary.
@@ -398,9 +401,9 @@ module ActiveTriples
398
401
  # @yieldparam [ActiveTriples::RDFSource] resource self
399
402
  #
400
403
  # @return [ActiveTriples::RDFSource] self
401
- def fetch(*args, &_block)
404
+ def fetch(**args, &_block)
402
405
  begin
403
- load(rdf_subject, *args)
406
+ load(rdf_subject, **args)
404
407
  rescue => e
405
408
  if block_given?
406
409
  yield(self)
@@ -575,6 +578,26 @@ module ActiveTriples
575
578
  !persisted?
576
579
  end
577
580
 
581
+ ##
582
+ # @overload term?
583
+ # Returns `false` indicating this is not an RDF::Statemenet.
584
+ # @see RDF::Value#statement?
585
+ # @return [Boolean]
586
+ # @overload term?(value)
587
+ # Returns `true` if `self` contains the given RDF subject term.
588
+ #
589
+ # @param [RDF::Resource] value
590
+ # @return [Boolean]
591
+ #
592
+ # See RDF::Enumerable#term?
593
+ def term?(*args)
594
+ case args.length
595
+ when 0 then to_term.term?
596
+ when 1 then args.first && graph.term?(args.first)
597
+ else raise ArgumentError("wrong number of arguments (given #{args.length}, expected 0 or 1)")
598
+ end
599
+ end
600
+
578
601
  def mark_for_destruction
579
602
  @marked_for_destruction = true
580
603
  end
@@ -641,7 +664,7 @@ module ActiveTriples
641
664
  # @param [RDF::Term] new_subject
642
665
  # @return [void]
643
666
  def rewrite_statement_uris(old_subject, new_subject)
644
- graph.query(subject: old_subject).each do |st|
667
+ graph.query([old_subject, nil, nil]).each do |st|
645
668
  graph.delete(st)
646
669
 
647
670
  st.subject = new_subject
@@ -649,7 +672,7 @@ module ActiveTriples
649
672
  graph.insert(st)
650
673
  end
651
674
 
652
- graph.query(object: old_subject).each do |st|
675
+ graph.query([nil, nil, old_subject]).each do |st|
653
676
  graph.delete(st)
654
677
 
655
678
  st.object = new_subject
@@ -481,7 +481,7 @@ module ActiveTriples
481
481
  ##
482
482
  # @private
483
483
  def objects(&block)
484
- solutions = parent.query(subject: rdf_subject, predicate: predicate)
484
+ solutions = parent.query([rdf_subject, predicate, nil])
485
485
  solutions.extend(RDF::Enumerable) unless solutions.respond_to?(:each_object)
486
486
 
487
487
  solutions.each_object(&block)
@@ -55,7 +55,7 @@ module ActiveTriples
55
55
  ancestors = @ancestors.dup
56
56
 
57
57
  if block_given?
58
- statements = source_graph.query(subject: starting_node).each
58
+ statements = source_graph.query([starting_node, nil, nil]).each
59
59
  statements.each_statement { |st| yield st }
60
60
 
61
61
  ancestors << starting_node
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module ActiveTriples
3
- VERSION = '1.1.0'.freeze
3
+ VERSION = '1.2.0'.freeze
4
4
  end
@@ -743,7 +743,7 @@ describe ActiveTriples::RDFSource do
743
743
 
744
744
  it 'has errors' do
745
745
  expect { subject.valid? }
746
- .to change { subject.errors.messages }
746
+ .to change { subject.errors.messages.transform_values { |v| v.map(&:to_s) } }
747
747
  .from({})
748
748
  .to(include(base: ['The underlying graph must be valid']))
749
749
  end
@@ -470,7 +470,7 @@ describe ActiveTriples::Resource do
470
470
  describe '#set_value' do
471
471
  it 'should set a value in the graph' do
472
472
  subject.set_value(RDF::Vocab::DC.title, 'Comet in Moominland')
473
- subject.query(:subject => subject.rdf_subject, :predicate => RDF::Vocab::DC.title).each_statement do |s|
473
+ subject.query([subject.rdf_subject, nil, RDF::Vocab::DC.title]).each_statement do |s|
474
474
  expect(s.object.to_s).to eq 'Comet in Moominland'
475
475
  end
476
476
  end
@@ -522,14 +522,14 @@ describe ActiveTriples::Resource do
522
522
 
523
523
  it "should be able to accept a subject" do
524
524
  expect{subject.set_value(RDF::URI("http://opaquenamespace.org/jokes"), RDF::Vocab::DC.title, 'Comet in Moominland')}.not_to raise_error
525
- expect(subject.query(:subject => RDF::URI("http://opaquenamespace.org/jokes"), :predicate => RDF::Vocab::DC.title).statements.to_a.length).to eq 1
525
+ expect(subject.query([RDF::URI("http://opaquenamespace.org/jokes"), RDF::Vocab::DC.title, nil]).statements.to_a.length).to eq 1
526
526
  end
527
527
  end
528
528
 
529
529
  describe '#[]=' do
530
530
  it 'should set a value in the graph' do
531
531
  subject[RDF::Vocab::DC.title] = 'Comet in Moominland'
532
- subject.query(:subject => subject.rdf_subject, :predicate => RDF::Vocab::DC.title).each_statement do |s|
532
+ subject.query([subject.rdf_subject, nil, RDF::Vocab::DC.title]).each_statement do |s|
533
533
  expect(s.object.to_s).to eq 'Comet in Moominland'
534
534
  end
535
535
  end
@@ -616,7 +616,7 @@ describe ActiveTriples::Resource do
616
616
  end
617
617
 
618
618
  it 'should be the type in the graph' do
619
- subject.query(:subject => subject.rdf_subject, :predicate => RDF.type).statements do |s|
619
+ subject.query([subject.rdf_subject, nil, RDF.type]).statements do |s|
620
620
  expect(s.object).to eq RDF::URI('http://example.org/AnotherClass')
621
621
  end
622
622
  end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require 'coveralls'
3
- Coveralls.wear!
2
+ require 'simplecov'
3
+ SimpleCov.start
4
4
 
5
5
  require 'bundler/setup'
6
6
  Bundler.setup
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active-triples
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
- - Tom Johnson
8
- - Trey Terrell
9
- autorequire:
7
+ - Tamsin Johnson
8
+ - Trey Pendragon
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-06-28 00:00:00.000000000 Z
12
+ date: 2023-01-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rdf
@@ -252,7 +252,7 @@ dependencies:
252
252
  - !ruby/object:Gem::Version
253
253
  version: 0.1.2
254
254
  description: ActiveTriples provides tools for modeling RDF as discrete resources.
255
- email: tom@curationexperts.com
255
+ email: tomjohnson@ucsb.edu
256
256
  executables: []
257
257
  extensions: []
258
258
  extra_rdoc_files:
@@ -261,6 +261,7 @@ extra_rdoc_files:
261
261
  files:
262
262
  - ".coveralls.yml"
263
263
  - ".gitignore"
264
+ - ".gitlab-ci.yml"
264
265
  - ".travis.yml"
265
266
  - AUTHORS
266
267
  - CHANGES.md
@@ -328,17 +329,16 @@ files:
328
329
  - spec/integration/dummies/dummy_resource_b.rb
329
330
  - spec/integration/parent_persistence_spec.rb
330
331
  - spec/integration/reciprocal_properties_spec.rb
331
- - spec/pragmatic_context_spec.rb
332
332
  - spec/spec_helper.rb
333
333
  - spec/support/active_model_lint.rb
334
334
  - spec/support/dummies/basic_persistable.rb
335
335
  - spec/support/matchers.rb
336
336
  - spec/support/shared_examples/persistence_strategy.rb
337
- homepage: https://github.com/ActiveTriples/ActiveTriples
337
+ homepage: https://gitlab.com/no_reply/ActiveTriples
338
338
  licenses:
339
339
  - Apache-2.0
340
340
  metadata: {}
341
- post_install_message:
341
+ post_install_message:
342
342
  rdoc_options: []
343
343
  require_paths:
344
344
  - lib
@@ -353,9 +353,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
353
353
  - !ruby/object:Gem::Version
354
354
  version: '0'
355
355
  requirements: []
356
- rubyforge_project:
357
- rubygems_version: 2.6.13
358
- signing_key:
356
+ rubygems_version: 3.1.6
357
+ signing_key:
359
358
  specification_version: 4
360
359
  summary: RDF graphs in ActiveModel wrappers.
361
360
  test_files: []
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'spec_helper'
3
- require 'pragmatic_context'
4
-
5
- describe 'PragmaticContext integration' do
6
- before do
7
- class DummyLicense
8
- include ActiveTriples::RDFSource
9
- include PragmaticContext::Contextualizable
10
- property :title, :predicate => RDF::Vocab::DC.title
11
-
12
- contextualize :title, :as => RDF::Vocab::DC.title.to_s
13
- end
14
-
15
- class DummyResource
16
- include ActiveTriples::RDFSource
17
- include PragmaticContext::Contextualizable
18
-
19
- configure :type => RDF::URI('http://example.org/SomeClass')
20
- property :license, :predicate => RDF::Vocab::DC.license,
21
- :class_name => DummyLicense
22
- property :title, :predicate => RDF::Vocab::DC.title
23
-
24
- contextualize :title, :as => RDF::Vocab::DC.title.to_s
25
- contextualize :license, :as => RDF::Vocab::DC.license.to_s
26
- end
27
-
28
- license.title = 'cc'
29
- subject.title = 'my resource'
30
- subject.license = license
31
- subject.license << RDF::Literal('Creative Commons')
32
- end
33
-
34
- after do
35
- Object.send(:remove_const, "DummyResource")
36
- Object.send(:remove_const, "DummyLicense")
37
- end
38
-
39
- subject { DummyResource.new('http://example.org/test') }
40
- let(:license) { DummyLicense.new }
41
-
42
- xit 'should output a valid jsonld representation of itself' do
43
- g = RDF::Graph.new << JSON::LD::API.toRdf(subject.as_jsonld)
44
- expect(subject == g).to be true
45
- end
46
-
47
- it 'should have contexts' do
48
- expect(subject.as_jsonld['@context'].keys).to contain_exactly('license', 'title')
49
- end
50
-
51
- it 'should use context with dump' do
52
- context = JSON.parse(subject.dump :jsonld)['@context']
53
- subject.class.properties.keys.each do |prop|
54
- expect(context).to include prop
55
- end
56
- end
57
- end