lotus-utils 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,113 @@
1
+ module Lotus
2
+ module Utils
3
+ # String on steroids
4
+ #
5
+ # @since 0.1.0
6
+ class String < ::String
7
+ # Separator between Ruby namespaces
8
+ #
9
+ # @example
10
+ # Lotus::Utils
11
+ #
12
+ # @since 0.1.0
13
+ # @api private
14
+ NAMESPACE_SEPARATOR = '::'.freeze
15
+
16
+ # Initialize the string
17
+ #
18
+ # @param string [::String, Symbol] the value we want to initialize
19
+ #
20
+ # @return [String] self
21
+ #
22
+ # @since 0.1.0
23
+ def initialize(string)
24
+ super(string.to_s)
25
+ end
26
+
27
+ # Return a CamelCase version of the string
28
+ #
29
+ # @return [String] the transformed string
30
+ #
31
+ # @since 0.1.0
32
+ #
33
+ # @example
34
+ # require 'lotus/utils/string'
35
+ #
36
+ # string = Lotus::Utils::String.new 'lotus_utils'
37
+ # string.classify # => 'LotusUtils'
38
+ def classify
39
+ split('_').map {|token| token.slice(0).upcase + token.slice(1..-1) }.join
40
+ end
41
+
42
+ # Return a downcased and underscore separated version of the string
43
+ #
44
+ # Revised version of `ActiveSupport::Inflector.underscore` implementation
45
+ # @see https://github.com/rails/rails/blob/feaa6e2048fe86bcf07e967d6e47b865e42e055b/activesupport/lib/active_support/inflector/methods.rb#L90
46
+ #
47
+ # @return [String] the transformed string
48
+ #
49
+ # @since 0.1.0
50
+ #
51
+ # @example
52
+ # require 'lotus/utils/string'
53
+ #
54
+ # string = Lotus::Utils::String.new 'LotusUtils'
55
+ # string.underscore # => 'lotus_utils'
56
+ def underscore
57
+ gsub(NAMESPACE_SEPARATOR, '/').
58
+ gsub(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2').
59
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
60
+ downcase
61
+ end
62
+
63
+ # Return the string without the Ruby namespace of the class
64
+ #
65
+ # @return [String] the transformed string
66
+ #
67
+ # @since 0.1.0
68
+ #
69
+ # @example
70
+ # require 'lotus/utils/string'
71
+ #
72
+ # string = Lotus::Utils::String.new 'Lotus::Utils::String'
73
+ # string.demodulize # => 'String'
74
+ #
75
+ # string = Lotus::Utils::String.new 'String'
76
+ # string.demodulize # => 'String'
77
+ def demodulize
78
+ split(NAMESPACE_SEPARATOR).last
79
+ end
80
+
81
+ # It iterates thru the tokens and calls the given block.
82
+ # A token is a substring wrapped by `()` and separated by `|`.
83
+ #
84
+ # @param blk [Proc, #call] the block that is called for each token.
85
+ #
86
+ # @return [void]
87
+ #
88
+ # @since 0.1.0
89
+ #
90
+ # @example
91
+ # require 'lotus/utils/string'
92
+ #
93
+ # string = Lotus::Utils::String.new 'Lotus::(Utils|App)'
94
+ # string.tokenize do |token|
95
+ # puts token
96
+ # end
97
+ #
98
+ # # =>
99
+ # 'Lotus::Utils'
100
+ # 'Lotus::App'
101
+ def tokenize(&blk)
102
+ template = gsub(/\((.*)\)/, "%{token}")
103
+ tokens = Array(( $1 || self ).split('|'))
104
+
105
+ tokens.each do |token|
106
+ blk.call(template % {token: token})
107
+ end
108
+
109
+ nil
110
+ end
111
+ end
112
+ end
113
+ end
@@ -1,5 +1,5 @@
1
1
  module Lotus
2
2
  module Utils
3
- VERSION = "0.0.0"
3
+ VERSION = '0.1.0'
4
4
  end
5
5
  end
data/lib/lotus/utils.rb CHANGED
@@ -1,7 +1,7 @@
1
- require "lotus/utils/version"
1
+ require 'lotus/utils/version'
2
2
 
3
3
  module Lotus
4
+ # Ruby core extentions and Louts utilities
4
5
  module Utils
5
- # Your code goes here...
6
6
  end
7
7
  end
data/lotus-utils.gemspec CHANGED
@@ -4,20 +4,20 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'lotus/utils/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "lotus-utils"
7
+ spec.name = 'lotus-utils'
8
8
  spec.version = Lotus::Utils::VERSION
9
- spec.authors = ["Luca Guidi"]
10
- spec.email = ["me@lucaguidi.com"]
11
- spec.summary = %q{Ruby core extentions and class utilities for Lotus}
12
- spec.description = %q{Ruby core extentions and class utilities for Lotus}
13
- spec.homepage = ""
14
- spec.license = "MIT"
9
+ spec.authors = ['Luca Guidi']
10
+ spec.email = ['me@lucaguidi.com']
11
+ spec.description = %q{Lotus utilities}
12
+ spec.summary = %q{Ruby core extentions and Louts utilities}
13
+ spec.homepage = 'http://lotusrb.org'
14
+ spec.license = 'MIT'
15
15
 
16
16
  spec.files = `git ls-files`.split($/)
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
19
+ spec.require_paths = ['lib']
20
20
 
21
- spec.add_development_dependency "bundler", "~> 1.5"
22
- spec.add_development_dependency "rake"
21
+ spec.add_development_dependency 'bundler', '~> 1.5'
22
+ spec.add_development_dependency 'rake', '~> 10'
23
23
  end
@@ -0,0 +1,213 @@
1
+ require 'test_helper'
2
+ require 'lotus/utils/callbacks'
3
+
4
+ class Callable
5
+ def call
6
+ end
7
+ end
8
+
9
+ class Action
10
+ attr_reader :logger
11
+
12
+ def initialize
13
+ @logger = Array.new
14
+ end
15
+
16
+ private
17
+ def authenticate!
18
+ logger.push 'authenticate!'
19
+ end
20
+
21
+ def set_article(params)
22
+ logger.push "set_article: #{ params[:id] }"
23
+ end
24
+ end
25
+
26
+ describe Lotus::Utils::Callbacks::Chain do
27
+ before do
28
+ @chain = Lotus::Utils::Callbacks::Chain.new
29
+ end
30
+
31
+ describe '#add' do
32
+ it 'wraps the given callback with a callable object' do
33
+ @chain.add :symbolize!
34
+
35
+ cb = @chain.first
36
+ cb.must_respond_to(:call)
37
+ end
38
+
39
+ describe 'when a callable object is passed' do
40
+ before do
41
+ @chain.add callback
42
+ end
43
+
44
+ let(:callback) { Callable.new }
45
+
46
+ it 'includes the given callback' do
47
+ cb = @chain.first
48
+ cb.callback.must_equal(callback)
49
+ end
50
+ end
51
+
52
+ describe 'when a Symbol is passed' do
53
+ before do
54
+ @chain.add callback
55
+ end
56
+
57
+ let(:callback) { :upcase }
58
+
59
+ it 'includes the given callback' do
60
+ cb = @chain.first
61
+ cb.callback.must_equal(callback)
62
+ end
63
+
64
+ it 'guarantees unique entries' do
65
+ # add the callback again, see before block
66
+ @chain.add callback
67
+ @chain.size.must_equal(1)
68
+ end
69
+ end
70
+
71
+ describe 'when a block is passed' do
72
+ before do
73
+ @chain.add(&callback)
74
+ end
75
+
76
+ let(:callback) { Proc.new{} }
77
+
78
+ it 'includes the given callback' do
79
+ cb = @chain.first
80
+ assert_equal cb.callback, callback
81
+ end
82
+ end
83
+
84
+ describe 'when multiple callbacks are passed' do
85
+ before do
86
+ @chain.add *callbacks
87
+ end
88
+
89
+ let(:callbacks) { [:upcase, Callable.new, Proc.new{}] }
90
+
91
+ it 'includes all the given callbacks' do
92
+ @chain.size.must_equal(callbacks.size)
93
+ end
94
+
95
+ it 'all the included callbacks are callable' do
96
+ @chain.each do |callback|
97
+ callback.must_respond_to(:call)
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ describe '#run' do
104
+ let(:action) { Action.new }
105
+ let(:params) { Hash[id: 23] }
106
+
107
+ describe 'when symbols are passed' do
108
+ before do
109
+ @chain.add :authenticate!, :set_article
110
+ @chain.run action, params
111
+ end
112
+
113
+ it 'executes the callbacks' do
114
+ authenticate = action.logger.shift
115
+ authenticate.must_equal 'authenticate!'
116
+
117
+ set_article = action.logger.shift
118
+ set_article.must_equal "set_article: #{ params[:id] }"
119
+ end
120
+ end
121
+
122
+ describe 'when procs are passed' do
123
+ before do
124
+ @chain.add do
125
+ logger.push 'authenticate!'
126
+ end
127
+
128
+ @chain.add do |params|
129
+ logger.push "set_article: #{ params[:id] }"
130
+ end
131
+
132
+ @chain.run action, params
133
+ end
134
+
135
+ it 'executes the callbacks' do
136
+ authenticate = action.logger.shift
137
+ authenticate.must_equal 'authenticate!'
138
+
139
+ set_article = action.logger.shift
140
+ set_article.must_equal "set_article: #{ params[:id] }"
141
+ end
142
+ end
143
+ end
144
+ end
145
+
146
+ describe Lotus::Utils::Callbacks::Factory do
147
+ describe '.fabricate' do
148
+ before do
149
+ @callback = Lotus::Utils::Callbacks::Factory.fabricate(callback)
150
+ end
151
+
152
+ describe 'when a callable is passed' do
153
+ let(:callback) { Callable.new }
154
+
155
+ it 'fabricates a Callback' do
156
+ @callback.must_be_kind_of(Lotus::Utils::Callbacks::Callback)
157
+ end
158
+
159
+ it 'wraps the given callback' do
160
+ @callback.callback.must_equal(callback)
161
+ end
162
+ end
163
+
164
+ describe 'when a symbol is passed' do
165
+ let(:callback) { :symbolize! }
166
+
167
+ it 'fabricates a MethodCallback' do
168
+ @callback.must_be_kind_of(Lotus::Utils::Callbacks::MethodCallback)
169
+ end
170
+
171
+ it 'wraps the given callback' do
172
+ @callback.callback.must_equal(callback)
173
+ end
174
+ end
175
+ end
176
+ end
177
+
178
+ describe Lotus::Utils::Callbacks::Callback do
179
+ before do
180
+ @callback = Lotus::Utils::Callbacks::Callback.new(callback)
181
+ end
182
+
183
+ let(:callback) { Proc.new{|params| logger.push("set_article: #{ params[:id] }") } }
184
+
185
+ it 'executes self within the given context' do
186
+ context = Action.new
187
+ @callback.call(context, { id: 23 })
188
+
189
+ invokation = context.logger.shift
190
+ invokation.must_equal("set_article: 23")
191
+ end
192
+ end
193
+
194
+ describe Lotus::Utils::Callbacks::MethodCallback do
195
+ before do
196
+ @callback = Lotus::Utils::Callbacks::MethodCallback.new(callback)
197
+ end
198
+
199
+ let(:callback) { :set_article }
200
+
201
+ it 'executes self within the given context' do
202
+ context = Action.new
203
+ @callback.call(context, { id: 23 })
204
+
205
+ invokation = context.logger.shift
206
+ invokation.must_equal("set_article: 23")
207
+ end
208
+
209
+ it 'implements #hash' do
210
+ cb = Lotus::Utils::Callbacks::MethodCallback.new(callback)
211
+ cb.send(:hash).must_equal(@callback.send(:hash))
212
+ end
213
+ end
@@ -0,0 +1,132 @@
1
+ require 'test_helper'
2
+ require 'lotus/utils/class_attribute'
3
+
4
+ describe Lotus::Utils::ClassAttribute do
5
+ before do
6
+ class ClassAttributeTest
7
+ include Lotus::Utils::ClassAttribute
8
+ class_attribute :callbacks, :functions, :values
9
+ self.callbacks = [:a]
10
+ self.values = [1]
11
+ end
12
+
13
+ class SubclassAttributeTest < ClassAttributeTest
14
+ class_attribute :subattribute
15
+ self.functions = [:x, :y]
16
+ self.subattribute = 42
17
+ end
18
+
19
+ class SubSubclassAttributeTest < SubclassAttributeTest
20
+ end
21
+
22
+ class Vehicle
23
+ include Lotus::Utils::ClassAttribute
24
+ class_attribute :engines, :wheels
25
+
26
+ self.engines = 0
27
+ self.wheels = 0
28
+ end
29
+
30
+ class Car < Vehicle
31
+ self.engines = 1
32
+ self.wheels = 4
33
+ end
34
+
35
+ class Airplane < Vehicle
36
+ self.engines = 4
37
+ self.wheels = 16
38
+ end
39
+
40
+ class SmallAirplane < Airplane
41
+ self.engines = 2
42
+ self.wheels = 8
43
+ end
44
+ end
45
+
46
+ after do
47
+ [:ClassAttributeTest,
48
+ :SubclassAttributeTest,
49
+ :SubSubclassAttributeTest,
50
+ :Vehicle,
51
+ :Car,
52
+ :Airplane,
53
+ :SmallAirplane].each do |const|
54
+ Object.send :remove_const, const
55
+ end
56
+ end
57
+
58
+ it 'sets the given value' do
59
+ ClassAttributeTest.callbacks.must_equal([:a])
60
+ end
61
+
62
+ it 'the value it is inherited by subclasses' do
63
+ SubclassAttributeTest.callbacks.must_equal([:a])
64
+ end
65
+
66
+ it 'if the superclass value changes it does not affects subclasses' do
67
+ ClassAttributeTest.functions = [:y]
68
+ SubclassAttributeTest.functions.must_equal([:x, :y])
69
+ end
70
+
71
+ it 'if the subclass value changes it does not affects superclass' do
72
+ SubclassAttributeTest.values = [3,2]
73
+ ClassAttributeTest.values.must_equal([1])
74
+ end
75
+
76
+ describe 'when the subclass is defined in a different namespace' do
77
+ before do
78
+ module Lts
79
+ module Routing
80
+ class Resource
81
+ class Action
82
+ include Lotus::Utils::ClassAttribute
83
+ class_attribute :verb
84
+ end
85
+
86
+ class New < Action
87
+ self.verb = :get
88
+ end
89
+ end
90
+
91
+ class Resources < Resource
92
+ class New < Resource::New
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ it 'refers to the superclass value' do
100
+ Lts::Routing::Resources::New.verb.must_equal :get
101
+ end
102
+ end
103
+
104
+ # it 'if the subclass value changes it affects subclasses' do
105
+ # values = [3,2]
106
+ # SubclassAttributeTest.values = values
107
+ # SubclassAttributeTest.values.must_equal(values)
108
+ # SubSubclassAttributeTest.values.must_equal(values)
109
+ # end
110
+
111
+ it 'if the subclass defines an attribute it should not be available for the superclass' do
112
+ -> { ClassAttributeTest.subattribute }.must_raise(NoMethodError)
113
+ end
114
+
115
+ it 'if the subclass defines an attribute it should be available for its subclasses' do
116
+ SubSubclassAttributeTest.subattribute.must_equal 42
117
+ end
118
+
119
+ it 'preserves values within the inheritance chain' do
120
+ Vehicle.engines.must_equal 0
121
+ Vehicle.wheels.must_equal 0
122
+
123
+ Car.engines.must_equal 1
124
+ Car.wheels.must_equal 4
125
+
126
+ Airplane.engines.must_equal 4
127
+ Airplane.wheels.must_equal 16
128
+
129
+ SmallAirplane.engines.must_equal 2
130
+ SmallAirplane.wheels.must_equal 8
131
+ end
132
+ end
@@ -0,0 +1,47 @@
1
+ require 'test_helper'
2
+ require 'lotus/utils/class'
3
+
4
+ describe Lotus::Utils::Class do
5
+ describe '.load!' do
6
+ before do
7
+ module App
8
+ module Layer
9
+ class Step
10
+ end
11
+ end
12
+
13
+ module Service
14
+ class Point
15
+ end
16
+ end
17
+
18
+ class ServicePoint
19
+ end
20
+ end
21
+ end
22
+
23
+ it 'loads the class from the given static string' do
24
+ Lotus::Utils::Class.load!('App::Layer::Step').must_equal(App::Layer::Step)
25
+ end
26
+
27
+ it 'raises error for missing constant' do
28
+ -> { Lotus::Utils::Class.load!('MissingConstant') }.must_raise(NameError)
29
+ end
30
+
31
+ it 'loads the class from given string, by interpolating tokens' do
32
+ Lotus::Utils::Class.load!('App::Service(::Point|Point)').must_equal(App::Service::Point)
33
+ end
34
+
35
+ it 'loads the class from given string, by interpolating string tokens and respecting their order' do
36
+ Lotus::Utils::Class.load!('App::Service(Point|::Point)').must_equal(App::ServicePoint)
37
+ end
38
+
39
+ it 'loads the class from given string, by interpolating tokens and not stopping after first fail' do
40
+ Lotus::Utils::Class.load!('App::(Layer|Layer::)Step').must_equal(App::Layer::Step)
41
+ end
42
+
43
+ it 'loads class from given string and namespace' do
44
+ Lotus::Utils::Class.load!('(Layer|Layer::)Step', App).must_equal(App::Layer::Step)
45
+ end
46
+ end
47
+ end
data/test/hash_test.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'test_helper'
2
+ require 'lotus/utils/hash'
3
+
4
+ describe Lotus::Utils::Hash do
5
+ it 'acts as a Ruby standard Hash' do
6
+ hash = Lotus::Utils::Hash.new
7
+ hash.must_be_kind_of(::Hash)
8
+
9
+ ::Hash.new.methods.each do |m|
10
+ hash.must_respond_to(m)
11
+ end
12
+ end
13
+
14
+ it 'holds values passed to the constructor' do
15
+ hash = Lotus::Utils::Hash.new('foo' => 'bar')
16
+ hash['foo'].must_equal('bar')
17
+ end
18
+
19
+ describe '#symbolize!' do
20
+ it 'symbolize keys' do
21
+ hash = Lotus::Utils::Hash.new('fub' => 'baz')
22
+ hash.symbolize!
23
+
24
+ hash['fub'].must_be_nil
25
+ hash[:fub].must_equal('baz')
26
+ end
27
+
28
+ it 'symbolize nested hashes' do
29
+ hash = Lotus::Utils::Hash.new('nested' => {'key' => 'value'})
30
+ hash.symbolize!
31
+
32
+ hash[:nested][:key].must_equal('value')
33
+ end
34
+ end
35
+ end
data/test/io_test.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'test_helper'
2
+ require 'lotus/utils/io'
3
+
4
+ class IOTest
5
+ TEST_CONSTANT = 'initial'
6
+ end
7
+
8
+ describe Lotus::Utils::IO do
9
+ describe '.silence_warnings' do
10
+ it 'lowers verbosity of stdout' do
11
+ begin
12
+ position = STDOUT.tell
13
+
14
+ Lotus::Utils::IO.silence_warnings do
15
+ IOTest::TEST_CONSTANT = 'redefined'
16
+ end
17
+
18
+ IOTest::TEST_CONSTANT.must_equal('redefined')
19
+ STDOUT.tell.must_equal(position)
20
+ rescue Errno::ESPIPE
21
+ puts 'Skipping this test, IO#tell is not supported in this environment'
22
+
23
+ Lotus::Utils::IO.silence_warnings do
24
+ IOTest::TEST_CONSTANT = 'redefined'
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,68 @@
1
+ require 'test_helper'
2
+ require 'lotus/utils/path_prefix'
3
+
4
+ describe Lotus::Utils::PathPrefix do
5
+ it 'exposes itself as a string' do
6
+ prefix = Lotus::Utils::PathPrefix.new
7
+ prefix.must_equal ''
8
+ end
9
+
10
+ it 'adds root prefix only when needed' do
11
+ prefix = Lotus::Utils::PathPrefix.new('/fruits')
12
+ prefix.must_equal '/fruits'
13
+ end
14
+
15
+ describe '#join' do
16
+ it 'joins a string' do
17
+ prefix = Lotus::Utils::PathPrefix.new('fruits')
18
+ prefix.join('peaches').must_equal '/fruits/peaches'
19
+ end
20
+
21
+ it 'joins a prefixed string' do
22
+ prefix = Lotus::Utils::PathPrefix.new('fruits')
23
+ prefix.join('/cherries').must_equal '/fruits/cherries'
24
+ end
25
+
26
+ it 'joins a string when the root is blank' do
27
+ prefix = Lotus::Utils::PathPrefix.new
28
+ prefix.join('tea').must_equal '/tea'
29
+ end
30
+
31
+ it 'joins a prefixed string when the root is blank' do
32
+ prefix = Lotus::Utils::PathPrefix.new
33
+ prefix.join('/tea').must_equal '/tea'
34
+ end
35
+ end
36
+
37
+ describe '#relative_join' do
38
+ it 'joins a string without prefixing with separator' do
39
+ prefix = Lotus::Utils::PathPrefix.new('fruits')
40
+ prefix.relative_join('peaches').must_equal 'fruits/peaches'
41
+ end
42
+
43
+ it 'joins a prefixed string without prefixing with separator' do
44
+ prefix = Lotus::Utils::PathPrefix.new('fruits')
45
+ prefix.relative_join('/cherries').must_equal 'fruits/cherries'
46
+ end
47
+
48
+ it 'joins a string when the root is blank without prefixing with separator' do
49
+ prefix = Lotus::Utils::PathPrefix.new
50
+ prefix.relative_join('tea').must_equal 'tea'
51
+ end
52
+
53
+ it 'joins a prefixed string when the root is blank and removes the prefix' do
54
+ prefix = Lotus::Utils::PathPrefix.new
55
+ prefix.relative_join('/tea').must_equal 'tea'
56
+ end
57
+
58
+ it 'joins a string with custom separator' do
59
+ prefix = Lotus::Utils::PathPrefix.new('fruits')
60
+ prefix.relative_join('cherries', '_').must_equal 'fruits_cherries'
61
+ end
62
+
63
+ it 'joins a prefixed string without prefixing with custom separator' do
64
+ prefix = Lotus::Utils::PathPrefix.new('fruits')
65
+ prefix.relative_join('_cherries', '_').must_equal 'fruits_cherries'
66
+ end
67
+ end
68
+ end