navigable 1.0.2 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b96bb3ac3fd1a2e93eaaa1517c7bf890065f9ee803f4e20269bf96fcaea7f78f
4
- data.tar.gz: 3e08676aef87c9340af3b58e0b8124221e17a7ced004813dddd8f9377d9da9e0
3
+ metadata.gz: 966eb5d995d2e8f36c4450d4e2519457da26c2a0002710c12f77a612826d1807
4
+ data.tar.gz: 103449a1ac072269ea8b1286b47b07d6be5d6964adc04ca4b183ba40232d4ea8
5
5
  SHA512:
6
- metadata.gz: 689df880596033ed27ef92716554f2534a12a43b5d6c54a100eb363d8b3a57eed6ed356d77fbbcfddcf2ffc6509c6e866b0d6365ba27b9ecd908216b4d6b9322
7
- data.tar.gz: 298e1239bfd0cf6e7050766173dfee8cbae94af0cf2994e2d4bb0b7a318f3303da09878c55c45dcffff1356a8a3d69072154bb9bd8d81b17219bae42090224f0
6
+ metadata.gz: 97a549b8a71e31b29c3c99773a4ad24f1ce653b5223c5e526de33130196c025ffe502b296ae5719b67bee26d9d206a9c738f8ac5b8a4eed025295392c7b2396c
7
+ data.tar.gz: 62cd0c2c211d3e4056c6cb6cb3801a165842af287a09f95451fe78b265f6b04111acee9ae4b0cbadee246b3c80748a4ba38afd17e00ac8032ef32f4d224159fb
data/.gitignore CHANGED
@@ -13,5 +13,6 @@
13
13
  # rspec failure tracking
14
14
  .rspec_status
15
15
 
16
+ .DS_Store
16
17
  Gemfile.lock
17
18
 
data/README.md CHANGED
@@ -11,26 +11,26 @@ Navigable is a family of gems that together provide all the tools you need to bu
11
11
  <td width="130"><img alt="Clipper Ship" src="https://raw.githubusercontent.com/first-try-software/navigable/main/assets/clipper.png"></td>
12
12
  <td>
13
13
 
14
- **Navigable**<br>
14
+ **[Navigable][navigable]**<br>
15
15
  A stand-alone tool for isolating business logic from external interfaces and cross-cutting concerns. Navigable composes self-configured command and observer objects to allow you to extend your business logic without modifying it. Navigable is compatible with any Ruby-based application development framework, including Rails, Hanami, and Sinatra.
16
16
 
17
17
  </td>
18
18
  </tr>
19
19
  <tr height="140">
20
- <td width="130"><img alt="Compass" src="https://raw.githubusercontent.com/first-try-software/navigable/main/assets/compass.png"></td>
20
+ <td width="130"><img alt="Compass" src="https://raw.githubusercontent.com/first-try-software/navigable/main/assets/sextant.png"></td>
21
21
  <td>
22
22
 
23
- **Navigable Server** *(coming soon)*<br>
24
- A Rack-based web server for building Navigable-backed applications.
23
+ **[Navigable Router][router]** *(coming soon)*<br>
24
+ A simple, highly-performant, Rack-based router.
25
25
 
26
26
  </td>
27
27
  </tr>
28
28
  <tr height="140">
29
- <td width="130"><img alt="Telescope" src="https://raw.githubusercontent.com/first-try-software/navigable/main/assets/telescope.png"></td>
29
+ <td width="130"><img alt="Compass" src="https://raw.githubusercontent.com/first-try-software/navigable/main/assets/compass.png"></td>
30
30
  <td>
31
31
 
32
- **Navigable API** *(coming soon)*<br>
33
- An extension of Navigable Server for building restful JSON APIs.
32
+ **[Navigable Server][server]** *(coming soon)*<br>
33
+ A Rack-based server for building Ruby and Navigable web applications.
34
34
 
35
35
  </td>
36
36
  </tr>
@@ -38,7 +38,7 @@ An extension of Navigable Server for building restful JSON APIs.
38
38
  <td width="130"><img alt="Map" src="https://raw.githubusercontent.com/first-try-software/navigable/main/assets/map.png"></td>
