hanami-utils 0.0.0 → 0.7.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
  SHA1:
3
- metadata.gz: 7ae931bb7b52b8813571a42d02b4fa726f2050be
4
- data.tar.gz: d77d30b3a8e167047bdca4eb1d08a9e02f17095d
3
+ metadata.gz: 5d7bd99764bc258d92e6fdbb95ee0b703b80728e
4
+ data.tar.gz: 76e1e00e98a9ba63f6b6516099613f8628e7c676
5
5
  SHA512:
6
- metadata.gz: 61e38f6da84ca2416f27f54f43cb9bd7a802ccba64835b17390ba690adc0c1246d8f67cacf7a7493a2a3987fdc9d40adcff08ab864610930f3c2e73443c82e58
7
- data.tar.gz: 99359c4cd2c3301093928c4154feb454b6e6f2ef5ce9043d193a375fafde16095dd372684493d3b81c09314b6263865ce6d85d02798f939db6db2140afebc244
6
+ metadata.gz: dbc1321581bee6c3e0400ad4596b7b712f817550b94536010a573153c0dd53d7fe0cc0201621cfd1502fa531e38a7b040fa314801f671f6bf3bd74521b12a248
7
+ data.tar.gz: ae7b724c1ae87c3b5c5ba5cde7740e3be157edf9e9c6773b2f97c4e75432090dd29dac4c846d7ca62f6b00e4bd5984e4f933315c0ce9aee9058d1222f9e1b641
@@ -0,0 +1,183 @@
1
+ # Hanami::Utils
2
+ Ruby core extentions and class utilities for Hanami
3
+
4
+ ## v0.7.0 - 2016-01-22
5
+ ### Changed
6
+ - [Luca Guidi] Renamed the project
7
+
8
+ ## v0.6.1 - 2016-01-19
9
+ ### Fixed
10
+ - [Anton Davydov] Ensure `Lotus::Utils::String#classify` to work properly with dashes (eg. `"app-store" => "App::Store"`)
11
+
12
+ ## v0.6.0 - 2016-01-12
13
+ ### Added
14
+ - [Luca Guidi] Official support for Ruby 2.3
15
+ - [Luca Guidi] Custom inflections
16
+ - [Luca Guidi] Introduced `Lotus::Utils::Duplicable` as a safe dup logic for Ruby types
17
+ - [Luca Guidi] Added `Lotus::Utils::String#rsub` replace rightmost occurrence
18
+
19
+ ### Fixed
20
+ - [Luca Guidi] Fix `Lotus::Utils::PathPrefix#join` and `#relative_join` by rejecting arguments that are equal to the separator
21
+ - [Karim Kiatlottiavi] Fix `Encoding::UndefinedConversionError` in `Lotus::Utils::Escape.encode`
22
+
23
+ ### Changed
24
+ - [Luca Guidi] Deprecate Ruby 2.0 and 2.1
25
+ - [Luca Guidi] Removed `Lotus::Utils::Callbacks#add` in favor of `#append`
26
+ - [Luca Guidi] Removed pattern support for `Utils::Class.load!` (eg. `Articles(Controller|::Controller)`)
27
+
28
+ ## v0.5.2 - 2015-09-30
29
+ ### Added
30
+ - [Luca Guidi] Added `Lotus::Utils::String#capitalize`
31
+ - [Trung Lê] Official support for JRuby 9k+
32
+
33
+ ## v0.5.1 - 2015-07-10
34
+ ### Fixed
35
+ - [Thiago Felippe] Ensure `Lotus::Utils::PathPrefix#join` won't remote duplicate entries (eg `/admin/dashboard/admin`)
36
+
37
+ ## v0.5.0 - 2015-06-23
38
+ ### Added
39
+ - [Luca Guidi] Extracted `Lotus::Logger` from `hanamirb`
40
+
41
+ ### Changed
42
+ - [Luca Guidi] `Lotus::Interactor::Result` contains only objects explicitly exposed via `Lotus::Interactor.expose`.
43
+
44
+ ## v0.4.3 - 2015-05-22
45
+ ### Added
46
+ - [François Beausoleil] Improved `Lotus::Utils::Kernel` messages for `TypeError`.
47
+
48
+ ## v0.4.2 - 2015-05-15
49
+ ### Fixed
50
+ - [Luca Guidi] Ensure `Lotus::Utils::Attributes#to_h` to return `::Hash`
51
+
52
+ ## v0.4.1 - 2015-05-15
53
+ ### Added
54
+ - [Luca Guidi & Alfonso Uceda Pompa] Introduced `Lotus::Utils::Inflector`, `Lotus::Utils::String#pluralize` and `#singularize`
55
+
56
+ ### Fixed
57
+ - [Luca Guidi] Ensure `Lotus::Utils::Attributes#to_h` to safely return nested `::Hash` instances for complex data structures.
58
+ - [Luca Guidi] Let `Lotus::Interactor#error` to return a falsey value for control flow. (eg. `check_permissions or error "You can't access"`)
59
+
60
+ ## v0.4.0 - 2015-03-23
61
+ ### Added
62
+ - [Luca Guidi] Introduced `Lotus::Utils::Escape`. It implements OWASP/ESAPI suggestions for HTML, HTML attribute and URL escape utilities.
63
+ - [Luca Guidi] Introduced `Lotus::Utils::String#dasherize`
64
+ - [Luca Guidi] Introduced `Lotus::Utils::String#titleize`
65
+
66
+ ## v0.3.5 - 2015-03-12
67
+ ### Added
68
+ - [Luca Guidi] Introduced `Lotus::Interactor`
69
+ - [Luca Guidi] Introduced `Lotus::Utils::BasicObject`
70
+
71
+ ## v0.3.4 - 2015-01-30
72
+ ### Added
73
+ - [Alfonso Uceda Pompa] Aliased `Lotus::Utils::Attributes#get` with `#[]`
74
+ - [Simone Carletti] Introduced `Lotus::Utils::Callbacks::Chain#prepend` and `#append`
75
+
76
+ ### Deprecated
77
+ - [Luca Guidi] Deprecated `Lotus::Utils::Callbacks::Chain#add` in favor of `#append`
78
+
79
+ ## v0.3.3 - 2015-01-08
80
+ ### Fixed
81
+ - [Luca Guidi] Ensure to return the right offending object if a missing method is called with Utils::String and Hash (eg. `Utils::Hash.new(a: 1).all? {|_, v| v.foo }` blame `v` instead of `Hash`)
82
+ - [Luca Guidi] Raise an error if try to coerce non numeric strings into Integer, Float & BigDecimal (eg. `Utils::Kernel.Integer("hello") # => raise TypeError`)
83
+
84
+ ## v0.3.2 - 2014-12-23
85
+ ### Added
86
+ - [Luca Guidi] Official support for Ruby 2.2
87
+ - [Luca Guidi] Introduced `Utils::Attributes`
88
+ - [Luca Guidi] Added `Utils::Hash#stringify!`
89
+
90
+ ## v0.3.1 - 2014-11-23
91
+ ### Added
92
+ - [Luca Guidi] Allow `Utils::Class.load!` to accept any object that implements `#to_s`
93
+ - [Trung Lê] Allow `Utils::Class.load!` to accept a class
94
+ - [Luca Guidi] Introduced `Utils::Class.load_from_pattern!`
95
+ - [Luca Guidi] Introduced `Utils.jruby?` and `Utils.rubinius?`
96
+ - [Luca Guidi] Introduced `Utils::Deprecation`
97
+ - [Luca Guidi] Official support for Rubinius 2.3+
98
+ - [Luca Guidi] Official support for JRuby 1.7+ (with 2.0 mode)
99
+ - [Janko Marohnić] Implemented `Utils::PathPrefix` relativness and absolutness
100
+ - [Luca Guidi] Made `Utils::PathPrefix` `#join` and `#relative_join` to return a new instance of that class
101
+ - [Luca Guidi] Implemented `Utils::Hash#deep_dup`
102
+ - [Luca Guidi] Made `Utils::PathPrefix#join` to accept multiple argument
103
+
104
+ ### Fixed
105
+ - [Luca Guidi] Made `Utils::PathPrefix#join` remove trailing occurrences for `@separator` from the output
106
+ - [Luca Guidi] Made `Utils::PathPrefix#relative_join` to correctly replace all the instances of `@separator` from the output
107
+
108
+ ### Deprecated
109
+ - [Luca Guidi] Deprecated `Utils::Class.load!` with a pattern like `Articles(Controller|::Controller)`, use `Utils::Class.load_from_pattern!` instead
110
+
111
+ ## v0.3.0 - 2014-10-23
112
+ ### Added
113
+ - [Celso Fernandes] Add BigDecimal coercion to Lotus::Utils::Kernel
114
+ - [Luca Guidi] Define `Boolean` constant, if missing
115
+ - [Luca Guidi] Use composition over inheritance for `Lotus::Utils::PathPrefix`
116
+ - [Luca Guidi] Use composition over inheritance for `Lotus::Utils::Hash`
117
+ - [Luca Guidi] Use composition over inheritance for `Lotus::Utils::String`
118
+
119
+ ### Fixed
120
+ - [Luca Guidi] Improved error message for `Utils::Class.load!`
121
+ - [Tom Kadwill] Improved error `NameError` message by passing in the whole constant name to `Utils::Class.load!`
122
+ - [Luca Guidi] `Utils::Hash#to_h` return instances of `::Hash` in case of nested symbolized data structure
123
+ - [Luca Guidi] Raise `TypeError` if `nil` is passed to `PathPrefix#relative_join`
124
+ - [Peter Suschlik] Define `Lotus::Utils::Hash#respond_to_missing?`
125
+ - [Peter Suschlik] Define `Lotus::Utils::String#responds_to_missing?`
126
+ - [Luca Guidi] Ensure `Utils::Hash#inspect` output to be the same of `::Hash#inspect`
127
+
128
+ ## v0.2.0 - 2014-06-23
129
+ ### Added
130
+ - [Luca Guidi] Implemented `Lotus::Utils::Kernel.Symbol`
131
+ - [Luca Guidi] Made `Kernel.Pathname` to raise an error when `nil` is passed as argument
132
+ - [Luca Guidi] Implemented `Lotus::Utils::LoadPaths#freeze` in order to prevent modification after the object has been frozen
133
+ - [Luca Guidi] Implemented Lotus::Utils::LoadPaths#push, also aliased as #<<
134
+ - [Luca Guidi] Use composition over inheritance for `Lotus::Utils::LoadPaths`
135
+ - [Luca Guidi] Introduced `Lotus::Utils::LoadPaths`
136
+ - [Luca Guidi] Introduced `Lotus::Utils::String#namespace`, in order to return the top level Ruby namespace for the given string
137
+ - [Luca Guidi] Implemented `Lotus::Utils::Kernel.Pathname`
138
+
139
+ ### Fixed
140
+ - [Luca Guidi] Implemented `Lotus::Utils::LoadPaths#initialize_copy` in order to safely `#dup` and `#clone`
141
+
142
+ ### Changed
143
+ - [Luca Guidi] Implemented `Lotus::Utils::Callbacks::Chain#freeze` in order to prevent modification after the object has been frozen
144
+ - [Luca Guidi] All the `Utils::Kernel` methods will raise `TypeError` in case of failed coercion.
145
+ - [Luca Guidi] Made `Kernel.Time` to raise an error when `nil` is passed as argument
146
+ - [Luca Guidi] Made `Kernel.DateTime` to raise an error when `nil` is passed as argument
147
+ - [Luca Guidi] Made `Kernel.Date` to raise an error when `nil` is passed as argument
148
+ - [Luca Guidi] Made `Kernel.Boolean` to return false when `nil` is passed as argument
149
+ - [Luca Guidi] Made `Kernel.String` to return an empty string when `nil` is passed as argument
150
+ - [Luca Guidi] Made `Kernel.Float` to return `0.0` when `nil` is passed as argument
151
+ - [Luca Guidi] Made `Kernel.Integer` to return `0` when `nil` is passed as argument
152
+ - [Luca Guidi] Made `Kernel.Hash` to return an empty `Hash` when `nil` is passed as argument
153
+ - [Luca Guidi] Made `Kernel.Set` to return an empty `Set` when `nil` is passed as argument
154
+ - [Luca Guidi] Made `Kernel.Array` to return an empty `Array` when `nil` is passed as argument
155
+ - [Luca Guidi] Use composition over inheritance for `Lotus::Utils::Callbacks::Chain`
156
+
157
+ ## v0.1.1 - 2014-04-23
158
+ ### Added
159
+ - [Luca Guidi] Implemented `Lotus::Utils::Kernel.Time`
160
+ - [Luca Guidi] Implemented `Lotus::Utils::Kernel.DateTime`
161
+ - [Luca Guidi] Implemented `Lotus::Utils::Kernel.Date`
162
+ - [Luca Guidi] Implemented `Lotus::Utils::Kernel.Float`
163
+ - [Luca Guidi] Implemented `Lotus::Utils::Kernel.Boolean`
164
+ - [Luca Guidi] Implemented `Lotus::Utils::Kernel.Hash`
165
+ - [Luca Guidi] Implemented `Lotus::Utils::Kernel.Set`
166
+ - [Luca Guidi] Implemented `Lotus::Utils::Kernel.String`
167
+ - [Luca Guidi] Implemented `Lotus::Utils::Kernel.Integer`
168
+ - [Luca Guidi] Implemented `Lotus::Utils::Kernel.Array`
169
+
170
+ ### Fixed
171
+ - [Christopher Keele] Add missing stdlib `Set` require to `Utils::ClassAttribute`
172
+
173
+ ## v0.1.0 - 2014-01-23
174
+ ### Added
175
+ - [Luca Guidi] Introduced `Lotus::Utils::String#demodulize`
176
+ - [Luca Guidi] Introduced `Lotus::Utils::IO.silence_warnings`
177
+ - [Luca Guidi] Introduced class loading mechanism from a string: `Utils::Class.load!`
178
+ - [Luca Guidi] Introduced callbacks support for classes
179
+ - [Luca Guidi] Introduced inheritable class level attributes
180
+ - [Luca Guidi] Introduced `Utils::Hash`
181
+ - [Luca Guidi] Introduced `Utils::String`
182
+ - [Luca Guidi] Introduced `Utils::PathPrefix`
183
+ - [Luca Guidi] Official support for MRI 2.0+
@@ -0,0 +1,22 @@
1
+ Copyright © 2014-2016 Luca Guidi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,8 +1,28 @@
1
1
  # Hanami::Utils
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/hanami/utils`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ Ruby core extentions and class utilities for [Hanami](http://hanamirb.org)
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ ## Status
6
+
7
+ [![Gem Version](http://img.shields.io/gem/v/hanami-utils.svg)](https://badge.fury.io/rb/hanami-utils)
8
+ [![Build Status](http://img.shields.io/travis/hanami/utils/master.svg)](https://travis-ci.org/hanami/utils?branch=master)
9
+ [![Coverage](http://img.shields.io/coveralls/hanami/utils/master.svg)](https://coveralls.io/r/hanami/utils)
10
+ [![Code Climate](http://img.shields.io/codeclimate/github/hanami/utils.svg)](https://codeclimate.com/github/hanami/utils)
11
+ [![Dependencies](http://img.shields.io/gemnasium/hanami/utils.svg)](https://gemnasium.com/hanami/utils)
12
+ [![Inline Docs](http://inch-ci.org/github/hanami/utils.svg)](http://inch-ci.org/github/hanami/utils)
13
+
14
+ ## Contact
15
+
16
+ * Home page: http://hanamirb.org
17
+ * Mailing List: http://hanamirb.org/mailing-list
18
+ * API Doc: http://rdoc.info/gems/hanami-utils
19
+ * Bugs/Issues: https://github.com/hanami/utils/issues
20
+ * Support: http://stackoverflow.com/questions/tagged/hanami
21
+ * Chat: http://chat.hanamirb.org
22
+
23
+ ## Rubies
24
+
25
+ __Hanami::Utils__ supports Ruby (MRI) 2.2+, JRuby 9k+
6
26
 
7
27
  ## Installation
8
28
 
@@ -22,15 +42,94 @@ Or install it yourself as:
22
42
 
23
43
  ## Usage
24
44
 
25
- TODO: Write usage instructions here
45
+ __Hanami::Utils__ is designed to enhance Ruby's code and stdlib.
46
+ **By default this gem doesn't load any code, you must require what you need.**
47
+
48
+ ## Features
49
+
50
+ ### Hanami::Interactor
51
+
52
+ Standardized Service Object with small interface and rich returning result. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Interactor)]
53
+
54
+ ### Hanami::Logger
55
+
56
+ Enhanced version of Ruby's `Logger`. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Logger)]
57
+
58
+ ### Hanami::Utils::Attributes
59
+
60
+ Set of attributes with indifferent access. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/Attributes)]
61
+
62
+ ### Hanami::Utils::BasicObject
63
+
64
+ Enhanced version of Ruby's `BasicObject`. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/BasicObject)]
65
+
66
+ ### Hanami::Utils::Callbacks
67
+
68
+ Callbacks to decorate methods with `before` and `after` logic. It supports polymorphic callbacks (methods and procs). [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/Callbacks)]
69
+
70
+ ### Hanami::Utils::Class
71
+
72
+ Load classes from strings. It also supports namespaces. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/Class)]
73
+
74
+ ### Hanami::Utils::ClassAttribute
75
+
76
+ Inheritable class attributes. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/ClassAttribute)]
77
+
78
+ ### Hanami::Utils::Deprecation
26
79
 
27
- ## Development
80
+ Deprecate Hanami features. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/Deprecation)]
28
81
 
29
- After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
82
+ ### Hanami::Utils::Duplicable
30
83
 
31
- 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).
84
+ Safe `#dup` logic for Ruby objects. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/Deprecation)]
85
+
86
+
87
+ ### Hanami::Utils::Escape
88
+
89
+ Safe and fast escape for URLs, HTML content and attributes. Based on OWASP/ESAPI code. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/Escape)]
90
+
91
+ ### Hanami::Utils::Hash
92
+
93
+ Enhanced version of Ruby's `Hash`. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/Hash)]
94
+
95
+ ### Hanami::Utils::IO
96
+
97
+ Silence Ruby warnings. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/IO)]
98
+
99
+ ### Hanami::Utils::Inflector
100
+
101
+ Complete and customizable english inflections (pluralization and singularization). [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/Inflector)]
102
+
103
+ ### Hanami::Utils::Kernel
104
+
105
+ Type coercions for most common Ruby types. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/Kernel)]
106
+
107
+ ### Hanami::Utils::LoadPaths
108
+
109
+ Manage directories where to find Ruby source code or web static assets. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/LoadPaths)]
110
+
111
+ ### Hanami::Utils::PathPrefix
112
+
113
+ Safe logic to manage relative URLs. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/PathPrefix)]
114
+
115
+ ### Hanami::Utils::String
116
+
117
+ Enhanced version of Ruby's `String`. [[API doc](http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/String)]
118
+
119
+ ## Versioning
120
+
121
+ __Hanami::Utils__ uses [Semantic Versioning 2.0.0](http://semver.org)
32
122
 
33
123
  ## Contributing
34
124
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/hanami-utils.
125
+ 1. Fork it ( https://github.com/hanami/utils/fork )
126
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
127
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
128
+ 4. Push to the branch (`git push origin my-new-feature`)
129
+ 5. Create new Pull Request
130
+
131
+ ## Copyright
132
+
133
+ Copyright © 2014-2016 Luca Guidi – Released under MIT License
36
134
 
135
+ This project was formerly known as Lotus (`lotus-utils`).
@@ -4,20 +4,22 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'hanami/utils/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "hanami-utils"
7
+ spec.name = 'hanami-utils'
8
8
  spec.version = Hanami::Utils::VERSION
9
- spec.authors = ["Luca Guidi"]
10
- spec.email = ["me@lucaguidi.com"]
9
+ spec.authors = ['Luca Guidi', 'Trung Lê', 'Alfonso Uceda']
10
+ spec.email = ['me@lucaguidi.com', 'trung.le@ruby-journal.com', 'uceda73@gmail.com']
11
+ spec.description = %q{Hanami utilities}
12
+ spec.summary = %q{Ruby core extentions and Hanami utilities}
13
+ spec.homepage = 'http://hanamirb.org'
14
+ spec.license = 'MIT'
11
15
 
12
- spec.summary = %q{The web, with simplicity}
13
- spec.description = %q{Hanami is a web framework for Ruby}
14
- spec.homepage = "http://hanamirb.org"
16
+ spec.files = `git ls-files -- lib/* CHANGELOG.md LICENSE.md README.md hanami-utils.gemspec`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+ spec.required_ruby_version = '>= 2.0.0'
15
21
 
16
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
- spec.bindir = "exe"
18
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
- spec.require_paths = ["lib"]
20
-
21
- spec.add_development_dependency "bundler", "~> 1.11"
22
- spec.add_development_dependency "rake", "~> 10.0"
22
+ spec.add_development_dependency 'bundler', '~> 1.6'
23
+ spec.add_development_dependency 'rake', '~> 10'
24
+ spec.add_development_dependency 'minitest', '~> 5.4'
23
25
  end
@@ -0,0 +1 @@
1
+ require 'hanami/utils'
@@ -0,0 +1,497 @@
1
+ require 'hanami/utils/basic_object'
2
+ require 'hanami/utils/class_attribute'
3
+ require 'hanami/utils/hash'
4
+
5
+ module Hanami
6
+ # Hanami Interactor
7
+ #
8
+ # @since 0.3.5
9
+ module Interactor
10
+ # Result of an operation
11
+ #
12
+ # @since 0.3.5
13
+ class Result < Utils::BasicObject
14
+ # Concrete methods
15
+ #
16
+ # @since 0.3.5
17
+ # @api private
18
+ #
19
+ # @see Hanami::Interactor::Result#respond_to_missing?
20
+ METHODS = {initialize: true, success?: true, fail!: true, prepare!: true, errors: true, error: true}.freeze
21
+
22
+ # Initialize a new result
23
+ #
24
+ # @param payload [Hash] a payload to carry on
25
+ #
26
+ # @return [Hanami::Interactor::Result]
27
+ #
28
+ # @since 0.3.5
29
+ # @api private
30
+ def initialize(payload = {})
31
+ @payload = _payload(payload)
32
+ @errors = []
33
+ @success = true
34
+ end
35
+
36
+ # Check if the current status is successful
37
+ #
38
+ # @return [TrueClass,FalseClass] the result of the check
39
+ #
40
+ # @since 0.3.5
41
+ def success?
42
+ @success && errors.empty?
43
+ end
44
+
45
+ # Force the status to be a failure
46
+ #
47
+ # @since 0.3.5
48
+ def fail!
49
+ @success = false
50
+ end
51
+
52
+ # Returns all the errors collected during an operation
53
+ #
54
+ # @return [Array] the errors
55
+ #
56
+ # @since 0.3.5
57
+ #
58
+ # @see Hanami::Interactor::Result#error
59
+ # @see Hanami::Interactor#call
60
+ # @see Hanami::Interactor#error
61
+ # @see Hanami::Interactor#error!
62
+ def errors
63
+ @errors.dup
64
+ end
65
+
66
+ # @since 0.5.0
67
+ # @api private
68
+ def add_error(*errors)
69
+ @errors << errors
70
+ @errors.flatten!
71
+ nil
72
+ end
73
+
74
+ # Returns the first errors collected during an operation
75
+ #
76
+ # @return [nil,String] the error, if present
77
+ #
78
+ # @since 0.3.5
79
+ #
80
+ # @see Hanami::Interactor::Result#errors
81
+ # @see Hanami::Interactor#call
82
+ # @see Hanami::Interactor#error
83
+ # @see Hanami::Interactor#error!
84
+ def error
85
+ errors.first
86
+ end
87
+
88
+ # Prepare the result before to be returned
89
+ #
90
+ # @param payload [Hash] an updated payload
91
+ #
92
+ # @since 0.3.5
93
+ # @api private
94
+ def prepare!(payload)
95
+ @payload.merge!(_payload(payload))
96
+ self
97
+ end
98
+
99
+ protected
100
+ # @since 0.3.5
101
+ # @api private
102
+ def method_missing(m, *)
103
+ @payload.fetch(m) { super }
104
+ end
105
+
106
+ # @since 0.3.5
107
+ # @api private
108
+ def respond_to_missing?(method_name, include_all)
109
+ method_name = method_name.to_sym
110
+ METHODS[method_name] || @payload.key?(method_name)
111
+ end
112
+
113
+ # @since 0.3.5
114
+ # @api private
115
+ def _payload(payload)
116
+ Utils::Hash.new(payload).symbolize!
117
+ end
118
+
119
+ # @since 0.3.5
120
+ # @api private
121
+ def __inspect
122
+ " @success=#{ @success } @payload=#{ @payload.inspect }"
123
+ end
124
+ end
125
+
126
+ # Override for <tt>Module#included</tt>.
127
+ #
128
+ # @since 0.3.5
129
+ # @api private
130
+ def self.included(base)
131
+ super
132
+
133
+ base.class_eval do
134
+ prepend Interface
135
+ extend ClassMethods
136
+ end
137
+ end
138
+
139
+ # Interactor interface
140
+ #
141
+ # @since 0.3.5
142
+ module Interface
143
+ # Initialize an interactor
144
+ #
145
+ # It accepts arbitrary number of arguments.
146
+ # Developers can override it.
147
+ #
148
+ # @param args [Array<Object>] arbitrary number of arguments
149
+ #
150
+ # @return [Hanami::Interactor] the interactor
151
+ #
152
+ # @since 0.3.5
153
+ #
154
+ # @example Override #initialize
155
+ # require 'hanami/interactor'
156
+ #
157
+ # class UpdateProfile
158
+ # include Hanami::Interactor
159
+ #
160
+ # def initialize(user, params)
161
+ # @user = user
162
+ # @params = params
163
+ # end
164
+ #
165
+ # def call
166
+ # # ...
167
+ # end
168
+ # end
169
+ def initialize(*args)
170
+ super
171
+ ensure
172
+ @__result = ::Hanami::Interactor::Result.new
173
+ end
174
+
175
+ # Triggers the operation and return a result.
176
+ #
177
+ # All the instance variables will be available in the result.
178
+ #
179
+ # ATTENTION: This must be implemented by the including class.
180
+ #
181
+ # @return [Hanami::Interactor::Result] the result of the operation
182
+ #
183
+ # @raise [NoMethodError] if this isn't implemented by the including class.
184
+ #
185
+ # @example Expose instance variables in result payload
186
+ # require 'hanami/interactor'
187
+ #
188
+ # class Signup
189
+ # include Hanami::Interactor
190
+ # expose :user, :params
191
+ #
192
+ # def initialize(params)
193
+ # @params = params
194
+ # @user = User.new(@params)
195
+ # @foo = 'bar'
196
+ # end
197
+ #
198
+ # def call
199
+ # @user = UserRepository.persist(@user)
200
+ # end
201
+ # end
202
+ #
203
+ # result = Signup.new(name: 'Luca').call
204
+ # result.success? # => true
205
+ #
206
+ # result.user # => #<User:0x007fa311105778 @id=1 @name="Luca">
207
+ # result.params # => { :name=>"Luca" }
208
+ # result.foo # => raises NoMethodError
209
+ #
210
+ # @example Failed precondition
211
+ # require 'hanami/interactor'
212
+ #
213
+ # class Signup
214
+ # include Hanami::Interactor
215
+ # expose :user
216
+ #
217
+ # def initialize(params)
218
+ # @params = params
219
+ # @user = User.new(@params)
220
+ # end
221
+ #
222
+ # # THIS WON'T BE INVOKED BECAUSE #valid? WILL RETURN false
223
+ # def call
224
+ # @user = UserRepository.persist(@user)
225
+ # end
226
+ #
227
+ # private
228
+ # def valid?
229
+ # @params.valid?
230
+ # end
231
+ # end
232
+ #
233
+ # result = Signup.new(name: nil).call
234
+ # result.success? # => false
235
+ #
236
+ # result.user # => #<User:0x007fa311105778 @id=nil @name="Luca">
237
+ #
238
+ # @example Bad usage
239
+ # require 'hanami/interactor'
240
+ #
241
+ # class Signup
242
+ # include Hanami::Interactor
243
+ #
244
+ # # Method #call is not defined
245
+ # end
246
+ #
247
+ # Signup.new.call # => NoMethodError
248
+ def call
249
+ _call { super }
250
+ end
251
+ end
252
+
253
+ private
254
+ # Check if proceed with <tt>#call</tt> invokation.
255
+ # By default it returns <tt>true</tt>.
256
+ #
257
+ # Developers can override it.
258
+ #
259
+ # @return [TrueClass,FalseClass] the result of the check
260
+ #
261
+ # @since 0.3.5
262
+ def valid?
263
+ true
264
+ end
265
+
266
+ # Fail and interrupt the current flow.
267
+ #
268
+ # @since 0.3.5
269
+ #
270
+ # @example
271
+ # require 'hanami/interactor'
272
+ #
273
+ # class CreateEmailTest
274
+ # include Hanami::Interactor
275
+ #
276
+ # def initialize(params)
277
+ # @params = params
278
+ # @email_test = EmailTest.new(@params)
279
+ # end
280
+ #
281
+ # def call
282
+ # persist_email_test!
283
+ # capture_screenshot!
284
+ # end
285
+ #
286
+ # private
287
+ # def persist_email_test!
288
+ # @email_test = EmailTestRepository.persist(@email_test)
289
+ # end
290
+ #
291
+ # # IF THIS RAISES AN EXCEPTION WE FORCE A FAILURE
292
+ # def capture_screenshot!
293
+ # Screenshot.new(@email_test).capture!
294
+ # rescue
295
+ # fail!
296
+ # end
297
+ # end
298
+ #
299
+ # result = CreateEmailTest.new(account_id: 1).call
300
+ # result.success? # => false
301
+ def fail!
302
+ @__result.fail!
303
+ throw :fail
304
+ end
305
+
306
+ # Log an error without interrupting the flow.
307
+ #
308
+ # When used, the returned result won't be successful.
309
+ #
310
+ # @param message [String] the error message
311
+ #
312
+ # @return false
313
+ #
314
+ # @since 0.3.5
315
+ #
316
+ # @see Hanami::Interactor#error!
317
+ #
318
+ # @example
319
+ # require 'hanami/interactor'
320
+ #
321
+ # class CreateRecord
322
+ # include Hanami::Interactor
323
+ # expose :logger
324
+ #
325
+ # def initialize
326
+ # @logger = []
327
+ # end
328
+ #
329
+ # def call
330
+ # prepare_data!
331
+ # persist!
332
+ # sync!
333
+ # end
334
+ #
335
+ # private
336
+ # def prepare_data!
337
+ # @logger << __method__
338
+ # error "Prepare data error"
339
+ # end
340
+ #
341
+ # def persist!
342
+ # @logger << __method__
343
+ # error "Persist error"
344
+ # end
345
+ #
346
+ # def sync!
347
+ # @logger << __method__
348
+ # end
349
+ # end
350
+ #
351
+ # result = CreateRecord.new.call
352
+ # result.success? # => false
353
+ #
354
+ # result.errors # => ["Prepare data error", "Persist error"]
355
+ # result.logger # => [:prepare_data!, :persist!, :sync!]
356
+ def error(message)
357
+ @__result.add_error message
358
+ false
359
+ end
360
+
361
+ # Log an error AND interrupting the flow.
362
+ #
363
+ # When used, the returned result won't be successful.
364
+ #
365
+ # @param message [String] the error message
366
+ #
367
+ # @since 0.3.5
368
+ #
369
+ # @see Hanami::Interactor#error
370
+ #
371
+ # @example
372
+ # require 'hanami/interactor'
373
+ #
374
+ # class CreateRecord
375
+ # include Hanami::Interactor
376
+ # expose :logger
377
+ #
378
+ # def initialize
379
+ # @logger = []
380
+ # end
381
+ #
382
+ # def call
383
+ # prepare_data!
384
+ # persist!
385
+ # sync!
386
+ # end
387
+ #
388
+ # private
389
+ # def prepare_data!
390
+ # @logger << __method__
391
+ # error "Prepare data error"
392
+ # end
393
+ #
394
+ # def persist!
395
+ # @logger << __method__
396
+ # error! "Persist error"
397
+ # end
398
+ #
399
+ # # THIS WILL NEVER BE INVOKED BECAUSE WE USE #error! IN #persist!
400
+ # def sync!
401
+ # @logger << __method__
402
+ # end
403
+ # end
404
+ #
405
+ # result = CreateRecord.new.call
406
+ # result.success? # => false
407
+ #
408
+ # result.errors # => ["Prepare data error", "Persist error"]
409
+ # result.logger # => [:prepare_data!, :persist!]
410
+ def error!(message)
411
+ error(message)
412
+ fail!
413
+ end
414
+
415
+ # @since 0.3.5
416
+ # @api private
417
+ def _call
418
+ catch :fail do
419
+ validate!
420
+ yield
421
+ end
422
+
423
+ _prepare!
424
+ end
425
+
426
+ # @since 0.3.5
427
+ def validate!
428
+ fail! unless valid?
429
+ end
430
+
431
+ # @since 0.3.5
432
+ # @api private
433
+ def _prepare!
434
+ @__result.prepare!(_exposures)
435
+ end
436
+
437
+ # @since 0.5.0
438
+ # @api private
439
+ def _exposures
440
+ Hash[].tap do |result|
441
+ self.class.exposures.each do |name, ivar|
442
+ result[name] = instance_variable_get(ivar)
443
+ end
444
+ end
445
+ end
446
+ end
447
+
448
+ # @since 0.5.0
449
+ # @api private
450
+ module ClassMethods
451
+ # @since 0.5.0
452
+ # @api private
453
+ def self.extended(interactor)
454
+ interactor.class_eval do
455
+ include Utils::ClassAttribute
456
+
457
+ class_attribute :exposures
458
+ self.exposures = Utils::Hash.new
459
+ end
460
+ end
461
+
462
+ # Expose local instance variables into the returing value of <tt>#call</tt>
463
+ #
464
+ # @param instance_variable_names [Symbol,Array<Symbol>] one or more instance
465
+ # variable names
466
+ #
467
+ # @since 0.5.0
468
+ #
469
+ # @see Hanami::Interactor::Result
470
+ #
471
+ # @example Expose instance variable
472
+ #
473
+ # class Signup
474
+ # include Hanami::Interactor
475
+ # expose :user
476
+ #
477
+ # def initialize(params)
478
+ # @params = params
479
+ # @user = User.new(@params[:user])
480
+ # end
481
+ #
482
+ # def call
483
+ # # ...
484
+ # end
485
+ # end
486
+ #
487
+ # result = Signup.new(user: { name: "Luca" }).call
488
+ #
489
+ # result.user # => #<User:0x007fa85c58ccd8 @name="Luca">
490
+ # result.params # => NoMethodError
491
+ def expose(*instance_variable_names)
492
+ instance_variable_names.each do |name|
493
+ exposures[name] = "@#{ name }"
494
+ end
495
+ end
496
+ end
497
+ end