repository-base 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|