hexx-storage 0.0.2

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