hexx-storage 0.0.2
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/.coveralls.yml +2 -0
- data/.gitignore +9 -0
- data/.metrics +9 -0
- data/.rspec +2 -0
- data/.rubocop.yml +2 -0
- data/.travis.yml +12 -0
- data/.yardopts +3 -0
- data/Gemfile +12 -0
- data/Guardfile +14 -0
- data/LICENSE +21 -0
- data/README.md +351 -0
- data/Rakefile +22 -0
- data/config/metrics/STYLEGUIDE +230 -0
- data/config/metrics/cane.yml +5 -0
- data/config/metrics/churn.yml +6 -0
- data/config/metrics/flay.yml +2 -0
- data/config/metrics/metric_fu.yml +13 -0
- data/config/metrics/reek.yml +1 -0
- data/config/metrics/roodi.yml +24 -0
- data/config/metrics/rubocop.yml +73 -0
- data/config/metrics/saikuro.yml +3 -0
- data/config/metrics/simplecov.yml +8 -0
- data/config/metrics/yardstick.yml +37 -0
- data/hexx-storage.gemspec +28 -0
- data/lib/hexx-storage.rb +20 -0
- data/lib/hexx/storage.rb +17 -0
- data/lib/hexx/storage/base.rb +108 -0
- data/lib/hexx/storage/patches.rb +135 -0
- data/lib/hexx/storage/repositories.rb +40 -0
- data/lib/hexx/storage/repositories/base.rb +70 -0
- data/lib/hexx/storage/repositories/memory.rb +33 -0
- data/lib/hexx/storage/repositories/sql.rb +78 -0
- data/lib/hexx/storage/version.rb +13 -0
- data/spec/integration/storage.yml +9 -0
- data/spec/integration/storage_spec.rb +56 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/tests/storage/base_spec.rb +178 -0
- data/spec/tests/storage/patches_spec.rb +202 -0
- data/spec/tests/storage/repositories/base_spec.rb +120 -0
- data/spec/tests/storage/repositories/memory_spec.rb +32 -0
- data/spec/tests/storage/repositories/sql_spec.rb +180 -0
- data/spec/tests/storage/repositories_spec.rb +66 -0
- data/spec/tests/storage_spec.rb +24 -0
- metadata +168 -0
data/lib/hexx-storage.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "logger"
|
4
|
+
require "naught"
|
5
|
+
require "rom"
|
6
|
+
|
7
|
+
# Shared namespace for the Hexx-based gems
|
8
|
+
module Hexx
|
9
|
+
|
10
|
+
require_relative "hexx/storage/patches" # Monkey patches for core classes
|
11
|
+
require_relative "hexx/storage"
|
12
|
+
|
13
|
+
require_relative "hexx/storage/repositories/base"
|
14
|
+
require_relative "hexx/storage/repositories/memory"
|
15
|
+
require_relative "hexx/storage/repositories/sql"
|
16
|
+
require_relative "hexx/storage/repositories" # Factory for adapters
|
17
|
+
|
18
|
+
require_relative "hexx/storage/base" # API
|
19
|
+
|
20
|
+
end # module Hexx
|
data/lib/hexx/storage.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Hexx
|
4
|
+
|
5
|
+
# The builder for storage settings
|
6
|
+
module Storage
|
7
|
+
|
8
|
+
# The shortcut to the storage object constructor
|
9
|
+
#
|
10
|
+
# @return [Hexx::Storage::Base]
|
11
|
+
def self.setup
|
12
|
+
Base.new
|
13
|
+
end
|
14
|
+
|
15
|
+
end # module Storage
|
16
|
+
|
17
|
+
end # module Hexx
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "delegate"
|
4
|
+
|
5
|
+
module Hexx
|
6
|
+
|
7
|
+
module Storage
|
8
|
+
|
9
|
+
# The container to load and store +ROM+ settings
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# storage = Storage::Base.new
|
13
|
+
#
|
14
|
+
# storage.root = "path_to_application"
|
15
|
+
# storage.load "config/initializers/my_gem.rb", env: :test
|
16
|
+
# storage.migrations = "path_to_my_gem/db/migrate"
|
17
|
+
#
|
18
|
+
# ROM.setup storage.settings
|
19
|
+
class Base < SimpleDelegator
|
20
|
+
using Patches
|
21
|
+
|
22
|
+
# @private
|
23
|
+
def initialize
|
24
|
+
super({})
|
25
|
+
end
|
26
|
+
|
27
|
+
# @!method keys
|
28
|
+
# Returns the list of names for +ROM+ repositories'
|
29
|
+
#
|
30
|
+
# @return [Array<Symbol>]
|
31
|
+
|
32
|
+
# @!method [](repository)
|
33
|
+
# Returns the object that stores settings for some repository
|
34
|
+
#
|
35
|
+
# @param [Symbol] repository
|
36
|
+
#
|
37
|
+
# @return [Hexx::Storage::Repositories::Memory]
|
38
|
+
|
39
|
+
# @!method root
|
40
|
+
# Returns the root of the application to {#load} settings from
|
41
|
+
#
|
42
|
+
# @return [String, nil]
|
43
|
+
attr_reader :root
|
44
|
+
|
45
|
+
# Sets the root of the application where settings are defined
|
46
|
+
#
|
47
|
+
# @param [#to_s] value The absolute path
|
48
|
+
#
|
49
|
+
# @return [String] if the folder exists
|
50
|
+
# @return [nil] if the folder is absent
|
51
|
+
def root=(value)
|
52
|
+
@root = value.to_s.to_folder
|
53
|
+
end
|
54
|
+
|
55
|
+
# Loads +ROM+ settings from given yml file for given environment
|
56
|
+
#
|
57
|
+
# @param [#to_s] file The path relative to the {#root}
|
58
|
+
# @option [#to_sym] :env The environment to load settings for
|
59
|
+
#
|
60
|
+
# @raise (see #setup)
|
61
|
+
#
|
62
|
+
# @return [undefined]
|
63
|
+
def load(file, env:)
|
64
|
+
check_root # need to know where to load the file from
|
65
|
+
setup [root, file].to_path.from_file.from_yaml.sub(env)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Sets +ROM+ settings from hash
|
69
|
+
#
|
70
|
+
# @param [Hash] hash
|
71
|
+
#
|
72
|
+
# @raise [NotImplementedError] if the {#root} hasn't been defined yet
|
73
|
+
#
|
74
|
+
# @return [undefined]
|
75
|
+
def setup(hash)
|
76
|
+
check_root # need to know where to place logs to
|
77
|
+
__setobj__ hash.normalize.wrap(&new_repo)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns settings as an argument for +ROM.setup+
|
81
|
+
#
|
82
|
+
# @raise [NotImplementedError] if the settings haven't been set
|
83
|
+
#
|
84
|
+
# @return [Hash]
|
85
|
+
def settings
|
86
|
+
check_storage
|
87
|
+
__getobj__.wrap(&:settings)
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def new_repo
|
93
|
+
proc { |values| Repositories.build(root: root, **values.normalize) }
|
94
|
+
end
|
95
|
+
|
96
|
+
def check_root
|
97
|
+
fail NotImplementedError.new "The #root hasn't been set" unless root
|
98
|
+
end
|
99
|
+
|
100
|
+
def check_storage
|
101
|
+
fail NotImplementedError.new "The storage hasn't been set" if empty?
|
102
|
+
end
|
103
|
+
|
104
|
+
end # class Base
|
105
|
+
|
106
|
+
end # module Storage
|
107
|
+
|
108
|
+
end # module Hexx
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
require "logger"
|
5
|
+
require "tempfile"
|
6
|
+
|
7
|
+
module Hexx
|
8
|
+
|
9
|
+
module Storage
|
10
|
+
|
11
|
+
# Collection of local monkey patches for core classes
|
12
|
+
module Patches
|
13
|
+
|
14
|
+
refine String do
|
15
|
+
|
16
|
+
# Treats the string as the absolute filepath and returns its content
|
17
|
+
#
|
18
|
+
# @example Returns an empty string if file is absent
|
19
|
+
# "".from_file
|
20
|
+
# # => ""
|
21
|
+
#
|
22
|
+
# @return [String]
|
23
|
+
def from_file
|
24
|
+
File.read(self).to_s rescue ""
|
25
|
+
end
|
26
|
+
|
27
|
+
# Treats the string as yml content and converts it to hash
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# "---\nfoo:\n bar: :baz\n".from_yaml
|
31
|
+
# # => { foo: { bar: :baz } }
|
32
|
+
#
|
33
|
+
# @example Returns an empty hash if the string is not a valid yml
|
34
|
+
# "foo".from_yaml
|
35
|
+
# # => {}
|
36
|
+
#
|
37
|
+
# "".from_yaml
|
38
|
+
# # => {}
|
39
|
+
#
|
40
|
+
# @return [Hash]
|
41
|
+
def from_yaml
|
42
|
+
data = YAML.load(self)
|
43
|
+
data.instance_of?(Hash) ? data : {}
|
44
|
+
end
|
45
|
+
|
46
|
+
# Treats the string as a folder path and returns a copy if it exists
|
47
|
+
#
|
48
|
+
# @return [String] if the folder exists
|
49
|
+
# @return [nil] if the folder is absent
|
50
|
+
def to_folder
|
51
|
+
dup if Dir.exist? self
|
52
|
+
end
|
53
|
+
|
54
|
+
end # class String
|
55
|
+
|
56
|
+
refine Hash do
|
57
|
+
|
58
|
+
# Returns the copy of current hash with symbolized root keys
|
59
|
+
#
|
60
|
+
# @example
|
61
|
+
# { "foo" => "bar" }.normalize
|
62
|
+
# # => { foo: "bar" }
|
63
|
+
#
|
64
|
+
# @return [Hash]
|
65
|
+
def normalize
|
66
|
+
keys.map(&:to_s).map(&:to_sym).zip(values).to_h
|
67
|
+
end
|
68
|
+
|
69
|
+
# Applies given block to values and returns the result as a new hash
|
70
|
+
#
|
71
|
+
# Returns a copy of itself if block not given.
|
72
|
+
#
|
73
|
+
# @example Counts the number of items in each value
|
74
|
+
# { "a" => %w(a b c), "b" => %w(d e) }.wrap(&:count)
|
75
|
+
# # => { "a" => 3, "b" => 2 }
|
76
|
+
#
|
77
|
+
# @param [Proc] block
|
78
|
+
#
|
79
|
+
# @return [Hash]
|
80
|
+
def wrap(&block)
|
81
|
+
block_given? ? keys.zip(values.map(&block)).to_h : dup
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns a normalized subhash of the current hash for given key
|
85
|
+
#
|
86
|
+
# @example Returns a subhash
|
87
|
+
# { "foo" => { "bar" => "baz" } }.sub(:foo)
|
88
|
+
# # => { bar: "baz" }
|
89
|
+
#
|
90
|
+
# @example Returns an empty hash if the requested part is not a hash
|
91
|
+
# { foo: :bar }.sub(:foo)
|
92
|
+
# # => {}
|
93
|
+
#
|
94
|
+
# @example Returns an empty hash if the requested part is absent
|
95
|
+
# { foo: { bar: "baz" } }.sub(:bar)
|
96
|
+
# # => {}
|
97
|
+
#
|
98
|
+
# @param [#to_sym] key
|
99
|
+
#
|
100
|
+
# @return [Hash]
|
101
|
+
def sub(key)
|
102
|
+
data = normalize[key.to_sym]
|
103
|
+
data.instance_of?(Hash) ? data : {}
|
104
|
+
end
|
105
|
+
|
106
|
+
end # class Hash
|
107
|
+
|
108
|
+
refine Array do
|
109
|
+
|
110
|
+
# Treats the array as parts of path and returns the filename
|
111
|
+
#
|
112
|
+
# @exapmle
|
113
|
+
# ["/foo", "/bar/", "baz.qux"].to_path
|
114
|
+
# # => "/foo/bar/baz.qux"
|
115
|
+
#
|
116
|
+
# @example Ignores nils
|
117
|
+
# [nil, :foo].to_path
|
118
|
+
# # => "foo"
|
119
|
+
#
|
120
|
+
# @example Converts the empty array to the empty string
|
121
|
+
# [].to_path
|
122
|
+
# # => ""
|
123
|
+
#
|
124
|
+
# @return [String]
|
125
|
+
def to_path
|
126
|
+
File.join compact.map(&:to_s)
|
127
|
+
end
|
128
|
+
|
129
|
+
end # class Array
|
130
|
+
|
131
|
+
end # module Patches
|
132
|
+
|
133
|
+
end # module Storage
|
134
|
+
|
135
|
+
end # module Hexx
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Hexx
|
4
|
+
|
5
|
+
module Storage
|
6
|
+
|
7
|
+
# Factory for repositories
|
8
|
+
# @private
|
9
|
+
module Repositories
|
10
|
+
class << self
|
11
|
+
|
12
|
+
# Factory method to build a repository settings object for given adapter
|
13
|
+
#
|
14
|
+
# @option [#to_sym] :adapter The name of the +ROM+ adapter
|
15
|
+
# @param [Hash] options The options to build the settings object
|
16
|
+
#
|
17
|
+
# @return [Hexx::Storage::Repositories::Base] repository settings object
|
18
|
+
def build(adapter:, **options)
|
19
|
+
inflect(adapter).new(options)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def inflect(adapter)
|
25
|
+
inflections.fetch adapter.to_sym do
|
26
|
+
fail ArgumentError.new "Unknown adapter: #{ adapter }"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def inflections
|
31
|
+
{ sql: SQL, memory: Memory }
|
32
|
+
end
|
33
|
+
|
34
|
+
end # eigenclass
|
35
|
+
|
36
|
+
end # module Repositories
|
37
|
+
|
38
|
+
end # module Storage
|
39
|
+
|
40
|
+
end # module Hexx
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
module Hexx
|
6
|
+
|
7
|
+
module Storage
|
8
|
+
|
9
|
+
module Repositories
|
10
|
+
|
11
|
+
# Base class for a single repository settings
|
12
|
+
#
|
13
|
+
# @abstract
|
14
|
+
class Base
|
15
|
+
using Patches
|
16
|
+
|
17
|
+
# @private
|
18
|
+
def initialize(root:, log: nil, **)
|
19
|
+
@root = root
|
20
|
+
@log = log
|
21
|
+
prepare_logpath
|
22
|
+
end
|
23
|
+
|
24
|
+
# @!attribute [r] root
|
25
|
+
# Returns the root the {#log} path is relative to
|
26
|
+
#
|
27
|
+
# @return [String]
|
28
|
+
attr_reader :root
|
29
|
+
|
30
|
+
# @!attribute [r] log
|
31
|
+
# Returns the path to the logfile, relative to the {#root}
|
32
|
+
#
|
33
|
+
# @return [String, nil]
|
34
|
+
attr_reader :log
|
35
|
+
|
36
|
+
# Returns the logger object
|
37
|
+
#
|
38
|
+
# @return [Logger]
|
39
|
+
def logger
|
40
|
+
@logger ||= Logger.new _logfile, "a"
|
41
|
+
end
|
42
|
+
|
43
|
+
# @private
|
44
|
+
def respond_to_missing?(*)
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Provides adapter-agnostic interface
|
51
|
+
def method_missing(*)
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def prepare_logpath
|
56
|
+
return unless @log
|
57
|
+
FileUtils.mkdir_p File.dirname(_logfile)
|
58
|
+
end
|
59
|
+
|
60
|
+
def _logfile
|
61
|
+
@logfile ||= @log ? [root, log].to_path : StringIO.new
|
62
|
+
end
|
63
|
+
|
64
|
+
end # class Memory
|
65
|
+
|
66
|
+
end # module Repositories
|
67
|
+
|
68
|
+
end # module Storage
|
69
|
+
|
70
|
+
end # module Hexx
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Hexx
|
4
|
+
|
5
|
+
module Storage
|
6
|
+
|
7
|
+
module Repositories
|
8
|
+
|
9
|
+
# Default container for the ROM repository settings
|
10
|
+
class Memory < Base
|
11
|
+
using Patches
|
12
|
+
|
13
|
+
# Returns settings for ROM repository setup
|
14
|
+
#
|
15
|
+
# @return [Array]
|
16
|
+
def settings
|
17
|
+
adapter
|
18
|
+
end
|
19
|
+
|
20
|
+
# The name of the current adapter
|
21
|
+
#
|
22
|
+
# @return [Symbol]
|
23
|
+
def adapter
|
24
|
+
:memory
|
25
|
+
end
|
26
|
+
|
27
|
+
end # class Memory
|
28
|
+
|
29
|
+
end # module Repositories
|
30
|
+
|
31
|
+
end # module Storage
|
32
|
+
|
33
|
+
end # module Hexx
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Hexx
|
4
|
+
|
5
|
+
module Storage
|
6
|
+
|
7
|
+
module Repositories
|
8
|
+
|
9
|
+
# Container for the ROM :sql repository settings
|
10
|
+
class SQL < Base
|
11
|
+
using Patches
|
12
|
+
|
13
|
+
# @private
|
14
|
+
def initialize(uri:, root: nil, log: nil, **options)
|
15
|
+
@uri = uri
|
16
|
+
@options = options
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
# The path to migration directory for the current repository
|
21
|
+
#
|
22
|
+
# @return [String, nil]
|
23
|
+
attr_reader :uri, :migrations, :options
|
24
|
+
|
25
|
+
# Sets the path to migration directory for the current repository
|
26
|
+
#
|
27
|
+
# @param [#to_s] path
|
28
|
+
#
|
29
|
+
# @return [String] if the directory exists
|
30
|
+
# @return [nil] if the directory is absent
|
31
|
+
def migrations=(path)
|
32
|
+
@migrations ||= path.to_s.to_folder
|
33
|
+
end
|
34
|
+
|
35
|
+
# Retuns the +Sequel+ connection for the repository
|
36
|
+
#
|
37
|
+
# @return [Sequel::Database]
|
38
|
+
def conn
|
39
|
+
@conn ||= begin
|
40
|
+
conn = Sequel.connect uri, **options
|
41
|
+
conn.loggers << logger
|
42
|
+
conn
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the migrator for the repository
|
47
|
+
#
|
48
|
+
# @return [ROM::SQL::Migration::Migrator]
|
49
|
+
def migrator
|
50
|
+
@migrator ||= begin
|
51
|
+
rom = ROM::SQL::Migration::Migrator
|
52
|
+
null = Naught.build { |config| config.impersonate rom }
|
53
|
+
klass = migrations ? rom : null
|
54
|
+
klass.new conn, path: migrations
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns the name of the adapter
|
59
|
+
#
|
60
|
+
# @return [Symbol]
|
61
|
+
def adapter
|
62
|
+
:sql
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns the array of settings for the ROM repository
|
66
|
+
#
|
67
|
+
# @return [Array]
|
68
|
+
def settings
|
69
|
+
[adapter, (migrations ? [conn, migrator: migrator] : conn)]
|
70
|
+
end
|
71
|
+
|
72
|
+
end # class Memory
|
73
|
+
|
74
|
+
end # module Repositories
|
75
|
+
|
76
|
+
end # module Storage
|
77
|
+
|
78
|
+
end # module Hexx
|