ruby_smart-support 1.0.0

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