39
39
  <td>
40
40
 
41
- **Navigable GraphQL** *(coming soon)*<br>
41
+ **[Navigable GraphQL][graphql]** *(coming soon)*<br>
42
42
  An extension of Navigable Server for building GraphQL APIs.
43
43
 
44
44
  </td>
@@ -49,8 +49,6 @@ An extension of Navigable Server for building GraphQL APIs.
49
49
 
50
50
  <img style="width: 600px; display: block; margin: 0 auto;" alt="Lighthouse" src="https://raw.githubusercontent.com/first-try-software/navigable/main/assets/lighthouse.png">
51
51
 
52
- <br>
53
-
54
52
  # The Navigable Charter
55
53
 
56
54
  We hold these truths to be self-evident, that not all objects are created equal, that poorly structured code leads to poorly tested code, and that poorly tested code leads to rigid software and fearful engineers.
@@ -164,11 +162,13 @@ Here are a few things to look for in the code above:
164
162
 
165
163
  * Use the `observes_all_commands` statement (as shown in the `Monitor` class) for cross-cutting concerns. It tells Navigable to send messages to the observer no matter which command was executed.
166
164
 
165
+ For a deeper look at the core concepts introduced by Navigable, please have a look at our [wiki][wiki].
166
+
167
167
  ## Feedback
168
168
 
169
169
  We are really excited about Navigable! We think it solves the problem of seperating business logic from the web interface, persistence layer, and even cross-cutting concerns in an elegant and simple way.
170
170
 
171
- We sincerely hope you'll download Navigable and play with it! If you do, please send us a note to [navigable@firsttry.software][mail] to give us your thoughts!
171
+ We're thrilled you're checking out Navigable! If you have any questions or comments, please feel free to reach out to [navigable@firsttry.software][mail].
172
172
 
173
173
  ## Installation
174
174
 
@@ -186,12 +186,6 @@ Or install it yourself as:
186
186
 
187
187
  $ gem install navigable
188
188
 
189
- ## Development
190
-
191
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
192
-
193
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
194
-
195
189
  ## Contributing
196
190
 
197
191
  Bug reports and pull requests are welcome on GitHub at https://github.com/first-try-software/navigable.
@@ -210,3 +204,8 @@ Everyone interacting in the Navigable project's codebases, issue trackers, chat
210
204
  [ocp]: https://en.wikipedia.org/wiki/Open–closed_principle
211
205
  [ddd]: https://en.wikipedia.org/wiki/Domain-driven_design
212
206
  [mail]: mailto:navigable@firsttry.software
207
+ [wiki]: https://github.com/first-try-software/navigable/wiki
208
+ [navigable]: https://github.com/first-try-software/navigable
209
+ [router]: https://github.com/first-try-software/navigable-router
210
+ [server]: https://github.com/first-try-software/navigable-server
211
+ [graphql]: https://github.com/first-try-software/navigable-graphql
@@ -1,5 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
+ require 'concurrent'
3
4
  require 'manufacturable'
4
5
 
5
6
  require 'navigable/version'
@@ -4,30 +4,35 @@ require 'navigable/observable'
4
4
 
5
5
  module Navigable
6
6
  module Command
7
+ class NotFoundError < StandardError; end
8
+
7
9
  TYPE = :__command__
8
10
  EXECUTE_NOT_IMPLEMENTED_MESSAGE = 'Class must implement `execute` method.'
9
11
 
10
12
  def self.extended(base)
11
13
  base.extend(Manufacturable::Item)
14
+ base.extend(ClassMethods)
12
15
  base.include(Observable)
16
+ base.include(InstanceMethods)
17
+ end
13
18
 
14
- base.instance_eval do
15
- def corresponds_to(key)
16
- super(key, TYPE)
17
- end
19
+ module ClassMethods
20
+ def corresponds_to(key)
21
+ super(key, TYPE)
18
22
  end
23
+ end
19
24
 
20
- base.class_eval do
21
- attr_reader :params, :observers
25
+ module InstanceMethods
26
+ attr_reader :params, :observers, :resolver
22
27
 
