data-provider 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.
- checksums.yaml +7 -0
- data/.gitignore +35 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +36 -0
- data/LICENSE +22 -0
- data/README.md +154 -0
- data/Rakefile +20 -0
- data/data-provider.gemspec +19 -0
- data/lib/data_provider/base.rb +227 -0
- data/lib/data_provider/provider.rb +20 -0
- data/lib/data_provider.rb +2 -0
- data/spec/data_provider_spec.rb +420 -0
- data/spec/spec_helper.rb +12 -0
- metadata +69 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 38f6f8ba1418886f65eb5bed06cfba596744dfb8
|
4
|
+
data.tar.gz: 33d309c91f3cbb341159b20f99129d0ea387a10e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f73b0708c03917f4a58a14bffddbd1357336154b917f0ff610511c0572fb743f12f2824647b36d7bd30972967cc41d6a887de8ae696b6318acd69236a847ec51
|
7
|
+
data.tar.gz: a19b6508e5c30fe4ad94ee42c0f205f8a539cc9f1311b9828feb407daf66ab99d2651f8268b418ffaf91c922c93006cfc9a03535a7ee14af676b7382ecc76c13
|
data/.gitignore
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/test/tmp/
|
9
|
+
/test/version_tmp/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
## Specific to RubyMotion:
|
13
|
+
.dat*
|
14
|
+
.repl_history
|
15
|
+
build/
|
16
|
+
|
17
|
+
## Documentation cache and generated files:
|
18
|
+
/.yardoc/
|
19
|
+
/_yardoc/
|
20
|
+
/doc/
|
21
|
+
/rdoc/
|
22
|
+
|
23
|
+
## Environment normalisation:
|
24
|
+
/.bundle/
|
25
|
+
/vendor/bundle
|
26
|
+
/lib/bundler/man/
|
27
|
+
|
28
|
+
# for a library or gem, you might want to ignore these files since the code is
|
29
|
+
# intended to run in multiple environments; otherwise, check them in:
|
30
|
+
# Gemfile.lock
|
31
|
+
.ruby-version
|
32
|
+
.ruby-gemset
|
33
|
+
|
34
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
35
|
+
.rvmrc
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
data-provider (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
byebug (5.0.0)
|
10
|
+
columnize (= 0.9.0)
|
11
|
+
columnize (0.9.0)
|
12
|
+
diff-lcs (1.2.5)
|
13
|
+
rspec (3.3.0)
|
14
|
+
rspec-core (~> 3.3.0)
|
15
|
+
rspec-expectations (~> 3.3.0)
|
16
|
+
rspec-mocks (~> 3.3.0)
|
17
|
+
rspec-core (3.3.1)
|
18
|
+
rspec-support (~> 3.3.0)
|
19
|
+
rspec-expectations (3.3.0)
|
20
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
21
|
+
rspec-support (~> 3.3.0)
|
22
|
+
rspec-mocks (3.3.1)
|
23
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
24
|
+
rspec-support (~> 3.3.0)
|
25
|
+
rspec-support (3.3.0)
|
26
|
+
|
27
|
+
PLATFORMS
|
28
|
+
ruby
|
29
|
+
|
30
|
+
DEPENDENCIES
|
31
|
+
byebug
|
32
|
+
data-provider!
|
33
|
+
rspec
|
34
|
+
|
35
|
+
BUNDLED WITH
|
36
|
+
1.10.5
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Mark
|
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 all
|
13
|
+
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 THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
# data-provider
|
2
|
+
Ruby gem that provides a set of classes to build consistent data interfaces
|
3
|
+
|
4
|
+
## Installation
|
5
|
+
|
6
|
+
Rubygems:
|
7
|
+
```
|
8
|
+
gem install data-provider
|
9
|
+
```
|
10
|
+
|
11
|
+
Bundler:
|
12
|
+
```ruby
|
13
|
+
gem 'data-provider'
|
14
|
+
```
|
15
|
+
|
16
|
+
## Examples
|
17
|
+
|
18
|
+
Define a provider class with some providers
|
19
|
+
```ruby
|
20
|
+
|
21
|
+
require 'data_provider'
|
22
|
+
|
23
|
+
class BookProvider
|
24
|
+
include DataProvider::Base
|
25
|
+
|
26
|
+
# quick syntax for 'simple' providers
|
27
|
+
provides({
|
28
|
+
title: 'The Monkey Wrench Gang',
|
29
|
+
author: 'Edward Abbey'
|
30
|
+
})
|
31
|
+
|
32
|
+
# longer syntax for more complicated providers
|
33
|
+
provider :display_title do
|
34
|
+
"#{take(:author)} - #{take(:title)}"
|
35
|
+
end
|
36
|
+
|
37
|
+
provider :price do
|
38
|
+
9.99
|
39
|
+
end
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
Using a provider class
|
44
|
+
```ruby
|
45
|
+
book_provider = BookProvider.new()
|
46
|
+
book_provider.take(:title) # => 'The Monkey Wrench Gang'
|
47
|
+
book_provider.take(:author) # => 'Edward Abbey'
|
48
|
+
book_provider.take(:display_title) # => 'Edward Abbey - The Monkey Wrench Gang'
|
49
|
+
book_provider.take(:price) # => 9.99
|
50
|
+
```
|
51
|
+
|
52
|
+
Data providers can be given data to customize their output
|
53
|
+
```ruby
|
54
|
+
require 'data_provider'
|
55
|
+
|
56
|
+
class ProductProvider
|
57
|
+
include DataProvider::Base
|
58
|
+
|
59
|
+
provider :normal_price do
|
60
|
+
17.99
|
61
|
+
end
|
62
|
+
|
63
|
+
provider :discount_price do
|
64
|
+
take(:normal_price) - get_data(:discount)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
product_provider = ProductProvider.new
|
69
|
+
product_provider.take(:normal_price) # => 17.99
|
70
|
+
product_provider.take(:discount_price) # => TypeError (discount data not given,): nil can't be coerced into Float
|
71
|
+
discounted_provider = product_provider.add_data(discount: 3.0) # returns a new instance of the same provider class
|
72
|
+
discounted_provider.take(:discount_price) # => 14.99
|
73
|
+
product_provider.take(:discount_price) # => TypeError (this instance didn't get the new data)
|
74
|
+
product_provider.add_date!(discount: 2) # => Updates this instance instead of creating a new one
|
75
|
+
product_provider.take(:discount_price) # => 15.99
|
76
|
+
product_provider.add_date!(discount: 4).take(:discount_price) #=> 13.99
|
77
|
+
```
|
78
|
+
|
79
|
+
Providers can be defined in a module and added to a provider class using the add class-method (not using include!)
|
80
|
+
```ruby
|
81
|
+
require 'data-provider'
|
82
|
+
|
83
|
+
module BandInfo
|
84
|
+
include DataProvider::Base
|
85
|
+
|
86
|
+
provider :band do
|
87
|
+
'D4'
|
88
|
+
end
|
89
|
+
|
90
|
+
provider :bassPlayer do
|
91
|
+
'Paddy'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class Band
|
96
|
+
include DataProvider::Base
|
97
|
+
|
98
|
+
provider :band do
|
99
|
+
'Dillinger Four'
|
100
|
+
end
|
101
|
+
|
102
|
+
provider :drummer do
|
103
|
+
'Lane Pederson'
|
104
|
+
end
|
105
|
+
|
106
|
+
add BandInfo # note: add, not include!
|
107
|
+
end
|
108
|
+
|
109
|
+
band = Band.new
|
110
|
+
band.take(:band) # => 'D4', class provider was overwritten when the module got added
|
111
|
+
band.take(:bassPlayer) # => 'Paddy'
|
112
|
+
band.take(:drummer) #=> 'Lane Pederson'
|
113
|
+
```
|
114
|
+
|
115
|
+
Provider identifiers don't have to be symbols, they can be anything. Specifically array identifiers are suitable for creating data providers for hierarchical systems
|
116
|
+
```ruby
|
117
|
+
require 'data_provider'
|
118
|
+
|
119
|
+
module AlbumProvider
|
120
|
+
include DataProvider::Base
|
121
|
+
|
122
|
+
provides({
|
123
|
+
title: 'Reinventing Axle Rose',
|
124
|
+
band: 'Against Me!'
|
125
|
+
[:band, :hometown] => 'Gainesville, FL'
|
126
|
+
})
|
127
|
+
end
|
128
|
+
|
129
|
+
class Catalog
|
130
|
+
include DataProvider::Base
|
131
|
+
|
132
|
+
provider [:music, :album, :bandname] do
|
133
|
+
# scoped_take is a method only available inside provider blocks,
|
134
|
+
# it lets a provider access other providers in the same 'scope',
|
135
|
+
# which means provider whose array-identifiers start with the same values,
|
136
|
+
# in this case the scope is [:music, :album]
|
137
|
+
scoped_take(:band).upcase
|
138
|
+
end
|
139
|
+
|
140
|
+
# by using `add_scoped` all providers of the added module, will be
|
141
|
+
# turned into array and prefixed with the given scope
|
142
|
+
add_scoped AlbumProvider, :scope => [:music, :album]
|
143
|
+
end
|
144
|
+
|
145
|
+
catalog = Catalog.new
|
146
|
+
catalog.take(:title) # => DataProvider::ProviderMissingException
|
147
|
+
catalog.take([:music, :album, :title]) # => 'Reinventing Axle Rose'
|
148
|
+
catalog.take([:music, :album, :band, :hometown]) # => 'Gainesville, FL'
|
149
|
+
catalog.take([:music, :album, :bandname]) # => 'AGAINST ME!"
|
150
|
+
```
|
151
|
+
|
152
|
+
|
153
|
+
|
154
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# rxsd project Rakefile
|
2
|
+
#
|
3
|
+
# Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
# Licensed under the LGPLv3+ http://www.gnu.org/licenses/lgpl.txt
|
5
|
+
|
6
|
+
require 'rdoc/task'
|
7
|
+
require "rspec/core/rake_task"
|
8
|
+
|
9
|
+
task :default => :rspec do; end
|
10
|
+
|
11
|
+
desc "Run all specs"
|
12
|
+
RSpec::Core::RakeTask.new('rspec') do |t|
|
13
|
+
t.pattern = 'spec/**/*_spec.rb'
|
14
|
+
end
|
15
|
+
|
16
|
+
Rake::RDocTask.new do |rd|
|
17
|
+
rd.main = "README.rdoc"
|
18
|
+
rd.rdoc_dir = "doc/site/api"
|
19
|
+
rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
GEM_NAME="data-provider"
|
2
|
+
PKG_VERSION='0.1.0'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = GEM_NAME
|
6
|
+
s.version = PKG_VERSION
|
7
|
+
s.files = `git ls-files`.split($/)
|
8
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
9
|
+
|
10
|
+
s.add_development_dependency 'rspec'
|
11
|
+
|
12
|
+
s.author = "Mark van de Korput"
|
13
|
+
s.email = "dr.theman@gmail.com"
|
14
|
+
s.date = '2015-07-14'
|
15
|
+
s.description = %q{A library of Ruby classes to help create consistent data interfaces}
|
16
|
+
s.summary = %q{A library of Ruby classes to help create consistent data interfaces}
|
17
|
+
s.homepage = %q{https://github.com/markkorput/data-provider}
|
18
|
+
s.license = "MIT"
|
19
|
+
end
|
@@ -0,0 +1,227 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module DataProvider
|
4
|
+
|
5
|
+
class ProviderMissingException < Exception
|
6
|
+
end
|
7
|
+
|
8
|
+
module Base
|
9
|
+
|
10
|
+
def self.included(base)
|
11
|
+
base.class_eval do
|
12
|
+
include InstanceMethods
|
13
|
+
extend ClassMethods
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
# provides, when called with a hash param, will define 'simple providers' (providers
|
19
|
+
# with a simple, static value). When called without a param (or nil) it returns
|
20
|
+
# the current cumulative 'simple providers' hash
|
21
|
+
def provides simple_provides = nil
|
22
|
+
if simple_provides
|
23
|
+
@data_provider ||= {}
|
24
|
+
@data_provider[:provides] ||= {}
|
25
|
+
@data_provider[:provides].merge!(simple_provides)
|
26
|
+
return self
|
27
|
+
end
|
28
|
+
# no data given? just return existing hash
|
29
|
+
(@data_provider || {})[:provides] || {}
|
30
|
+
end
|
31
|
+
|
32
|
+
# returns the requested provider as a Provider object
|
33
|
+
def get_provider(id)
|
34
|
+
args = data_provider_definitions.find{|args| args.first == id}
|
35
|
+
return args.nil? ? nil : Provider.new(*args)
|
36
|
+
end
|
37
|
+
|
38
|
+
def provider_identifiers
|
39
|
+
(provides.keys + data_provider_definitions.map(&:first)).compact.uniq
|
40
|
+
end
|
41
|
+
|
42
|
+
# adds a new provider to the class
|
43
|
+
def provider identifier, opts = {}, &block
|
44
|
+
add_provider(identifier, opts, block_given? ? block : nil)
|
45
|
+
end
|
46
|
+
|
47
|
+
# reader method for the raw data of the currently defined providers
|
48
|
+
def data_provider_definitions
|
49
|
+
((@data_provider || {})[:provider_args] || [])
|
50
|
+
end
|
51
|
+
|
52
|
+
# returns wether a provider with the given identifier is available
|
53
|
+
def has_provider?(identifier)
|
54
|
+
(provides[identifier] || get_provider(identifier)) != nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def has_providers_with_scope?(args)
|
58
|
+
scope = args.is_a?(Array) ? args : [args]
|
59
|
+
provider_identifiers.find{|id| id.is_a?(Array) && id.length > scope.length && id[0..(scope.length-1)] == scope} != nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def fallback_provider?
|
63
|
+
!fallback_provider.nil?
|
64
|
+
end
|
65
|
+
|
66
|
+
# adds all the providers defined in the given module to this class
|
67
|
+
def add(providers_module)
|
68
|
+
data = providers_module.instance_variable_get('@data_provider') || {}
|
69
|
+
|
70
|
+
(data[:provider_args] || []).each do |definition|
|
71
|
+
add_provider(*definition)
|
72
|
+
end
|
73
|
+
|
74
|
+
self.provides(data[:provides] || {})
|
75
|
+
end
|
76
|
+
|
77
|
+
# adds all the providers defined in the given module to this class,
|
78
|
+
# but turns their identifiers into array and prefixes the array with the :scope option
|
79
|
+
def add_scoped(providers_module, _options = {})
|
80
|
+
data = providers_module.instance_variable_get('@data_provider') || {}
|
81
|
+
|
82
|
+
(data[:provider_args] || []).each do |definition|
|
83
|
+
definition[0] = [definition[0]].flatten
|
84
|
+
definition[0] = [_options[:scope]].flatten.compact + definition[0] if _options[:scope]
|
85
|
+
add_provider(*definition)
|
86
|
+
end
|
87
|
+
|
88
|
+
(data[:provides] || {}).each_pair do |key, value|
|
89
|
+
provides(([_options[:scope]].flatten.compact + [key].flatten.compact) => value)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def provider_missing &block
|
94
|
+
raise "DataProvider::Base#provider_missing expects a block as an argument" if !block_given?
|
95
|
+
@data_provider ||= {}
|
96
|
+
@data_provider[:provider_missing] = block
|
97
|
+
end
|
98
|
+
|
99
|
+
def fallback_provider
|
100
|
+
block = (@data_provider || {})[:provider_missing]
|
101
|
+
block.nil? ? nil : Provider.new(nil, nil, block)
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def add_provider(identifier, opts = {}, block = nil)
|
107
|
+
@data_provider ||= {}
|
108
|
+
@data_provider[:provider_args] ||= []
|
109
|
+
@data_provider[:provider_args].unshift [identifier, opts, block]
|
110
|
+
end
|
111
|
+
end # module ClassMethods
|
112
|
+
|
113
|
+
|
114
|
+
module InstanceMethods
|
115
|
+
|
116
|
+
attr_reader :data
|
117
|
+
attr_reader :options
|
118
|
+
|
119
|
+
def initialize(opts = {})
|
120
|
+
@options = opts.is_a?(Hash) ? opts : {}
|
121
|
+
@data = options[:data].is_a?(Hash) ? options[:data] : {}
|
122
|
+
end
|
123
|
+
|
124
|
+
def logger
|
125
|
+
@logger ||= options[:logger] || Logger.new(STDOUT)
|
126
|
+
end
|
127
|
+
|
128
|
+
def has_provider?(id)
|
129
|
+
self.class.has_provider?(id)
|
130
|
+
end
|
131
|
+
|
132
|
+
def has_providers_with_scope?(scope)
|
133
|
+
self.class.has_providers_with_scope?(scope)
|
134
|
+
end
|
135
|
+
|
136
|
+
def fallback_provider?
|
137
|
+
self.class.fallback_provider?
|
138
|
+
end
|
139
|
+
|
140
|
+
def take(id)
|
141
|
+
# first try the simple providers
|
142
|
+
if self.class.provides.has_key?(id)
|
143
|
+
provider = self.class.provides[id]
|
144
|
+
return provider.is_a?(Proc) ? provider.call : provider
|
145
|
+
end
|
146
|
+
# try to get a provider object
|
147
|
+
provider = self.class.get_provider(id)
|
148
|
+
if provider
|
149
|
+
@scope ||= []
|
150
|
+
@scope << (id.is_a?(Array) ? id[0..-2] : [])
|
151
|
+
result = instance_eval(&provider.block)
|
152
|
+
@scope.pop
|
153
|
+
# execute provider object's block within the scope of self
|
154
|
+
return result
|
155
|
+
end
|
156
|
+
|
157
|
+
# couldn't find requested provider, let's see if there's a fallback
|
158
|
+
if provider = self.class.fallback_provider
|
159
|
+
# temporarily set the @missing_provider instance variable, so the
|
160
|
+
# fallback provider can use it through the missing_provider private method
|
161
|
+
@missing_provider = id
|
162
|
+
@scope ||= []
|
163
|
+
@scope << (id.is_a?(Array) ? id[0..-2] : [])
|
164
|
+
result = instance_eval(&provider.block) # provider.block.call # with the block.call method the provider can't access private methods like missing_provider
|
165
|
+
@scope = nil
|
166
|
+
return result
|
167
|
+
end
|
168
|
+
# no fallback either? Time for an error
|
169
|
+
raise ProviderMissingException.new(:message=>"Tried to take data from missing provider.", :provider_id => id)
|
170
|
+
end
|
171
|
+
|
172
|
+
def try_take(id, opts = {})
|
173
|
+
return take(id) if self.has_provider?(id) || self.fallback_provider?
|
174
|
+
if opts[:fallback] == true
|
175
|
+
|
176
|
+
logger.debug "Try for missing provider: #{id.inspect}"
|
177
|
+
return nil
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
def scoped_take(id)
|
184
|
+
take(((@scope || []).last || []) + [id].flatten)
|
185
|
+
end
|
186
|
+
|
187
|
+
public
|
188
|
+
|
189
|
+
def given(param_name)
|
190
|
+
return data[param_name] if data.has_key?(param_name)
|
191
|
+
logger.error "Data provider expected missing data with identifier: #{param_name.inspect}"
|
192
|
+
# TODO: raise?
|
193
|
+
return nil
|
194
|
+
end
|
195
|
+
|
196
|
+
alias :get_data :given
|
197
|
+
|
198
|
+
def give(_data = {})
|
199
|
+
return self.class.new(options.merge(:data => data.merge(_data)))
|
200
|
+
end
|
201
|
+
|
202
|
+
alias :add_scope :give
|
203
|
+
alias :add_data :give
|
204
|
+
|
205
|
+
def give!(_data = {})
|
206
|
+
@data = data.merge(_data)
|
207
|
+
return self
|
208
|
+
end
|
209
|
+
|
210
|
+
alias :add_scope! :give!
|
211
|
+
alias :add_data! :give!
|
212
|
+
|
213
|
+
private
|
214
|
+
|
215
|
+
def missing_provider
|
216
|
+
# byebug
|
217
|
+
@missing_provider
|
218
|
+
end
|
219
|
+
|
220
|
+
def scope
|
221
|
+
@scope || []
|
222
|
+
end
|
223
|
+
end # module InstanceMethods
|
224
|
+
|
225
|
+
end # module Base
|
226
|
+
|
227
|
+
end # module DataProvider
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module DataProvider
|
2
|
+
class Provider
|
3
|
+
attr_reader :options
|
4
|
+
attr_reader :identifier
|
5
|
+
attr_reader :block
|
6
|
+
|
7
|
+
def initialize(identifier, opts = {}, block = nil)
|
8
|
+
@identifier = identifier
|
9
|
+
@options = opts.is_a?(Hash) ? opts : {}
|
10
|
+
@block = block || Proc.new
|
11
|
+
end
|
12
|
+
|
13
|
+
alias_method :id, :identifier
|
14
|
+
|
15
|
+
def requirements
|
16
|
+
[options[:requires]].flatten.compact
|
17
|
+
end
|
18
|
+
|
19
|
+
end # module Provider
|
20
|
+
end # module DataProvider
|
@@ -0,0 +1,420 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe DataProvider::Base do
|
4
|
+
# Example implementation of DataProvider::Base
|
5
|
+
class ProviderClass
|
6
|
+
include DataProvider::Base
|
7
|
+
|
8
|
+
provider :sum, :requires => [:array] do
|
9
|
+
sum = 0
|
10
|
+
|
11
|
+
given(:array).each do |number|
|
12
|
+
sum += number.to_i
|
13
|
+
end
|
14
|
+
|
15
|
+
sum
|
16
|
+
end
|
17
|
+
|
18
|
+
provider :static do
|
19
|
+
'StaticValue'
|
20
|
+
end
|
21
|
+
|
22
|
+
provider :billy do
|
23
|
+
take([:identification, :fullname])
|
24
|
+
end
|
25
|
+
|
26
|
+
provider [:identification, :firstname] do
|
27
|
+
'Billy'
|
28
|
+
end
|
29
|
+
|
30
|
+
provider [:identification, :lastname] do
|
31
|
+
'Bragg'
|
32
|
+
end
|
33
|
+
|
34
|
+
provider [:identification, :fullname] do
|
35
|
+
"#{scoped_take(:firstname)} #{scoped_take(:lastname)}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
let(:provider){
|
40
|
+
ProviderClass.new(:data => {:array => [1,2,4]})
|
41
|
+
}
|
42
|
+
|
43
|
+
describe "#has_provider?" do
|
44
|
+
it 'provides the has_provider? instance method' do
|
45
|
+
expect(provider.has_provider?(:sum)).to be true
|
46
|
+
expect(provider.has_provider?(:static)).to be true
|
47
|
+
expect(provider.has_provider?(:modulus)).to be false
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'provides the has_provider? class method' do
|
51
|
+
expect(ProviderClass.respond_to?(:has_provider?)).to eq true
|
52
|
+
expect(ProviderClass.has_provider?(:sum)).to eq true
|
53
|
+
expect(ProviderClass.has_provider?(:divid)).to eq false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#has_providers_with_scope?" do
|
58
|
+
let(:klass){
|
59
|
+
Class.new Object do
|
60
|
+
include DataProvider::Base
|
61
|
+
provider [:a, :b ,:c]
|
62
|
+
provider :unscoped
|
63
|
+
end
|
64
|
+
}
|
65
|
+
|
66
|
+
it "return true if there are providers defined with an array identifier that start with the given prefix" do
|
67
|
+
expect(klass.new.has_providers_with_scope?(:unscoped)).to eq false
|
68
|
+
# byebug
|
69
|
+
expect(klass.has_providers_with_scope?(:a)).to eq true
|
70
|
+
expect(klass.new.has_providers_with_scope?([:a, :b])).to eq true
|
71
|
+
# scope means prefix, identfier may not be exactly the given array
|
72
|
+
expect(klass.new.has_providers_with_scope?([:a, :b, :c])).to eq false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "#take" do
|
77
|
+
it 'lets you take data from it' do
|
78
|
+
expect(provider.take(:sum)).to eq 7
|
79
|
+
expect(provider.take(:static)).to eq 'StaticValue'
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'raise a ProviderMissingException when attempting to take from unknown provider' do
|
83
|
+
expect{provider.take(:unknown)}.to raise_error(DataProvider::ProviderMissingException)
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
it 'works from within a provider block' do
|
88
|
+
expect(provider.take(:billy)).to eq 'Billy Bragg'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "#try_take" do
|
93
|
+
it "acts like #take when the specified provider is present" do
|
94
|
+
expect(provider.try_take(:sum)).to eq 7
|
95
|
+
end
|
96
|
+
|
97
|
+
it "returns nil when the specified provider is not found" do
|
98
|
+
expect(provider.try_take(:square_root)).to eq nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "#scoped_take" do
|
103
|
+
it 'lets a provider call providers within its own scope' do
|
104
|
+
expect(provider.take([:identification, :fullname])).to eq 'Billy Bragg'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "#give" do
|
109
|
+
it "lets you give data, creating a new data provider instance" do
|
110
|
+
updated_provider = provider.give :array => [1,80]
|
111
|
+
expect(provider.take(:sum)).to eq 7
|
112
|
+
expect(updated_provider.take(:sum)).to eq 81
|
113
|
+
end
|
114
|
+
|
115
|
+
it "allows for linked notation" do
|
116
|
+
expect(provider.give(:array => [7, -3]).take(:sum)).to eq 4
|
117
|
+
end
|
118
|
+
|
119
|
+
it "has an add_scope alias" do
|
120
|
+
expect(provider.add_scope(:array => [400, 20]).take(:sum)).to eq 420
|
121
|
+
end
|
122
|
+
|
123
|
+
it "has an add_data alias" do
|
124
|
+
expect(provider.add_data(:array => [400, 20]).take(:sum)).to eq 420
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "#give!" do
|
129
|
+
it "lets you update the current provider with additional data" do
|
130
|
+
prov = ProviderClass.new(:data => {:array => [1,1,90]})
|
131
|
+
expect(prov.take(:sum)).to eq 92
|
132
|
+
prov.give!(:array => [3,90])
|
133
|
+
expect(prov.take(:sum)).to eq 93
|
134
|
+
end
|
135
|
+
|
136
|
+
it "allows for linked notation" do
|
137
|
+
expect(provider.give.give!(:array => [-1, -4]).take(:sum)).to eq -5
|
138
|
+
end
|
139
|
+
|
140
|
+
it "has an add_scope! alias" do
|
141
|
+
newprovider = provider.add_scope
|
142
|
+
newprovider.add_scope!(:array => [-1, -4])
|
143
|
+
expect(newprovider.given(:array)).to eq [-1,-4]
|
144
|
+
expect(newprovider.take(:sum)).to eq -5
|
145
|
+
end
|
146
|
+
|
147
|
+
it "has an add_data! alias" do
|
148
|
+
scoped_provider = provider.add_data(:array => []).add_data!(:array => [5, 5])
|
149
|
+
expect(scoped_provider.get_data(:array)).to eq [5,5]
|
150
|
+
expect(scoped_provider.take(:sum)).to eq 10
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe "#given" do
|
155
|
+
it "has a given method to get given data" do
|
156
|
+
expect(provider.given(:array)).to eq [1,2,4]
|
157
|
+
expect(provider.give(:array => 'array').given(:array)).to eq 'array'
|
158
|
+
end
|
159
|
+
|
160
|
+
it "has a get_data alias" do
|
161
|
+
expect(provider.get_data(:array)).to eq provider.given(:array)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "#provides" do
|
166
|
+
class SimpleProviders
|
167
|
+
include DataProvider::Base
|
168
|
+
provides({
|
169
|
+
:name => 'Paddy',
|
170
|
+
'instrument' => :bass,
|
171
|
+
})
|
172
|
+
end
|
173
|
+
|
174
|
+
it "lets you request all currently available simple providers when called without a parameter" do
|
175
|
+
expect(SimpleProviders.provides).to eq({
|
176
|
+
:name => 'Paddy',
|
177
|
+
'instrument' => :bass
|
178
|
+
})
|
179
|
+
end
|
180
|
+
|
181
|
+
it "lets you define simple providers" do
|
182
|
+
expect(SimpleProviders.new.take(:name)).to eq 'Paddy'
|
183
|
+
expect(SimpleProviders.new.take('instrument')).to eq :bass
|
184
|
+
end
|
185
|
+
|
186
|
+
it "works with has_provider?" do
|
187
|
+
expect(SimpleProviders.has_provider?(:name)).to eq true
|
188
|
+
expect(SimpleProviders.new.has_provider?('name')).to eq false
|
189
|
+
expect(SimpleProviders.has_provider?('instrument')).to eq true
|
190
|
+
expect(SimpleProviders.new.has_provider?(:instrument)).to eq false
|
191
|
+
end
|
192
|
+
|
193
|
+
it "lets you overwrite existing simple providers" do
|
194
|
+
SimpleProviders.provides({:name => 'Erik'})
|
195
|
+
expect(SimpleProviders.new.take(:name)).to eq 'Erik'
|
196
|
+
end
|
197
|
+
|
198
|
+
it "lets you write linked notation" do
|
199
|
+
expect(SimpleProviders.provides({:name => 'Lane'}).new.take(:name)).to eq 'Lane'
|
200
|
+
end
|
201
|
+
|
202
|
+
it "works with lambdas" do
|
203
|
+
expect(SimpleProviders.provides(:name => lambda{ 'Patrick' }).new.take(:name)).to eq 'Patrick'
|
204
|
+
end
|
205
|
+
|
206
|
+
it "works with Procs" do
|
207
|
+
expect(SimpleProviders.provides(:name => Proc.new{ 'St. Patrick' }).new.take(:name)).to eq 'St. Patrick'
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
describe "#add" do
|
212
|
+
module OddProviders
|
213
|
+
include DataProvider::Base
|
214
|
+
provides({1 => 'one'})
|
215
|
+
provider :three do 3 end
|
216
|
+
end
|
217
|
+
|
218
|
+
module OddOverwriteProviders
|
219
|
+
include DataProvider::Base
|
220
|
+
provides({1 => 'Uno', :five => 555})
|
221
|
+
provider :three do :tres end
|
222
|
+
end
|
223
|
+
|
224
|
+
class BasicProviders
|
225
|
+
include DataProvider::Base
|
226
|
+
provides({2 => '1'})
|
227
|
+
provider :four do '4' end
|
228
|
+
add OddProviders
|
229
|
+
end
|
230
|
+
|
231
|
+
it "lets you add providers from another module" do
|
232
|
+
expect(BasicProviders.has_provider?(1)).to eq true
|
233
|
+
expect(BasicProviders.has_provider?(2)).to eq true
|
234
|
+
expect(BasicProviders.has_provider?(:three)).to eq true
|
235
|
+
expect(BasicProviders.has_provider?(:four)).to eq true
|
236
|
+
# expect(BasicProviders.new.take(1)).to eq 'one'
|
237
|
+
expect(BasicProviders.new.take(:three)).to eq 3
|
238
|
+
end
|
239
|
+
|
240
|
+
it "lets you add providers from another module at runtime" do
|
241
|
+
expect(BasicProviders.has_provider?(:five)).to eq false
|
242
|
+
BasicProviders.add(OddOverwriteProviders)
|
243
|
+
expect(BasicProviders.has_provider?(:five)).to eq true
|
244
|
+
end
|
245
|
+
|
246
|
+
# for the following test the providers of OddOverwriteProviders
|
247
|
+
# have already been added (by the previous test)
|
248
|
+
it "lets overwrite providers" do
|
249
|
+
expect(BasicProviders.new.take(1)).to eq 'Uno'
|
250
|
+
expect(BasicProviders.new.take(:three)).to eq :tres
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
describe "#add_scoped" do
|
255
|
+
module ChildProviders
|
256
|
+
include DataProvider::Base
|
257
|
+
provider :name do "child" end
|
258
|
+
end
|
259
|
+
|
260
|
+
module GrandchildProviders
|
261
|
+
include DataProvider::Base
|
262
|
+
provider :name do "grandchild" end
|
263
|
+
provider [:age] do 1 end
|
264
|
+
provides({
|
265
|
+
:mommy => 'Wilma',
|
266
|
+
:daddy => 'Fret'
|
267
|
+
})
|
268
|
+
end
|
269
|
+
|
270
|
+
class ProviderKlass
|
271
|
+
include DataProvider::Base
|
272
|
+
provider :parent do 'parent' end
|
273
|
+
add_scoped ChildProviders, :scope => :child
|
274
|
+
add_scoped GrandchildProviders, :scope => [:child, :child]
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'let you array-prefix the providers of an included module' do
|
278
|
+
expect(ProviderKlass.has_provider?(:parent)).to eq true
|
279
|
+
expect(ProviderKlass.has_provider?(:name)).to eq false
|
280
|
+
expect(ProviderKlass.has_provider?([:child, :name])).to eq true
|
281
|
+
expect(ProviderKlass.has_provider?([:child, :age])).to eq false
|
282
|
+
expect(ProviderKlass.has_provider?([:child, :child, :name])).to eq true
|
283
|
+
expect(ProviderKlass.has_provider?([:child, :child, :age])).to eq true
|
284
|
+
expect(ProviderKlass.has_provider?([:child, :child, :mommy])).to eq true
|
285
|
+
expect(ProviderKlass.has_provider?([:child, :child, :daddy])).to eq true
|
286
|
+
expect(ProviderKlass.new.take([:child, :name])).to eq 'child'
|
287
|
+
expect(ProviderKlass.new.take([:child, :child, :name])).to eq 'grandchild'
|
288
|
+
expect(ProviderKlass.new.take([:child, :child, :age])).to eq 1
|
289
|
+
expect(ProviderKlass.new.take([:child, :child, :mommy])).to eq 'Wilma'
|
290
|
+
expect(ProviderKlass.new.take([:child, :child, :daddy])).to eq 'Fret'
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
describe "provider_missing" do
|
295
|
+
it "lets you define a default fallback provider" do
|
296
|
+
klass = Class.new Object do
|
297
|
+
include DataProvider::Base
|
298
|
+
provider_missing do
|
299
|
+
"This provider don't exist!"
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
expect(klass.has_provider?(:message)).to eq false
|
304
|
+
expect(klass.new.take(:message)).to eq "This provider don't exist!"
|
305
|
+
end
|
306
|
+
|
307
|
+
it "provides the missing provider id through the private missing_provider method" do
|
308
|
+
klass = Class.new Object do
|
309
|
+
include DataProvider::Base
|
310
|
+
provider_missing do
|
311
|
+
"Missing #{missing_provider}"
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
expect(klass.new.take(:something)).to eq 'Missing something'
|
316
|
+
expect{klass.new.missing_provider}.to raise_error(NoMethodError)
|
317
|
+
end
|
318
|
+
|
319
|
+
it 'calls the fallback provider when using try_take with an unknown provider' do
|
320
|
+
klass = Class.new Object do
|
321
|
+
include DataProvider::Base
|
322
|
+
provider_missing do
|
323
|
+
"fallback_#{missing_provider}"
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
expect(klass.new.try_take(:cool)).to eq 'fallback_cool'
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
describe "fallback_provider?" do
|
332
|
+
it "lets you know if a fallback provider has been registered" do
|
333
|
+
klass = Class.new Object do
|
334
|
+
include DataProvider::Base
|
335
|
+
end
|
336
|
+
|
337
|
+
expect(klass.fallback_provider?).to eq false
|
338
|
+
expect(klass.new.fallback_provider?).to eq false
|
339
|
+
|
340
|
+
klass.provider_missing do
|
341
|
+
"New fallback!"
|
342
|
+
end
|
343
|
+
|
344
|
+
expect(klass.fallback_provider?).to eq true
|
345
|
+
expect(klass.new.fallback_provider?).to eq true
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
describe "Adding additional providers" do
|
351
|
+
module AdditionalProvider
|
352
|
+
include DataProvider::Base
|
353
|
+
|
354
|
+
provider 'provider2' do
|
355
|
+
'#2'
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
module OverwriteProvider
|
360
|
+
include DataProvider::Base
|
361
|
+
|
362
|
+
provider 'provider1' do
|
363
|
+
'#111'
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
class OriginalProvider
|
368
|
+
include DataProvider::Base
|
369
|
+
|
370
|
+
provider 'provider1' do
|
371
|
+
'#1'
|
372
|
+
end
|
373
|
+
|
374
|
+
add AdditionalProvider
|
375
|
+
add OverwriteProvider
|
376
|
+
end
|
377
|
+
|
378
|
+
it "lets you include modules with additional providers" do
|
379
|
+
expect(OriginalProvider.new.take('provider2')).to eq '#2'
|
380
|
+
end
|
381
|
+
|
382
|
+
it "lets you include modules with additional providers" do
|
383
|
+
expect(OriginalProvider.new.take('provider1')).to eq '#111'
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
describe "Array identifiers" do
|
388
|
+
module ArrayProviderModule
|
389
|
+
include DataProvider::Base
|
390
|
+
|
391
|
+
provider [:some, 'Stuff'] do
|
392
|
+
'OtherStuff'
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
class ArrayProviderClass
|
397
|
+
include DataProvider::Base
|
398
|
+
|
399
|
+
provider [:some, 'Stuff'] do
|
400
|
+
'SomeStuff'
|
401
|
+
end
|
402
|
+
|
403
|
+
# add ArrayProviderModule
|
404
|
+
end
|
405
|
+
|
406
|
+
let(:provider){
|
407
|
+
ArrayProviderClass.new
|
408
|
+
}
|
409
|
+
|
410
|
+
it "lets you use array as provider identifiers" do
|
411
|
+
expect(provider.take([:some, 'Stuff'])).to eq 'SomeStuff'
|
412
|
+
end
|
413
|
+
|
414
|
+
it "lets you overwrite existing providers with Array-based identifiers" do
|
415
|
+
expect(provider.take([:some, 'Stuff'])).to eq 'SomeStuff'
|
416
|
+
|
417
|
+
provider.class.add(ArrayProviderModule)
|
418
|
+
expect(provider.take([:some, 'Stuff'])).to eq 'OtherStuff'
|
419
|
+
end
|
420
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# loads and runs all tests for the rxsd project
|
2
|
+
#
|
3
|
+
# Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
# Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
|
5
|
+
|
6
|
+
require 'rspec'
|
7
|
+
|
8
|
+
CURRENT_DIR=File.dirname(__FILE__)
|
9
|
+
$: << File.expand_path(CURRENT_DIR + "/../lib")
|
10
|
+
|
11
|
+
require 'data_provider'
|
12
|
+
require 'byebug'
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: data-provider
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mark van de Korput
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: A library of Ruby classes to help create consistent data interfaces
|
28
|
+
email: dr.theman@gmail.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- ".gitignore"
|
34
|
+
- Gemfile
|
35
|
+
- Gemfile.lock
|
36
|
+
- LICENSE
|
37
|
+
- README.md
|
38
|
+
- Rakefile
|
39
|
+
- data-provider.gemspec
|
40
|
+
- lib/data_provider.rb
|
41
|
+
- lib/data_provider/base.rb
|
42
|
+
- lib/data_provider/provider.rb
|
43
|
+
- spec/data_provider_spec.rb
|
44
|
+
- spec/spec_helper.rb
|
45
|
+
homepage: https://github.com/markkorput/data-provider
|
46
|
+
licenses:
|
47
|
+
- MIT
|
48
|
+
metadata: {}
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options: []
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
requirements: []
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 2.2.2
|
66
|
+
signing_key:
|
67
|
+
specification_version: 4
|
68
|
+
summary: A library of Ruby classes to help create consistent data interfaces
|
69
|
+
test_files: []
|