repository-base 0.4.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 +7 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.rubocop.yml +27 -0
- data/.travis.yml +6 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +128 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +132 -0
- data/Rakefile +33 -0
- data/bin/bundle +105 -0
- data/bin/htmldiff +29 -0
- data/bin/kramdown +29 -0
- data/bin/ldiff +29 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/bin/rubocop +29 -0
- data/bin/ruby-parse +29 -0
- data/bin/ruby-rewrite +29 -0
- data/bin/setup +43 -0
- data/bin/yard +29 -0
- data/bin/yardoc +29 -0
- data/bin/yri +29 -0
- data/doc/Repository.html +128 -0
- data/doc/Repository/Base.html +1248 -0
- data/doc/Repository/Base/Internals.html +133 -0
- data/doc/Repository/Base/Internals/RecordDeleter.html +687 -0
- data/doc/Repository/Base/Internals/RecordSaver.html +816 -0
- data/doc/Repository/Base/Internals/RecordUpdater.html +1026 -0
- data/doc/Repository/Base/Internals/SlugFinder.html +986 -0
- data/doc/_index.html +176 -0
- data/doc/class_list.html +51 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +58 -0
- data/doc/css/style.css +499 -0
- data/doc/file.CHANGELOG.html +240 -0
- data/doc/file.README.html +218 -0
- data/doc/file_list.html +61 -0
- data/doc/frames.html +17 -0
- data/doc/index.html +218 -0
- data/doc/js/app.js +248 -0
- data/doc/js/full_list.js +216 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +363 -0
- data/doc/top-level-namespace.html +110 -0
- data/lib/repository/base.rb +115 -0
- data/lib/repository/base/internals/internals.rb +6 -0
- data/lib/repository/base/internals/record_deleter.rb +46 -0
- data/lib/repository/base/internals/record_saver.rb +58 -0
- data/lib/repository/base/internals/record_updater.rb +54 -0
- data/lib/repository/base/internals/slug_finder.rb +70 -0
- data/lib/repository/base/version.rb +12 -0
- data/repository-base.gemspec +37 -0
- data/spec/repository/base_spec.rb +398 -0
- data/spec/spec_helper.rb +14 -0
- metadata +281 -0
@@ -0,0 +1,110 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>
|
7
|
+
Top Level Namespace
|
8
|
+
|
9
|
+
— Documentation by YARD 0.9.12
|
10
|
+
|
11
|
+
</title>
|
12
|
+
|
13
|
+
<link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
|
14
|
+
|
15
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
|
16
|
+
|
17
|
+
<script type="text/javascript" charset="utf-8">
|
18
|
+
pathId = "";
|
19
|
+
relpath = '';
|
20
|
+
</script>
|
21
|
+
|
22
|
+
|
23
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
24
|
+
|
25
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
26
|
+
|
27
|
+
|
28
|
+
</head>
|
29
|
+
<body>
|
30
|
+
<div class="nav_wrap">
|
31
|
+
<iframe id="nav" src="class_list.html?1"></iframe>
|
32
|
+
<div id="resizer"></div>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<div id="main" tabindex="-1">
|
36
|
+
<div id="header">
|
37
|
+
<div id="menu">
|
38
|
+
|
39
|
+
<a href="_index.html">Index</a> »
|
40
|
+
|
41
|
+
|
42
|
+
<span class="title">Top Level Namespace</span>
|
43
|
+
|
44
|
+
</div>
|
45
|
+
|
46
|
+
<div id="search">
|
47
|
+
|
48
|
+
<a class="full_list_link" id="class_list_link"
|
49
|
+
href="class_list.html">
|
50
|
+
|
51
|
+
<svg width="24" height="24">
|
52
|
+
<rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
|
53
|
+
<rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
|
54
|
+
<rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
|
55
|
+
</svg>
|
56
|
+
</a>
|
57
|
+
|
58
|
+
</div>
|
59
|
+
<div class="clear"></div>
|
60
|
+
</div>
|
61
|
+
|
62
|
+
<div id="content"><h1>Top Level Namespace
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
</h1>
|
67
|
+
<div class="box_info">
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
</div>
|
80
|
+
|
81
|
+
<h2>Defined Under Namespace</h2>
|
82
|
+
<p class="children">
|
83
|
+
|
84
|
+
|
85
|
+
<strong class="modules">Modules:</strong> <span class='object_link'><a href="Repository.html" title="Repository (module)">Repository</a></span>
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
|
90
|
+
</p>
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
</div>
|
101
|
+
|
102
|
+
<div id="footer">
|
103
|
+
Generated on Sat Feb 3 03:00:17 2018 by
|
104
|
+
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
105
|
+
0.9.12 (ruby-2.5.0).
|
106
|
+
</div>
|
107
|
+
|
108
|
+
</div>
|
109
|
+
</body>
|
110
|
+
</html>
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'repository/base/version'
|
4
|
+
|
5
|
+
require 'repository/base/internals/internals'
|
6
|
+
require 'repository/support/store_result'
|
7
|
+
|
8
|
+
# The `Repository` module functions as a namespace for implementing the
|
9
|
+
# repository logic in a [Data Mapper pattern](http://martinfowler.com/eaaCatalog/dataMapper.html)
|
10
|
+
# implementation.
|
11
|
+
# The Repository module initially included two names:
|
12
|
+
#
|
13
|
+
# 1. `Base`; and
|
14
|
+
# 1. `Support` (a namespace containing several related classes).
|
15
|
+
#
|
16
|
+
module Repository
|
17
|
+
# Base class for Repository in Data Mapper pattern.
|
18
|
+
class Base
|
19
|
+
# Internal support code exclusively used by Repository::Base
|
20
|
+
# @since 0.0.1
|
21
|
+
module Internals
|
22
|
+
end
|
23
|
+
private_constant :Internals
|
24
|
+
include Internals
|
25
|
+
|
26
|
+
attr_reader :dao, :factory
|
27
|
+
|
28
|
+
# Initialise a new `Repository::Base` instance.
|
29
|
+
# @param factory Has a .create method to create entities from DAO records.
|
30
|
+
# @param dao Data Access Object implements persistence without business
|
31
|
+
# logic.
|
32
|
+
def initialize(factory:, dao:)
|
33
|
+
validate_initializer_argument(:dao, dao)
|
34
|
+
validate_initializer_argument(:factory, factory)
|
35
|
+
@factory = factory
|
36
|
+
@dao = dao
|
37
|
+
end
|
38
|
+
|
39
|
+
# Add a new record with attributes matching the specified entity to the
|
40
|
+
# associated DAO.
|
41
|
+
# @param entity Entity specifying record to be persisted to new DAO record.
|
42
|
+
# @return [Repository::Support::StoreResult] An object containing
|
43
|
+
# information about the success or failure of an action.
|
44
|
+
def add(entity)
|
45
|
+
record = dao.new filtered_attributes_for(entity)
|
46
|
+
RecordSaver.new(record: record, factory: factory).result
|
47
|
+
end
|
48
|
+
|
49
|
+
# Return an array of entities matching all records currently in the
|
50
|
+
# associated DAO.
|
51
|
+
# @return [Array] Array of entities as supplied by the `factory`.
|
52
|
+
# @since 0.0.2
|
53
|
+
def all
|
54
|
+
dao.all.map { |record| factory.create record }
|
55
|
+
end
|
56
|
+
|
57
|
+
# Remove a record from the underlying DAO whose slug matches the passed-in
|
58
|
+
# identifier.
|
59
|
+
# @param identifier [String] [Slug](http://en.wikipedia.org/wiki/Semantic_URL#Slug)
|
60
|
+
# for record to be deleted.
|
61
|
+
# @return [Repository::Support::StoreResult] An object containing
|
62
|
+
# information about the success or failure of an action.
|
63
|
+
# @since 0.0.5
|
64
|
+
def delete(identifier)
|
65
|
+
RecordDeleter.new(identifier: identifier, dao: dao, factory: factory)
|
66
|
+
.delete
|
67
|
+
end
|
68
|
+
|
69
|
+
# Find a record in the DAO and, on success, return a corresponding entity
|
70
|
+
# using the specified [slug](http://en.wikipedia.org/wiki/Semantic_URL#Slug),
|
71
|
+
# *not* a numeric record ID, as a search identifier.
|
72
|
+
# @param slug [String] [Slug](http://en.wikipedia.org/wiki/Semantic_URL#Slug)
|
73
|
+
# for record to be deleted.
|
74
|
+
# @return [Repository::Support::StoreResult] An object containing
|
75
|
+
# information about the success or failure of an action.
|
76
|
+
# @since 0.0.3
|
77
|
+
def find_by_slug(slug)
|
78
|
+
SlugFinder.new(slug: slug, dao: dao, factory: factory).find
|
79
|
+
end
|
80
|
+
|
81
|
+
# Update a record in the DAO corresponding to the specified identifier,
|
82
|
+
# using the specified attribute-name/value pairs.
|
83
|
+
# @param identifier [String] [Slug](http://en.wikipedia.org/wiki/Semantic_URL#Slug)
|
84
|
+
# for record to be deleted.
|
85
|
+
# @param updated_attrs [Hash] Attributes to be updated.
|
86
|
+
# @since 0.0.4
|
87
|
+
# @example
|
88
|
+
# result = user_repo.update @user.slug, params[:user_params]
|
89
|
+
# @user = result.entity if result.success?
|
90
|
+
def update(identifier:, updated_attrs:)
|
91
|
+
RecordUpdater.new(identifier: identifier, updated_attrs: updated_attrs,
|
92
|
+
dao: dao, factory: factory).update
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
# supporting #initialize
|
98
|
+
|
99
|
+
# Verifies that parameter passed to #initialize is a Class.
|
100
|
+
# @param arg_sym [Symbol] Which parameter is being validated, either `:dao`
|
101
|
+
# or `:factory`.
|
102
|
+
# @param value Parameter value being validated. Must be a Class.
|
103
|
+
# @return [boolean]
|
104
|
+
def validate_initializer_argument(arg_sym, value)
|
105
|
+
message = "the :#{arg_sym} argument must be a Class"
|
106
|
+
raise ArgumentError, message unless value.respond_to? :new
|
107
|
+
end
|
108
|
+
|
109
|
+
# supporting #add
|
110
|
+
|
111
|
+
def filtered_attributes_for(entity) # :nodoc:
|
112
|
+
entity.attributes.to_hash.reject { |k, _v| k == :errors }
|
113
|
+
end
|
114
|
+
end # class Repository::Base
|
115
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'repository/support/store_result'
|
4
|
+
require_relative 'slug_finder'
|
5
|
+
|
6
|
+
module Repository
|
7
|
+
# Base class for Repository in Data Mapper pattern.
|
8
|
+
class Base
|
9
|
+
# Internal support code exclusively used by Repository::Base
|
10
|
+
module Internals
|
11
|
+
# Stows away details of reporting update success/failure.
|
12
|
+
# @since 0.0.5
|
13
|
+
class RecordDeleter
|
14
|
+
include Repository::Support
|
15
|
+
|
16
|
+
# Initializes a new instance of `SlugFinder`.
|
17
|
+
# @param identifier [String] [Slug](http://en.wikipedia.org/wiki/Semantic_URL#Slug)
|
18
|
+
# for record to be deleted.
|
19
|
+
# @param dao Data Access Object implements persistence without business
|
20
|
+
# logic.
|
21
|
+
# @param factory Factory-pattern class to build an entity from an
|
22
|
+
# existing DAO record.
|
23
|
+
def initialize(identifier:, dao:, factory:)
|
24
|
+
@identifier = identifier
|
25
|
+
@dao = dao
|
26
|
+
@factory = factory
|
27
|
+
end
|
28
|
+
|
29
|
+
# Command-pattern method returning indication of success or failure of
|
30
|
+
# attempt to delete identified record.
|
31
|
+
# @return Repository::Support::StoreResult
|
32
|
+
def delete
|
33
|
+
finder = SlugFinder.new slug: identifier, dao: dao, factory: factory
|
34
|
+
result = finder.find
|
35
|
+
return result unless result.success
|
36
|
+
dao.delete identifier
|
37
|
+
result
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
attr_reader :dao, :factory, :identifier
|
43
|
+
end # class Repository::Base::Internals::RecordDeleter
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'repository/support/error_factory'
|
4
|
+
require 'repository/support/store_result'
|
5
|
+
|
6
|
+
module Repository
|
7
|
+
# Base class for Repository in Data Mapper pattern.
|
8
|
+
class Base
|
9
|
+
# Internal support code exclusively used by Repository::Base
|
10
|
+
module Internals
|
11
|
+
# Reports on the success or failure of saving a *DAO record*, using the
|
12
|
+
# `Repository::Support::StoreResult` instance returned from `#result`.
|
13
|
+
# @since 0.0.1
|
14
|
+
class RecordSaver
|
15
|
+
include Repository::Support
|
16
|
+
|
17
|
+
# Sets instance variable(s) on a new `RecordSaver` instanace.
|
18
|
+
# @param record DAO record to attempt to save.
|
19
|
+
def initialize(record:, factory:)
|
20
|
+
@record = record
|
21
|
+
@factory = factory
|
22
|
+
end
|
23
|
+
|
24
|
+
# Command-pattern method returning indication of success or failure of
|
25
|
+
# attempt to save record.
|
26
|
+
# @return Repository::Support::StoreResult
|
27
|
+
# @see #failed_result
|
28
|
+
# @see #successful_result
|
29
|
+
def result
|
30
|
+
if record.save
|
31
|
+
successful_result
|
32
|
+
else
|
33
|
+
failed_result
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
attr_reader :factory, :record
|
40
|
+
|
41
|
+
# Represent error data sourced from an `ActiveModel::Errors` object as
|
42
|
+
# an Array of `{field: 'field', message: 'message'}` hashes.
|
43
|
+
def error_hashes
|
44
|
+
ErrorFactory.create record.errors
|
45
|
+
end
|
46
|
+
|
47
|
+
def failed_result
|
48
|
+
StoreResult::Failure.new error_hashes
|
49
|
+
end
|
50
|
+
|
51
|
+
def successful_result
|
52
|
+
entity = factory.create record
|
53
|
+
StoreResult::Success.new entity
|
54
|
+
end
|
55
|
+
end # class Internals::RecordSaver
|
56
|
+
end # module Repository::Base::Internals
|
57
|
+
end # class Repository::Base
|
58
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'repository/support/store_result'
|
4
|
+
|
5
|
+
module Repository
|
6
|
+
# Base class for Repository in Data Mapper pattern.
|
7
|
+
class Base
|
8
|
+
# Internal support code exclusively used by Repository::Base
|
9
|
+
module Internals
|
10
|
+
# Stows away details of reporting update success/failure.
|
11
|
+
# @since 0.0.4
|
12
|
+
class RecordUpdater
|
13
|
+
include Repository::Support
|
14
|
+
|
15
|
+
# Initializes a new instance of `RecordUpdater`.
|
16
|
+
# @param identifier [String] Slug uniquely identifying the record in the
|
17
|
+
# DAO to update
|
18
|
+
# @param updated_attrs [Hash] Attributes to be updated.
|
19
|
+
# @param dao Data Access Object implements persistence without business
|
20
|
+
# logic.
|
21
|
+
# @param factory Class whose `#create` method converts a DAO record to
|
22
|
+
# an entity.
|
23
|
+
def initialize(identifier:, updated_attrs:, dao:, factory:)
|
24
|
+
@identifier = identifier
|
25
|
+
@updated_attrs = updated_attrs
|
26
|
+
@dao = dao
|
27
|
+
@factory = factory
|
28
|
+
end
|
29
|
+
|
30
|
+
# Command-pattern method to update a record in the persistence layer,
|
31
|
+
# based on the parameters sent to `#initialize`.
|
32
|
+
# @return [Repository::Support::StoreResult]
|
33
|
+
def update
|
34
|
+
@record = dao.where(slug: identifier).first
|
35
|
+
return failed_result unless @record.update(updated_attrs.to_h)
|
36
|
+
successful_result
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_reader :dao, :factory, :identifier, :record, :updated_attrs
|
42
|
+
|
43
|
+
def failed_result # :nodoc:
|
44
|
+
StoreResult::Failure.new record.errors
|
45
|
+
end
|
46
|
+
|
47
|
+
def successful_result # :nodoc:
|
48
|
+
entity = factory.create record
|
49
|
+
StoreResult::Success.new entity
|
50
|
+
end
|
51
|
+
end # class Repository::Base::Internals::RecordUpdater
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'repository/support/result_builder'
|
4
|
+
|
5
|
+
module Repository
|
6
|
+
# Base class for Repository in Data Mapper pattern.
|
7
|
+
class Base
|
8
|
+
# Internal support code exclusively used by Repository::Base
|
9
|
+
module Internals
|
10
|
+
# Find a slug in the DAO, reporting errors if not successful.
|
11
|
+
# @since 0.0.3
|
12
|
+
class SlugFinder
|
13
|
+
include Repository::Support
|
14
|
+
|
15
|
+
# Initializes a new instance of `SlugFinder`.
|
16
|
+
# @param slug [String] [Slug](http://en.wikipedia.org/wiki/Semantic_URL#Slug)
|
17
|
+
# for record to be deleted.
|
18
|
+
# @param dao Data Access Object implements persistence without business
|
19
|
+
# logic.
|
20
|
+
def initialize(slug:, dao:, factory:)
|
21
|
+
@slug = slug
|
22
|
+
@dao = dao
|
23
|
+
@factory = factory
|
24
|
+
end
|
25
|
+
|
26
|
+
# Command-pattern method to search underlying DAO for record matching
|
27
|
+
# slug. Returns a `Repository::Support::StoreResult` instance with the
|
28
|
+
# corresponding entity on success, or with the error hash built by
|
29
|
+
# `Repository::Support::ErrorFactory.create` on failure.
|
30
|
+
# @return [Repository::Support::StoreResult]
|
31
|
+
# @see #errors_for_slug
|
32
|
+
# @see #result_builder
|
33
|
+
def find
|
34
|
+
result_builder(entity_for_slug).build do |_failed_record|
|
35
|
+
ErrorFactory.create errors_for_slug
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_reader :dao, :factory, :slug
|
42
|
+
|
43
|
+
# Builds an entity from an existing DAO record matching the slug.
|
44
|
+
# @return If the slug matches an existing DAO record, returns an entity
|
45
|
+
# as built from that record, otherwise returns `nil`.
|
46
|
+
def entity_for_slug
|
47
|
+
record = dao.where(slug: slug).first
|
48
|
+
factory.create(record) if record
|
49
|
+
end
|
50
|
+
|
51
|
+
# Builds an [`ActiveModel::Errors`](http://api.rubyonrails.org/classes/ActiveModel/Errors.html)
|
52
|
+
# instance and adds a slug-not-found message to it.
|
53
|
+
# @return [ActiveModel::Errors]
|
54
|
+
# @see #find
|
55
|
+
def errors_for_slug
|
56
|
+
errors = ActiveModel::Errors.new dao
|
57
|
+
errors.add :slug, "not found: '#{slug}'"
|
58
|
+
errors
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns a new `Repository::Support::ResultBuilder` instance, passing
|
62
|
+
# the parameter specified to this method as its `#initialize` parameter.
|
63
|
+
# @param record DAO record to pass to `ResultBuilder#initialize`.
|
64
|
+
def result_builder(record)
|
65
|
+
ResultBuilder.new record
|
66
|
+
end
|
67
|
+
end # class Repository::Base::Internals::SlugFinder
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|