23
- def initialize(params: {}, observers: [])
24
- @params = params
25
- @observers = observers
26
- end
28
+ def inject(params: {}, observers: [], resolver: NullResolver.new)
29
+ @params = params
30
+ @observers = observers
31
+ @resolver = resolver
32
+ end
27
33
 
28
- def execute
29
- raise NotImplementedError.new(EXECUTE_NOT_IMPLEMENTED_MESSAGE)
30
- end
34
+ def execute
35
+ raise NotImplementedError.new(EXECUTE_NOT_IMPLEMENTED_MESSAGE)
31
36
  end
32
37
  end
33
38
  end
@@ -1,12 +1,12 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- require 'navigable/basic_resolver'
3
+ require 'navigable/null_resolver'
4
4
  require 'navigable/observer'
5
5
  require 'navigable/command'
6
6
 
7
7
  module Navigable
8
8
  class Dispatcher
9
- def self.dispatch(key, params: {}, resolver: BasicResolver.new)
9
+ def self.dispatch(key, params: {}, resolver: NullResolver.new)
10
10
  self.new(key, params: params, resolver: resolver).dispatch
11
11
  end
12
12
 
@@ -25,11 +25,15 @@ module Navigable
25
25
  end
26
26
 
27
27
  def observers
28
- Manufacturable.build_all(Observer::TYPE, key, params: params).push(resolver)
28
+ Manufacturable.build_all(Observer::TYPE, key) { |observer| observer.inject(params: params) }
29
29
  end
30
30
 
31
31
  def command
32
- Manufacturable.build_one(Command::TYPE, key, params: params, observers: observers)
32
+ build_command.tap { |command| raise Navigable::Command::NotFoundError unless command }
33
+ end
34
+
35
+ def build_command
36
+ Manufacturable.build_one(Command::TYPE, key) { |command| command.inject(params: params, observers: observers, resolver: resolver) }
33
37
  end
34
38
  end
35
39
  end
@@ -0,0 +1,9 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Navigable
4
+ class Executor
5
+ def self.execute(&block)
6
+ ENV['CONCURRENT_OBSERVERS'] ? Concurrent.global_io_executor.post(&block) : block.call
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ # frozen-string-literal: true
2
+
3
+ require 'navigable/resolver'
4
+
5
+ module Navigable
6
+ class NullResolver
7
+ extend Navigable::Resolver
8
+
9
+ def resolve
10
+ @result
11
+ end
12
+
13
+ ObserverMap::METHODS.values.each do |observer_method|
14
+ define_method(observer_method) do |result|
15
+ @result = result
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,39 +1,29 @@
1
1
  # frozen-string-literal: true
2
2
 
3
+ require 'navigable/observer_map'
4
+ require 'navigable/executor'
5
+
3
6
  module Navigable
4
7
  module Observable
5
8
  OBSERVERS_NOT_IMPLEMENTED_MESSAGE = 'Class must implement `observers` method.'
9
+ RESOLVER_NOT_IMPLEMENTED_MESSAGE = 'Class must implement `resolver` method.'
6
10
 
7
11
  def observers
8
12
  raise NotImplementedError.new(OBSERVERS_NOT_IMPLEMENTED_MESSAGE)
9
13
  end
10
14
 
11
- def successfully(*args)
12
- observers.each { |observer| observer.on_success(*args) }
13
- end
14
-
15
- def failed_to_validate(*args)
16
- observers.each { |observer| observer.on_failure_to_validate(*args) }
17
- end
18
-
19
- def failed_to_find(*args)
20
- observers.each { |observer| observer.on_failure_to_find(*args) }
15
+ def resolver
16
+ raise NotImplementedError.new(RESOLVER_NOT_IMPLEMENTED_MESSAGE)
21
17
  end
22
18
 
