repository-base 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +27 -0
  5. data/.travis.yml +6 -0
  6. data/.yardopts +1 -0
  7. data/CHANGELOG.md +128 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE +22 -0
  10. data/README.md +132 -0
  11. data/Rakefile +33 -0
  12. data/bin/bundle +105 -0
  13. data/bin/htmldiff +29 -0
  14. data/bin/kramdown +29 -0
  15. data/bin/ldiff +29 -0
  16. data/bin/rake +29 -0
  17. data/bin/rspec +29 -0
  18. data/bin/rubocop +29 -0
  19. data/bin/ruby-parse +29 -0
  20. data/bin/ruby-rewrite +29 -0
  21. data/bin/setup +43 -0
  22. data/bin/yard +29 -0
  23. data/bin/yardoc +29 -0
  24. data/bin/yri +29 -0
  25. data/doc/Repository.html +128 -0
  26. data/doc/Repository/Base.html +1248 -0
  27. data/doc/Repository/Base/Internals.html +133 -0
  28. data/doc/Repository/Base/Internals/RecordDeleter.html +687 -0
  29. data/doc/Repository/Base/Internals/RecordSaver.html +816 -0
  30. data/doc/Repository/Base/Internals/RecordUpdater.html +1026 -0
  31. data/doc/Repository/Base/Internals/SlugFinder.html +986 -0
  32. data/doc/_index.html +176 -0
  33. data/doc/class_list.html +51 -0
  34. data/doc/css/common.css +1 -0
  35. data/doc/css/full_list.css +58 -0
  36. data/doc/css/style.css +499 -0
  37. data/doc/file.CHANGELOG.html +240 -0
  38. data/doc/file.README.html +218 -0
  39. data/doc/file_list.html +61 -0
  40. data/doc/frames.html +17 -0
  41. data/doc/index.html +218 -0
  42. data/doc/js/app.js +248 -0
  43. data/doc/js/full_list.js +216 -0
  44. data/doc/js/jquery.js +4 -0
  45. data/doc/method_list.html +363 -0
  46. data/doc/top-level-namespace.html +110 -0
  47. data/lib/repository/base.rb +115 -0
  48. data/lib/repository/base/internals/internals.rb +6 -0
  49. data/lib/repository/base/internals/record_deleter.rb +46 -0
  50. data/lib/repository/base/internals/record_saver.rb +58 -0
  51. data/lib/repository/base/internals/record_updater.rb +54 -0
  52. data/lib/repository/base/internals/slug_finder.rb +70 -0
  53. data/lib/repository/base/version.rb +12 -0
  54. data/repository-base.gemspec +37 -0
  55. data/spec/repository/base_spec.rb +398 -0
  56. data/spec/spec_helper.rb +14 -0
  57. 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
+ &mdash; 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> &raquo;
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,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'record_deleter'
4
+ require_relative 'record_saver'
5
+ require_relative 'record_updater'
6
+ require_relative 'slug_finder'
@@ -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