lotus-utils 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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