23
- def failed_to_create(*args)
24
- observers.each { |observer| observer.on_failure_to_create(*args) }
25
- end
26
-
27
- def failed_to_update(*args)
28
- observers.each { |observer| observer.on_failure_to_update(*args) }
29
- end
30
-
31
- def failed_to_delete(*args)
32
- observers.each { |observer| observer.on_failure_to_delete(*args) }
33
- end
19
+ ObserverMap::METHODS.each_pair do |observable_method, observer_method|
20
+ define_method(observable_method) do |*args, **kwargs|
21
+ observers.each do |observer|
22
+ Navigable::Executor.execute { observer.send(observer_method, *args, **kwargs) }
23
+ end
34
24
 
35
- def failed(*args)
36
- observers.each { |observer| observer.on_failure(*args) }
25
+ resolver.send(observer_method, *args, **kwargs)
26
+ end
37
27
  end
38
28
  end
39
29
  end
@@ -8,28 +8,30 @@ module Navigable
8
8
 
9
9
  def self.extended(base)
10
10
  base.extend(Manufacturable::Item)
11
+ base.extend(ClassMethods)
11
12
  base.include(ObserverInterface)
13
+ base.include(InstanceMethods)
14
+ end
12
15
 
13
- base.instance_eval do
14
- def observes_all_commands
15
- corresponds_to_all(TYPE)
16
- end
16
+ module ClassMethods
17
+ def observes_all_commands
18
+ corresponds_to_all(TYPE)
19
+ end
17
20
 
18
- def observes(key)
19
- corresponds_to(key, TYPE)
20
- end
21
+ def observes(key)
22
+ corresponds_to(key, TYPE)
21
23
  end
24
+ end
22
25
 
23
- base.class_eval do
24
- attr_reader :params
26
+ module InstanceMethods
27
+ attr_reader :params
25
28
 
26
- def initialize(params: {})
27
- @params = params
28
- end
29
+ def inject(params: {})
30
+ @params = params
31
+ end
29
32
 
30
- def observed_command_key
31
- manufacturable_item_key
32
- end
33
+ def observed_command_key
34
+ manufacturable_item_key
33
35
  end
34
36
  end
35
37
  end
@@ -1,13 +1,11 @@
1
1
  # frozen-string-literal: true
2
2
 
3
+ require 'navigable/observer_map'
4
+
3
5
  module Navigable
4
6
  module ObserverInterface
5
- def on_success(*args, **kwargs); end
6
- def on_failure_to_validate(*args, **kwargs); end
7
- def on_failure_to_find(*args, **kwargs); end
8
- def on_failure_to_create(*args, **kwargs); end
9
- def on_failure_to_update(*args, **kwargs); end
10
- def on_failure_to_delete(*args, **kwargs); end
11
- def on_failure(*args, **kwargs); end
7
+ ObserverMap::METHODS.values.each do |observer_method|
8
+ define_method(observer_method) { |*args, **kwargs| }
9
+ end
12
10
  end
13
11
  end
@@ -0,0 +1,16 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Navigable
4
+ class ObserverMap
5
+ METHODS = {
6
+ successfully: :on_success,
7
+ successfully_created: :on_creation,
8
+ failed_to_validate: :on_failure_to_validate,
9
+ failed_to_find: :on_failure_to_find,
10
+ failed_to_create: :on_failure_to_create,
11
+ failed_to_update: :on_failure_to_update,
12
+ failed_to_delete: :on_failure_to_delete,
13
+ failed: :on_failure
14
+ }.freeze
15
+ end
16
+ end
@@ -4,14 +4,29 @@ require 'navigable/observer_interface'
4
4
 
5
5
  module Navigable
6
6
  module Resolver
7
- RESOLVE_NOT_IMPLEMENTED_MESSAGE = 'Class must implement `resolve` method.'
7
+ TYPE = :__resolver__
8
+ RESOLVE_NOT_IMPLEMENTED_MESSAGE = 'Resolver classes must implement a `resolve` method.'
8
9
 
9
10
  def self.extended(base)
11
+ base.extend(Manufacturable::Item)
12
+ base.extend(ClassMethods)
10
13
  base.include(ObserverInterface)
