ruby_smart-support 1.0.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.
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ unless Hash.method_defined? "to_md5"
4
+ class Hash
5
+ # returns the md5 of any hash by using the to_s method
6
+ # @return [String] md5
7
+ def to_md5
8
+ Digest::MD5.hexdigest(Hash[self.sort].to_s)
9
+ end
10
+ end
11
+ end
12
+
13
+ # idea by https://stackoverflow.com/questions/9786264/all-possible-combinations-from-a-hash-of-arrays-in-ruby
14
+ unless Hash.method_defined? "product"
15
+ class Hash
16
+ # creates a 'product' of all values per existing key as a combination
17
+ #
18
+ # hash = { first: [:a,:b], second: [:x,:c]}
19
+ # > [{:first=>:a, :second=>:x}, {:first=>:a, :second=>:c}, {:first=>:b, :second=>:x}, {:first=>:b, :second=>:c}]
20
+ #
21
+ # @return [Array] combinations
22
+ def product
23
+ product = values[0].product(*values[1..-1])
24
+ product.map{|p| Hash[keys.zip p]}
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ unless Object.method_defined? "numeric?"
4
+ class Object
5
+ # returns true if this object is a numeric or a kind of numeric
6
+ # @return [Boolean] numeric
7
+ def numeric?
8
+ return true if self.is_a?(Numeric)
9
+ return true if self.to_s =~ /\A\d+\Z/
10
+ return true if Float(self) rescue false
11
+
12
+ false
13
+ end
14
+ end
15
+ end
16
+
17
+ unless Object.method_defined? "boolean?"
18
+ class Object
19
+ # return true if object is a boolean class (TrueClass or FalseClass)
20
+ # @return [Boolean] boolean
21
+ def boolean?
22
+ self.is_a?(TrueClass) || self.is_a?(FalseClass)
23
+ end
24
+ end
25
+ end
26
+
27
+ unless Object.method_defined? "missing_method?"
28
+ class Object
29
+ # returns true if method is missing.
30
+ # the second optional parameter <tt>check_ancestors</tt> prevents to check it's ancestors by providing a *false* value
31
+ # @param [Symbol] name - the method's name to check
32
+ # @param [Boolean] check_ancestors - check class ancestors (default: true)
33
+ # @return [Boolean] missing_method
34
+ def missing_method?(name, check_ancestors = true)
35
+ !self.instance_methods(check_ancestors).include?(name)
36
+ end
37
+ end
38
+ end
39
+
40
+ unless Object.method_defined? "alias_missing_method"
41
+ class Object
42
+ # creates an alias for provided method if it's missing.
43
+ # the third optional parameter <tt>check_ancestors</tt> prevents to check it's ancestors by providing a *false* value
44
+ # @param [Symbol] new - new method name
45
+ # @param [Symbol] old - old method name
46
+ # @param [Boolean] check_ancestors - check class ancestors (default: true)
47
+ def alias_missing_method(new, old, check_ancestors = true)
48
+ alias_method(new, old) if missing_method?(new, check_ancestors)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ unless String.method_defined? "to_boolean"
6
+ class String
7
+ # converts a string to 'boolean'
8
+ # @return [Boolean] bool
9
+ def to_boolean
10
+ ['true','1'].include? self.downcase
11
+ end
12
+ end
13
+ end
14
+
15
+ unless String.method_defined? "to_md5"
16
+ class String
17
+ # returns the md5 of this string
18
+ # @return [String] md5
19
+ def to_md5
20
+ ::Digest::MD5.hexdigest(self)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "core_ext/ruby/array"
4
+ require_relative "core_ext/ruby/float"
5
+ require_relative "core_ext/ruby/hash"
6
+ require_relative "core_ext/ruby/object"
7
+ require_relative "core_ext/ruby/string"
8
+
9
+ require_relative "gem_info"
10
+
11
+ # activesupport related stuff
12
+ if RubySmart::Support::GemInfo.loaded?('activesupport')
13
+ require_relative "core_ext/activesupport/hash"
14
+ end
15
+
16
+ # load Rails related stuff
17
+ if RubySmart::Support::GemInfo.loaded?('rails')
18
+ # since the Rails::Info.property block-returns are stored on call we need to wait
19
+ # until we have information about the rails application.
20
+ # This is done with the following hook
21
+ ActiveSupport.on_load(:before_initialize) do
22
+ require_relative "core_ext/rails/info"
23
+ end
24
+ end
25
+
26
+ # load Rake related stuff
27
+ if RubySmart::Support::GemInfo.loaded?('rake')
28
+ require_relative "core_ext/rake/task"
29
+ end
@@ -0,0 +1,207 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubySmart
4
+ module Support
5
+ module GemInfo
6
+
7
+ # regexp to detect a gems path
8
+ GEM_PATH_REGEXP = /\/gems\//
9
+
10
+ # regexp to resolves a name from a gem path
11
+ GEM_NAME_REGEXP = /\/gems\/([A-za-z0-9_\-]+)\-[\d\w\-\_\.]+\//
12
+
13
+ # regexp to resolves a feature from a gem path
14
+ FEATURE_NAME_REGEXP = /\/gems\/([A-za-z0-9_\-]+)\-[\d\w\-\_\.]+\/lib\/(?:\1\/(\1)|([A-za-z0-9_\-]+))\.rb/
15
+
16
+ # returns a hash of all installed gems with it's versions
17
+ #
18
+ # GemInfo.installed
19
+ # > {'bundler' => ['2.2.30', '1.2.3']}
20
+ #
21
+ # @return [Hash{<name> => Array[<version>]}] gem name-versions hash
22
+ def self.installed
23
+ Hash[::Gem::Specification.reduce({}) { |m, g| m[g.name] ||= []; m[g.name] << g.version.to_s; m }.sort]
24
+ end
25
+
26
+ # returns true if the provided gem name is installed.
27
+ # the optional version requirement matches against any available version
28
+ #
29
+ # GemInfo.installed?('bundler')
30
+ # > true
31
+ #
32
+ # GemInfo.installed?('bundler', '~> 3.0')
33
+ # > false
34
+ #
35
+ # @param [String] name - the gem name
36
+ # @param [nil, String] version - optional version requirement
37
+ # @return [Boolean]
38
+ def self.installed?(name, version = nil)
39
+ installed.key?(name) && (version.nil? || installed[name].any? { |gem_version| match?(version, gem_version) })
40
+ end
41
+
42
+ # returns a hash of all loaded gems with its current version
43
+ #
44
+ # GemInfo.loaded
45
+ # > {'bundler' => '2.2.30'}
46
+ #
47
+ # @return [Hash{<name> => <version>}] gem name-version hash
48
+ def self.loaded
49
+ Hash[::Gem.loaded_specs.values.map { |g| [g.name, g.version.to_s] }.sort]
50
+ end
51
+
52
+ # returns true if the provided gem name is loaded.
53
+ # the optional version requirement matches against the loaded version
54
+ #
55
+ # GemInfo.loaded?('bundler')
56
+ # > true
57
+ #
58
+ # GemInfo.loaded?('bundler', '~> 3.0')
59
+ # > false
60
+ #
61
+ # @param [String] name - the gem name
62
+ # @param [nil, String] version - optional version requirement
63
+ # @return [Boolean]
64
+ def self.loaded?(name, version = nil)
65
+ loaded.key?(name) && (version.nil? || match?(version, loaded[name]))
66
+ end
67
+
68
+ # returns an array of all active gems
69
+ #
70
+ # GemInfo.active
71
+ # > ['bundler']
72
+ #
73
+ # @return [Array<name>}] gem names
74
+ def self.active
75
+ $LOADED_FEATURES.
76
+ select { |feature| feature.match(GEM_PATH_REGEXP) }.
77
+ map { |feature|
78
+ m = feature.match(GEM_NAME_REGEXP)
79
+ m ? m[1] : File.dirname(feature).split('/').last
80
+ }.
81
+ uniq.
82
+ sort
83
+ end
84
+
85
+ # returns true if the provided gem name is active.
86
+ # the optional version requirement matches against the *loaded* version
87
+ #
88
+ # GemInfo.active?('bundler')
89
+ # > true
90
+ #
91
+ # GemInfo.active?('bundler', '~> 3.0')
92
+ # > false
93
+ #
94
+ # @param [String] name - the gem name
95
+ # @param [nil, String] version - optional version requirement
96
+ # @return [Boolean] activated?
97
+ def self.active?(name, version = nil)
98
+ active.include?(name) && (version.nil? || match?(version, version(name)))
99
+ end
100
+
101
+ # returns an array of all loaded features
102
+ #
103
+ # GemInfo.features
104
+ # > ['active_support','bundler']
105
+ #
106
+ # @return [Array<String>}] module names
107
+ def self.features
108
+ $LOADED_FEATURES.
109
+ select { |feature| feature.match(GEM_PATH_REGEXP) }.
110
+ map { |feature|
111
+ m = feature.match(FEATURE_NAME_REGEXP)
112
+ m ? (m[2] || m[3]) : nil
113
+ }.
114
+ uniq.
115
+ compact.
116
+ sort
117
+ end
118
+
119
+ # returns true if the provided feature is loaded.
120
+ #
121
+ # GemInfo.feature?('active_support')
122
+ # > true
123
+ #
124
+ # @param [String] name - the feature name
125
+ # @return [Boolean]
126
+ def self.feature?(name)
127
+ features.include?(name)
128
+ end
129
+
130
+ # returns the currently loaded gem version
131
+ #
132
+ # GemInfo.version 'bundler'
133
+ # > '2.2.30'
134
+ #
135
+ # @return [String,nil] current gem version - return nil if the gem wasn't found
136
+ def self.version(name)
137
+ loaded[name]
138
+ end
139
+
140
+ # safe requires a feature by provided name & optional gem
141
+ #
142
+ # GemInfo.safe_require('rake')
143
+ # > true
144
+ #
145
+ # GemInfo.safe_require('active_support')
146
+ # > true
147
+ #
148
+ # GemInfo.safe_require('action_view/helpers/date_helper','actionview', '> 0.1.0')
149
+ # > true
150
+ #
151
+ # @param [String] path - the resource path
152
+ # @param [nil,String] gem - optional gem name
153
+ # @param [nil,String] version - optional gem version compare string
154
+ # @return [Boolean]
155
+ def self.safe_require(path, gem = nil, version = nil)
156
+ # check for missing gem (nicely check if the feature name differs to the gem name)
157
+ return false if !gem.nil? && path != gem && !active?(gem, version)
158
+
159
+ # try to require the feature
160
+ begin
161
+ require path
162
+ rescue LoadError, NameError
163
+ return false
164
+ end
165
+
166
+ # just the final true result
167
+ true
168
+ end
169
+
170
+ # compares two versions against each other
171
+ #
172
+ # match?('4.3.0', '4.3.0')
173
+ # > true
174
+ #
175
+ # match?('>= 3.0', '4.3.0')
176
+ # > true
177
+ #
178
+ # match?( '~> 3.1', '3.3.0')
179
+ # > true
180
+ #
181
+ # match?( '~> 1.1.0', '0.1.0')
182
+ # > false
183
+ def self.match?(version_requirement, version_current)
184
+ return true if version_requirement.nil?
185
+
186
+ # split version compare operators
187
+ version_requirement_str = version_requirement.gsub(/([=~>< ]+)/, '')
188
+ version_requirement_op = $1
189
+
190
+ # create plain versions without compare operators
191
+ gem_version_current = Gem::Version.new(version_current)
192
+ gem_version_requirement = Gem::Version.new(version_requirement_str)
193
+
194
+ # check if required version is PRE & current is NOT
195
+ if gem_version_requirement.prerelease? && !gem_version_current.prerelease?
196
+ version_requirement = "#{version_requirement_op}#{gem_version_requirement.release}"
197
+ elsif gem_version_requirement.prerelease? != gem_version_current.prerelease?
198
+ # check if required version PRE doesn't equal current version PRE
199
+ version_current = gem_version_current.release.to_s
200
+ end
201
+ # else is not required here: either its PRE && PRE || !PRE && !PRE
202
+
203
+ Gem::Dependency.new('', version_requirement).match?('', version_current)
204
+ end
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubySmart
4
+ module Support
5
+ # Returns the version of the currently loaded module as a <tt>Gem::Version</tt>
6
+ def self.gem_version
7
+ Gem::Version.new VERSION::STRING
8
+ end
9
+
10
+ module VERSION
11
+ MAJOR = 1
12
+ MINOR = 0
13
+ TINY = 0
14
+ PRE = nil
15
+
16
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
17
+
18
+ def self.to_s
19
+ STRING
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'io/console'
4
+
5
+ module RubySmart
6
+ module Support
7
+ module ThreadInfo
8
+
9
+ # defines a array of types which will be checked by resolving the current thread type
10
+ TYPE_ORDER = [:rake, :console, :server].freeze
11
+
12
+ # returns true if this is a running rake process
13
+ # @return [Boolean]
14
+ def self.rake?
15
+ !!defined?(Rake.application) && Rake.application.top_level_tasks.any?
16
+ end
17
+
18
+ # returns true if this is a running rails process
19
+ # @return [Boolean]
20
+ def self.rails?
21
+ !!defined?(Rails.application)
22
+ end
23
+
24
+ # returns true if this is a running console process
25
+ # @return [Boolean]
26
+ def self.console?
27
+ irb? || pry? || io_console?
28
+ end
29
+
30
+ # returns true if this is a running IRB process
31
+ # @return [Boolean]
32
+ def self.irb?
33
+ !!defined?(IRB)
34
+ end
35
+
36
+ # returns true if this is a running Pry process
37
+ # @return [Boolean]
38
+ def self.pry?
39
+ !!defined?(Pry)
40
+ end
41
+
42
+ # returns true if this is a running server process.
43
+ # currently only detects rails
44
+ # @return [Boolean]
45
+ def self.server?
46
+ !!defined?(Rails::Server)
47
+ end
48
+
49
+ # returns true if this is a running rails console process
50
+ # @return [Boolean]
51
+ def self.rails_console?
52
+ console? && !!(defined?(Rails::Console) || ENV['RAILS_ENV'])
53
+ end
54
+
55
+ # returns true if this is a running IO console process
56
+ # @return [Boolean]
57
+ def self.io_console?
58
+ !!defined?(IO.console) && !!IO.console
59
+ end
60
+
61
+ # returns true if this is a running thread process.
62
+ # as it always should ...
63
+ # @return [Boolean]
64
+ def self.thread?
65
+ !!thread
66
+ end
67
+
68
+ # returns the current thread
69
+ # @return [Thread]
70
+ def self.thread
71
+ ::Thread.current
72
+ end
73
+
74
+ # returns the current thread id
75
+ # @return [Integer] thread_id
76
+ def self.thread_id
77
+ thread? ? thread.object_id : 0
78
+ end
79
+
80
+ # returns the ascertained id
81
+ # @return [Integer] id
82
+ def self.process_object_id
83
+ return Rake.application.top_level_tasks.first.object_id if rake?
84
+ return Rails.application.object_id if rails?
85
+ thread_id
86
+ end
87
+
88
+ # returns the OS process id
89
+ # @return [Integer] id
90
+ def self.id
91
+ $$
92
+ end
93
+
94
+ # returns the current thread name
95
+ # - for rake tasks the task name
96
+ # - for rails the application name
97
+ #
98
+ # @return [String] name
99
+ def self.name
100
+ return Rake.application.top_level_tasks.first.to_s if rake?
101
+ return Rails.application.to_s.split('::').first if rails?
102
+ ''
103
+ end
104
+
105
+ # returns the current thread by logical order
106
+ # defined through const TYPE_ORDER
107
+ # @return [nil, Symbol]
108
+ def self.type
109
+ TYPE_ORDER.detect { |type| self.send("#{type}?") } || :unknown
110
+ end
111
+
112
+ # returns the thread type string
113
+ # @return [String] thread-info string
114
+ def self.info
115
+ strs = ["$#{id}", "[##{process_object_id}]","@ #{type}"]
116
+ strs << " :: #{name}" if name != ''
117
+ strs.join ' '
118
+ end
119
+
120
+ # returns true if thread has a 'window'
121
+ # @return [Boolean]
122
+ def self.windowed?
123
+ winsize[1] > 0
124
+ end
125
+
126
+ # returns the current windows size, if current IO has a window
127
+ # @return [Array<rows, columns>] winsize
128
+ def self.winsize
129
+ return IO.console.winsize if io_console?
130
+ return [ENV['ROWS'], ENV['COLUMNS']] unless ENV['ROWS'].nil? && ENV['COLUMNS'].nil?
131
+ [0, 0]
132
+ end
133
+
134
+ # return true if a log can be send to stdout
135
+ def self.stdout?
136
+ console? && windowed?
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'gem_version'
4
+
5
+ module RubySmart
6
+ module Support
7
+ # Returns the version of the currently loaded Gem as a <tt>Gem::Version</tt>
8
+ def self.version
9
+ gem_version
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "support/version"
4
+ require_relative "support/core_ext"
5
+ require_relative "support/gem_info"
6
+ require_relative "support/thread_info"
7
+
8
+ module RubySmart
9
+ module Support
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ruby_smart/support'
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ruby_smart/support/thread_info'
4
+
5
+ # Alias to <tt>ruby_smart/support/thread_info</tt>
6
+ ThreadInfo = RubySmart::Support::ThreadInfo
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/ruby_smart/support/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "ruby_smart-support"
7
+ spec.version = RubySmart::Support.version
8
+ spec.authors = ["Tobias Gonsior"]
9
+ spec.email = ["info@ruby-smart.org"]
10
+
11
+ spec.summary = "A toolkit of support libraries including GemInfo, ThreadInfo, Ruby core extensions & optionally activesupport extensions"
12
+ spec.description = <<DESC
13
+ RubySmart::Support is a toolkit of support libraries for Ruby - major features includes GemInfo & ThreadInfo, as well core extensions for Ruby & activesupport (if installed).
14
+ DESC
15
+
16
+ spec.homepage = "https://github.com/ruby-smart/support"
17
+ spec.license = "MIT"
18
+ spec.required_ruby_version = ">= 2.6.0"
19
+
20
+ spec.metadata["homepage_uri"] = spec.homepage
21
+ spec.metadata["source_code_uri"] = "https://github.com/ruby-smart/support"
22
+ spec.metadata["changelog_uri"] = "#{spec.metadata["source_code_uri"]}/blob/main/docs/CHANGELOG.md"
23
+
24
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
25
+
26
+ # Specify which files should be added to the gem when it is released.
27
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
28
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
29
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
30
+ end
31
+
32
+ spec.require_paths = ["lib"]
33
+
34
+ spec.add_development_dependency 'activesupport', ">= 4.0"
35
+ spec.add_development_dependency 'rspec', "~> 3.0"
36
+ spec.add_development_dependency 'rake', "~> 13.0"
37
+ spec.add_development_dependency 'simplecov', '~> 0.21'
38
+ end