dawn 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ff614e1aab7364c357895c1580cbebab964b155e94fb72d52b00a31e224becd7
4
+ data.tar.gz: fae73234075fb18289088e0aa1f4e49cfe917e721bc04f47fe5e06b065277786
5
+ SHA512:
6
+ metadata.gz: 7ce6e37497dfcfa13406602e973529ef581d99f5ec0f76ed65f2cafa40a2e60c4313986c402c54721497d00984f31b7af4e39a49a46a3ab2e82b0681e65b903c
7
+ data.tar.gz: e8deccb8a66598ada426cdf83508420a3b3c285e3faf18bc254c8fbc839a691200e7f82f89969d1091e9f89cef5c6861502038243f5bb9e43dacd5b96c3509d9
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.6.3
7
+ before_install: gem install bundler -v 1.17.2
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in dawn.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,41 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ dawn (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ coderay (1.1.2)
10
+ diff-lcs (1.3)
11
+ method_source (0.9.2)
12
+ pry (0.12.2)
13
+ coderay (~> 1.1.0)
14
+ method_source (~> 0.9.0)
15
+ rake (10.4.2)
16
+ rspec (3.8.0)
17
+ rspec-core (~> 3.8.0)
18
+ rspec-expectations (~> 3.8.0)
19
+ rspec-mocks (~> 3.8.0)
20
+ rspec-core (3.8.0)
21
+ rspec-support (~> 3.8.0)
22
+ rspec-expectations (3.8.3)
23
+ diff-lcs (>= 1.2.0, < 2.0)
24
+ rspec-support (~> 3.8.0)
25
+ rspec-mocks (3.8.0)
26
+ diff-lcs (>= 1.2.0, < 2.0)
27
+ rspec-support (~> 3.8.0)
28
+ rspec-support (3.8.0)
29
+
30
+ PLATFORMS
31
+ ruby
32
+
33
+ DEPENDENCIES
34
+ bundler (~> 1.17)
35
+ dawn!
36
+ pry (~> 0.12.2)
37
+ rake (~> 10.0)
38
+ rspec (~> 3.0)
39
+
40
+ BUNDLED WITH
41
+ 1.17.2
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Eliav Lavi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,142 @@
1
+ # Dawn
2
+ `Dawn` is a simple and intuitive instances container for Ruby. It was born out of the need to reference existing application instances in Rails controllers, without having to re-initialize them upon each controller call or using the singleton pattern; however, while `Dawn` can be used in a Rails app, it may fit any Ruby application that is built with dependency injection in mind.
3
+
4
+ ## Installation
5
+
6
+ Add this line to your application's Gemfile:
7
+
8
+ ```ruby
9
+ gem 'dawn'
10
+ ```
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install dawn
19
+
20
+ ## Usage
21
+
22
+ `Dawn` allows you to build a container of namespaces which are registered with instances. Upon the booting stages of your application, you might want to initialize instances of classes which serve different purposes (e.g. `ChargeCreationService` for handling the complexity of creating a new charge in your system), accross different business domains. That way you can reference those instances later, when requests start arriving at your application, without having re-initialize those instances each time. This idea also allows you to build your app as a hierarchical tree of dependnecies, as will be demonstrated later.
23
+
24
+ You can build a `Dawn` container with multiple namespaces, each namespace having a unique name and containing several instances:
25
+
26
+ ```ruby
27
+ # Assuming instance_a and instance_b were just initialized:
28
+ foo_namespace_request = Dawn::Namespace::Request.new(name: :foo) do |namespace|
29
+ namespace
30
+ .set(key: :instance_a, instance: instance_a)
31
+ .set(key: :instance_b, instance: instance_b)
32
+ end
33
+
34
+ CONTAINER = Dawn::Container.build([foo_namespace_request])
35
+ ```
36
+
37
+ Later, you may fetch instances from the container:
38
+ ```ruby
39
+ CONTAINER.fetch(namespace: foo, key: :instance_a)
40
+ ```
41
+ ![](https://i.imgur.com/GFEKDlh.png)
42
+
43
+ ### Dependency Injection Based Applications
44
+ Since `Dawn` was created to support the development of dependency injection based application, I will briefly discuss this topic in this section. Usage in Rails will be clearer hence.
45
+
46
+ Crafting your application as such that is based on a nested tree of injected dependencies has many benefits that has been described in many writings before. That is, in contrary to an opposing design choice, which is fairly common in the Ruby eco-system, in which dependencies are hard coded. The following example demonstrates this in a nutshell.
47
+
48
+ Assuming some `FooService` class:
49
+
50
+ ```ruby
51
+ class FooService
52
+ def call
53
+ # Return some value / do something
54
+ end
55
+ end
56
+ ```
57
+
58
+ We can use it as a dependency in another class, `BarService` in two ways. Either directly, as an hard-coded dependency:
59
+ ```ruby
60
+ class BarService
61
+ def call
62
+ FooService.new.call
63
+ end
64
+ end
65
+ ```
66
+
67
+ Or as injected dependency:
68
+ ```ruby
69
+ class BarService
70
+ def self.build
71
+ foo_service = FooService.new
72
+ new(foo_service: foo_service)
73
+ end
74
+
75
+ def initialize(foo_service:)
76
+ @foo_service = foo_service
77
+ end
78
+ def call
79
+ @foo_service.call
80
+ end
81
+ end
82
+ ```
83
+
84
+ This is just a single level of dependency, real-life application are often much more nested.
85
+ Notice that I did mentioned `FooService` in an hard-coded fashion, in my `.build` method, but that is just a helper method to get the common usage of `BarService` instances and its not on the instant scope, but on the class scope. I can pass anything I want to `BarService#initialize`.
86
+
87
+ I urge you to read more about dependency injection, but the advantages of the latter approach are numerous! From testing to code reusability, dependency injection is a real thing, and it doesn't have to be complex or scary at all.
88
+
89
+ ### Usage In Rails
90
+ One problem that sometimes emerges is that we don't have control of everything in our application, and the most prominent example is if we are building our application on top of Rails. When Rails receives a request to a certain route, it will find the controller which is registered with this route **and will initialize a new instance of that controller with the parameters of the request as the instance variable `@param`**. This pattern is the complete opposite of dependency injection based design, but we have nothing to do about it (other than not using Rails, which is not always a choice we can make).
91
+ While we cannot change this behaviour of Rails - a new controller instance will be created upon each request - we can choose to stop this pattern right there, and use pre-initialized instances from there on. This is where `Dawn` becomes really helpful.
92
+
93
+ For example, let's assume some `ChargesController`, whose `#create` method needs to pass request's data to some `ChargeCreationService`. Usually, the implementation would look like this:
94
+ ```ruby
95
+ class ChargesController < ApplicationController
96
+ def create
97
+ ChargeCreationService.new.call(params[:charge])
98
+
99
+ # Note: In this manner, ChargeCreationService instances have no members / instance variables.
100
+ # Some people also do the following, which might feel slicker at first:
101
+ # ChargeCreationService.new(params[:charge]).call
102
+ # Honestly, assuming ChargeCreationService has dependencies of it's own, both feel wrong to me.
103
+ end
104
+ end
105
+ ```
106
+
107
+ With `Dawn` we can do this:
108
+ ```ruby
109
+ class ChargesController < ApplicationController
110
+ def create
111
+ # Assuming we initialized a proper Dawn container in the initialization stages of our application, and assigned it the the global const CONTAINER:
112
+
113
+ service = CONTAINER.fetch(namespace: :charges, key: :creation_service)
114
+ service.call(params[:charge])
115
+ end
116
+ end
117
+ ```
118
+
119
+ Rails offers the `after_initialize` hook that you can use in `application.rb` - this is a proper place to initialize your `Dawn::Container`. In the previous example, I have referenced it from the top level contsant `CONTAINER`, which could be assigned there. See [here](https://guides.rubyonrails.org/configuring.html#rails-general-configuration) for more info on `after_initialize`.
120
+
121
+ ## Development
122
+
123
+ 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.
124
+
125
+ 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).
126
+
127
+ ## Feature Requests
128
+
129
+ Dawn is open for changes and requests!
130
+ If you have an idea, a question or some need, feel free to contact me here or at eliavlavi@gmail.com.
131
+
132
+ ## Contributing
133
+
134
+ 1. Fork it ( https://github.com/eliav-lavi/dawn/fork )
135
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
136
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
137
+ 4. Push to the branch (`git push origin my-new-feature`)
138
+ 5. Create a new Pull Request
139
+
140
+ ## License
141
+
142
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "dawn"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/dawn.gemspec ADDED
@@ -0,0 +1,28 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "dawn/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "dawn"
8
+ spec.version = Dawn::VERSION
9
+ spec.authors = ["Eliav Lavi"]
10
+ spec.email = ["eliavlavi@gmail.com"]
11
+
12
+ spec.summary = %q{A Object Initialization Utility for Dependency Injection Based Design Purposes}
13
+ spec.description = %q{A a simple and intuitive instances container for Ruby. Dawn helps in managing re-usable instances in an application, without having to recreate them upon each request. }
14
+ spec.homepage = "https://github.com/eliav-lavi/dawn"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
18
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.17"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.0"
27
+ spec.add_development_dependency "pry", '~> 0.12.2'
28
+ end
data/lib/dawn.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'dawn/version'
2
+ require 'dawn/container'
3
+ require 'dawn/namespace/request'
4
+
5
+ module Dawn
6
+ class Error < StandardError
7
+ end
8
+
9
+ class NamespaceNotRegisteredError < Error
10
+ end
11
+
12
+ class NamespaceAlreadyRegisteredError < Error
13
+ end
14
+
15
+ class InstanceNotRegisteredError < Error
16
+ end
17
+
18
+ class InstanceAlreadyRegisteredError < Error
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ module Dawn
2
+ class Container
3
+ class << self
4
+ def build(namespace_requests)
5
+ namespaces = namespace_requests.each_with_object({}) do |request, hash|
6
+ raise Dawn::NamespaceAlreadyRegisteredError if hash.key?(request.name)
7
+ hash[request.name] = request.process
8
+ end
9
+
10
+ new(namespaces: namespaces)
11
+ end
12
+ end
13
+
14
+ def initialize(namespaces:)
15
+ @namespaces = namespaces
16
+ end
17
+
18
+ def fetch(namespace:, key:)
19
+ raise Dawn::NamespaceNotRegisteredError unless @namespaces.key?(namespace)
20
+ @namespaces[namespace].get(key: key)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ require 'dawn'
2
+
3
+ module Dawn
4
+ module Namespace
5
+ class Finalized
6
+ def initialize(instances:)
7
+ @instances = instances
8
+ end
9
+
10
+ def get(key:)
11
+ raise Dawn::InstanceNotRegisteredError unless @instances.key?(key)
12
+ @instances[key]
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ require 'dawn/namespace/transitional'
2
+
3
+ module Dawn
4
+ module Namespace
5
+ class Request
6
+ attr_reader :name
7
+
8
+ def initialize(name:, &proc)
9
+ @name = name
10
+ @proc = proc
11
+ end
12
+
13
+ def process
14
+ @proc.call(Transitional.new).finalize
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ require 'dawn/namespace/finalized'
2
+ require 'dawn'
3
+
4
+ module Dawn
5
+ module Namespace
6
+ class Transitional
7
+ def initialize(instances: {})
8
+ @instances = instances
9
+ end
10
+
11
+ def set(key:, instance:)
12
+ raise Dawn::InstanceAlreadyRegisteredError if @instances.key?(key)
13
+ Transitional.new(instances: @instances.merge(Hash[key, instance]))
14
+ end
15
+
16
+ def finalize
17
+ Finalized.new(instances: @instances)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ module Dawn
2
+ VERSION = '0.1.0'
3
+ end
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dawn
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Eliav Lavi
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-08-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.17'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.17'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.12.2
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.12.2
69
+ description: 'A a simple and intuitive instances container for Ruby. Dawn helps in
70
+ managing re-usable instances in an application, without having to recreate them
71
+ upon each request. '
72
+ email:
73
+ - eliavlavi@gmail.com
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - ".gitignore"
79
+ - ".rspec"
80
+ - ".travis.yml"
81
+ - Gemfile
82
+ - Gemfile.lock
83
+ - LICENSE.txt
84
+ - README.md
85
+ - Rakefile
86
+ - bin/console
87
+ - bin/setup
88
+ - dawn.gemspec
89
+ - lib/dawn.rb
90
+ - lib/dawn/container.rb
91
+ - lib/dawn/namespace/finalized.rb
92
+ - lib/dawn/namespace/request.rb
93
+ - lib/dawn/namespace/transitional.rb
94
+ - lib/dawn/version.rb
95
+ homepage: https://github.com/eliav-lavi/dawn
96
+ licenses:
97
+ - MIT
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubygems_version: 3.0.3
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: A Object Initialization Utility for Dependency Injection Based Design Purposes
118
+ test_files: []