11
- base.class_eval do
12
- def resolve
13
- raise NotImplementedError.new(RESOLVE_NOT_IMPLEMENTED_MESSAGE)
14
- end
14
+ base.include(InstanceMethods)
15
+ end
16
+
17
+ module ClassMethods
18
+ def default_resolver
19
+ default_manufacturable(TYPE)
20
+ end
21
+
22
+ def resolves(key)
23
+ corresponds_to(key, TYPE)
24
+ end
25
+ end
26
+
27
+ module InstanceMethods
28
+ def resolve
29
+ raise NotImplementedError.new(RESOLVE_NOT_IMPLEMENTED_MESSAGE)
15
30
  end
16
31
  end
17
32
  end
@@ -1,5 +1,5 @@
1
1
  # frozen-string-literal: true
2
2
 
3
3
  module Navigable
4
- VERSION = "1.0.2"
4
+ VERSION = "1.5.0"
5
5
  end
@@ -24,7 +24,8 @@ Gem::Specification.new do |spec|
24
24
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
25
  spec.require_paths = ["lib"]
26
26
 
27
- spec.add_dependency "manufacturable", "~> 1.4"
27
+ spec.add_dependency "concurrent-ruby", "~> 1.1.7"
28
+ spec.add_dependency "manufacturable", "~> 1.5"
28
29
 
29
30
  spec.add_development_dependency "bundler", "~> 2.0"
30
31
  spec.add_development_dependency "rake", "~> 12.0"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: navigable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alan Ridlehoover
@@ -9,22 +9,36 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2020-09-17 00:00:00.000000000 Z
12
+ date: 2020-12-06 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: concurrent-ruby
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 1.1.7
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 1.1.7
14
28
  - !ruby/object:Gem::Dependency
15
29
  name: manufacturable
16
30
  requirement: !ruby/object:Gem::Requirement
17
31
  requirements:
18
32
  - - "~>"
19
33
  - !ruby/object:Gem::Version
20
- version: '1.4'
34
+ version: '1.5'
21
35
  type: :runtime
22
36
  prerelease: false
23
37
  version_requirements: !ruby/object:Gem::Requirement
24
38
  requirements:
25
39
  - - "~>"
26
40
  - !ruby/object:Gem::Version
27
- version: '1.4'
41
+ version: '1.5'
28
42
  - !ruby/object:Gem::Dependency
29
43
  name: bundler
30
44
  requirement: !ruby/object:Gem::Requirement
@@ -108,7 +122,6 @@ extra_rdoc_files: []
108
122
  files:
109
123
  - ".gitignore"
110
124
  - ".rspec"
111
- - ".ruby-version"
112
125
  - ".travis.yml"
113
126
  - CODE_OF_CONDUCT.md
114
127
  - Gemfile
@@ -118,12 +131,14 @@ files:
118
131
  - bin/console
119
132
  - bin/setup
120
133
  - lib/navigable.rb
121
- - lib/navigable/basic_resolver.rb
122
134
  - lib/navigable/command.rb
123
135
  - lib/navigable/dispatcher.rb
136
+ - lib/navigable/executor.rb
137
+ - lib/navigable/null_resolver.rb
124
138
  - lib/navigable/observable.rb
125
139
  - lib/navigable/observer.rb
126
140
  - lib/navigable/observer_interface.rb
141
+ - lib/navigable/observer_map.rb
127
142
  - lib/navigable/resolver.rb
128
143
  - lib/navigable/version.rb
129
144
  - navigable.gemspec
@@ -1 +0,0 @@
1
- 2.7.1
@@ -1,24 +0,0 @@
1
- # frozen-string-literal: true
2
-
3
- require 'navigable/resolver'
4
-
5
- module Navigable
6
- class BasicResolver
7
- extend Navigable::Resolver
8
-
9
- def resolve
10
- @result
11
- end
12
-
13
- def on_success(result)
14
- @result = result
15
- end
16
-
17
- alias_method :on_failure_to_validate, :on_success
18
- alias_method :on_failure_to_find, :on_success
19
- alias_method :on_failure_to_create, :on_success
20
- alias_method :on_failure_to_update, :on_success
21
- alias_method :on_failure_to_delete, :on_success
22
- alias_method :on_failure, :on_success
23
- end
24
- end