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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +9 -0
  4. data/.metrics +9 -0
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +2 -0
  7. data/.travis.yml +12 -0
  8. data/.yardopts +3 -0
  9. data/Gemfile +12 -0
  10. data/Guardfile +14 -0
  11. data/LICENSE +21 -0
  12. data/README.md +351 -0
  13. data/Rakefile +22 -0
  14. data/config/metrics/STYLEGUIDE +230 -0
  15. data/config/metrics/cane.yml +5 -0
  16. data/config/metrics/churn.yml +6 -0
  17. data/config/metrics/flay.yml +2 -0
  18. data/config/metrics/metric_fu.yml +13 -0
  19. data/config/metrics/reek.yml +1 -0
  20. data/config/metrics/roodi.yml +24 -0
  21. data/config/metrics/rubocop.yml +73 -0
  22. data/config/metrics/saikuro.yml +3 -0
  23. data/config/metrics/simplecov.yml +8 -0
  24. data/config/metrics/yardstick.yml +37 -0
  25. data/hexx-storage.gemspec +28 -0
  26. data/lib/hexx-storage.rb +20 -0
  27. data/lib/hexx/storage.rb +17 -0
  28. data/lib/hexx/storage/base.rb +108 -0
  29. data/lib/hexx/storage/patches.rb +135 -0
  30. data/lib/hexx/storage/repositories.rb +40 -0
  31. data/lib/hexx/storage/repositories/base.rb +70 -0
  32. data/lib/hexx/storage/repositories/memory.rb +33 -0
  33. data/lib/hexx/storage/repositories/sql.rb +78 -0
  34. data/lib/hexx/storage/version.rb +13 -0
  35. data/spec/integration/storage.yml +9 -0
  36. data/spec/integration/storage_spec.rb +56 -0
  37. data/spec/spec_helper.rb +12 -0
  38. data/spec/tests/storage/base_spec.rb +178 -0
  39. data/spec/tests/storage/patches_spec.rb +202 -0
  40. data/spec/tests/storage/repositories/base_spec.rb +120 -0
  41. data/spec/tests/storage/repositories/memory_spec.rb +32 -0
  42. data/spec/tests/storage/repositories/sql_spec.rb +180 -0
  43. data/spec/tests/storage/repositories_spec.rb +66 -0
  44. data/spec/tests/storage_spec.rb +24 -0
  45. metadata +168 -0
@@ -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
@@ -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