refrigerator 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0c9e52b0092dba4efb2a5dff7fdb325f9c701edf
4
+ data.tar.gz: 24545654071f599ceb971ec29599e3275f7dd196
5
+ SHA512:
6
+ metadata.gz: de00ce2f90f0d7158a02e1fdd532f5f9d915bd3a84364158e86c1552ae6c0db3946b7456dbdd540eae77456b648a52f4017fedee79fb3be6522c62f82b2b51ac
7
+ data.tar.gz: 9c6d86426daedc0b3b5bf7ff25e8c5cdcaad9270d4600ef8b675631585dbc6ca83449caa514e8aa791e19e6ba0786ce53ce9cc4d7db8be262109951c30eb6301
data/CHANGELOG ADDED
@@ -0,0 +1,3 @@
1
+ === 1.0.0 (2017-02-17)
2
+
3
+ * Initial Public Release
data/MIT-LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2017 Jeremy Evans
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to
5
+ deal in the Software without restriction, including without limitation the
6
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ sell copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,155 @@
1
+ = Refrigerator
2
+
3
+ Refrigerator offers an easy way to freeze all ruby core classes and modules.
4
+ It's designed to be used in production to make sure that no code is making
5
+ unexpected changes to core classes or modules at runtime.
6
+
7
+ = Installation
8
+
9
+ gem install refrigerator
10
+
11
+ = Source Code
12
+
13
+ Source code is available on GitHub at https://github.com/jeremyevans/ruby-refrigerator
14
+
15
+ = Usage
16
+
17
+ == freeze_core
18
+
19
+ After loading all of the code for your application, you can call the
20
+ +freeze_core+ method to freeze all core classes:
21
+
22
+ require 'refrigerator'
23
+ Refrigerator.freeze_core
24
+
25
+ This will freeze all core classes, so that modifications to them at runtime will
26
+ raise exceptions.
27
+
28
+ You can also pass an :except option to +freeze_core+ with an array of class names not to
29
+ freeze:
30
+
31
+ Refrigerator.freeze_core(:except=>['BasicObject'])
32
+
33
+ One reason to exclude certain classes is because you know they will be modified at
34
+ runtime. For example, tilt (the ruby template library) modifies BasicObject at runtime
35
+ as part of template compilation.
36
+
37
+ == check_require
38
+
39
+ Refrigerator also offers a +check_require+ method for checking libraries for modifications
40
+ to the core classes. It allows you to easily see what fails when when you try to require
41
+ the library with a frozen core, and offers some options that you can use to try to get
42
+ the require to not raise an exception. This allows you to see what changes the library
43
+ makes to the core classes. Here's an example of basic use:
44
+
45
+ Refrigerator.check_require('open3', :modules=>[:Open3])
46
+
47
+ The +check_require+ method takes the following options:
48
+
49
+ :modules :: Define the given module names before freezing the core (array of symbols)
50
+ :classes :: Define the given class names before freezing the core (array of symbols
51
+ or two element arrays with class name symbol and superclass name symbol)
52
+ :exclude :: Exclude the given class/module names from freezin (array of strings)
53
+ :depends :: Any dependencies to require before freezing the core (array of strings)
54
+
55
+ Without any options, +check_require+ will likely raise an exception, as it
56
+ freezes the core before requiring, and if the required files tries to add a
57
+ class or module to the global namespace, that will fail. The :modules and :classes
58
+ options exist so that you can predefine the class so that the required file
59
+ will reopen the existing class instead of defining a new class.
60
+
61
+ The :depends option can be easily used to load all dependencies of the required
62
+ file before freezing the core. This is also necessary in most cases, especially
63
+ when using the stdlib, since many stdlib files modify the core classes in ways
64
+ beyond adding modules or classes. The :exclude option is basically a last resort,
65
+ where you can disable the freezing of certain core classes, if you know the
66
+ required library modifies them.
67
+
68
+ Here's an example using Sequel, a ruby database toolkit:
69
+
70
+ Refrigerator.check_require 'sequel',
71
+ :depends=>%w'bigdecimal date thread time uri',
72
+ :modules=>[:Sequel]
73
+
74
+ And an example using Roda, a ruby web toolkit:
75
+
76
+ Refrigerator.check_require 'roda',
77
+ :depends=>%w'rack uri fileutils set tmpdir tempfile thread date time',
78
+ :classes=>[:Roda]
79
+
80
+ Note that many stdlib libraries will fail +check_require+ unless you use the
81
+ :exclude option, for example, +set+:
82
+
83
+ Refrigerator.check_require 'set',
84
+ :classes=>[:Set, [:SortedSet, :Set]]
85
+ # Fails due to Enumerable#to_set addition
86
+
87
+ === bin/check_require
88
+
89
+ refrigerator ships with a +check_require+ binary that offers access to
90
+ +Refrigerator.check_require+ from the command line. Here's the usage:
91
+
92
+ $ bin/check_require
93
+ Usage: check_require [options] path
94
+
95
+ Options:
96
+ -m, --modules [Module1,Module2] define given modules under Object before freezing core classes
97
+ -c, --classes [Class1,Class2] define given modules under Object before freezing core classes
98
+ -r, --require [foo,bar/baz] require given libraries before freezing core classes
99
+ -e, --exclude [Object,Array] exclude given core classes from freezing
100
+ -h, -?, --help Show this message
101
+
102
+ You can use this to easily check ruby libraries for issues when requiring. For example, let's try
103
+ with +open3+:
104
+
105
+ $ bin/check_require open3
106
+ /usr/local/lib/ruby/2.4/open3.rb:32:in `<top (required)>': can't modify frozen #<Class:Object> (RuntimeError)
107
+ from /usr/local/lib/ruby/2.4/rubygems/core_ext/kernel_require.rb:55:in `require'
108
+ from /usr/local/lib/ruby/2.4/rubygems/core_ext/kernel_require.rb:55:in `require'
109
+ from /data/code/ruby-refrigerator/lib/refrigerator.rb:35:in `check_require'
110
+ from bin/check_require:42:in `<main>'
111
+ $ bin/check_require -m Open3 open3
112
+
113
+ As displayed above, open3 does not modify any core classes, beyond defining the +Open3+ module.
114
+
115
+ Let's try with +date+:
116
+
117
+ $ bin/check_require date
118
+ /usr/local/lib/ruby/2.4/rubygems/core_ext/kernel_require.rb:55:in `require': can't modify frozen #<Class:Object> (RuntimeError)
119
+ from /usr/local/lib/ruby/2.4/rubygems/core_ext/kernel_require.rb:55:in `require'
120
+ from /usr/local/lib/ruby/2.4/date.rb:4:in `<top (required)>'
121
+ from /usr/local/lib/ruby/2.4/rubygems/core_ext/kernel_require.rb:55:in `require'
122
+ from /usr/local/lib/ruby/2.4/rubygems/core_ext/kernel_require.rb:55:in `require'
123
+ from /data/code/ruby-refrigerator/lib/refrigerator.rb:35:in `check_require'
124
+ from bin/check_require:42:in `<main>'
125
+ $ bin/check_require -e Object date
126
+ /usr/local/lib/ruby/2.4/rubygems/core_ext/kernel_require.rb:55:in `require': can't modify frozen class (RuntimeError)
127
+ from /usr/local/lib/ruby/2.4/rubygems/core_ext/kernel_require.rb:55:in `require'
128
+ from /usr/local/lib/ruby/2.4/date.rb:4:in `<top (required)>'
129
+ from /usr/local/lib/ruby/2.4/rubygems/core_ext/kernel_require.rb:55:in `require'
130
+ from /usr/local/lib/ruby/2.4/rubygems/core_ext/kernel_require.rb:55:in `require'
131
+ from /data/code/ruby-refrigerator/lib/refrigerator.rb:35:in `check_require'
132
+ from bin/check_require:42:in `<main>'
133
+ $ bin/check_require -e Object,Time date
134
+
135
+ The first failure is because +date+ defines the +Date+ and +DateTime+ classes. Because
136
+ +date+ is a native library written in C, you can't define the classes in advance, so
137
+ you have to exclude the freezing of +Object+. Note that it still fails in that case, but
138
+ it doesn't even tell you why. It turns out the reason is that +date+ also adds <tt>Time#to_date</tt>
139
+ and other methods, so you need to exclude the freezing of +Time+ as well.
140
+
141
+ Here are a couple more examples, using Sequel and Roda:
142
+
143
+ bin/check_require -m Sequel -r bigdecimal,date,thread,time,uri sequel
144
+ bin/check_require -c Roda -r rack,uri,fileutils,set,tmpdir,tempfile,thread,date,time roda
145
+
146
+ Note that +bin/check_require+'s `-c` option does not support classes that do not subclass
147
+ from +Object+. You must use the +Refrigerator.check_require+ API in that case.
148
+
149
+ = License
150
+
151
+ MIT
152
+
153
+ = Author
154
+
155
+ Jeremy Evans <code@jeremyevans.net>
data/Rakefile ADDED
@@ -0,0 +1,46 @@
1
+ require "rake"
2
+ require "rake/clean"
3
+
4
+ CLEAN.include ["refrigerator-*.gem", "rdoc", "coverage"]
5
+
6
+ desc "Build refrigerator gem"
7
+ task :package=>[:clean] do |p|
8
+ sh %{#{FileUtils::RUBY} -S gem build refrigerator.gemspec}
9
+ end
10
+
11
+ ### Specs
12
+
13
+ desc "Run specs"
14
+ task :spec do
15
+ sh "#{FileUtils::RUBY} test/refrigerator_test.rb"
16
+ end
17
+
18
+ task :default => :spec
19
+
20
+ ### RDoc
21
+
22
+ RDOC_DEFAULT_OPTS = ["--quiet", "--line-numbers", "--inline-source", '--title', 'Refrigerator: Freeze core ruby classes']
23
+
24
+ begin
25
+ gem 'rdoc'
26
+ gem 'hanna-nouveau'
27
+ RDOC_DEFAULT_OPTS.concat(['-f', 'hanna'])
28
+ rescue Gem::LoadError
29
+ end
30
+
31
+ rdoc_task_class = begin
32
+ require "rdoc/task"
33
+ RDoc::Task
34
+ rescue LoadError
35
+ require "rake/rdoctask"
36
+ Rake::RDocTask
37
+ end
38
+
39
+ RDOC_OPTS = RDOC_DEFAULT_OPTS + ['--main', 'README.rdoc']
40
+
41
+ rdoc_task_class.new do |rdoc|
42
+ rdoc.rdoc_dir = "rdoc"
43
+ rdoc.options += RDOC_OPTS
44
+ rdoc.rdoc_files.add %w"README.rdoc CHANGELOG MIT-LICENSE lib/**/*.rb"
45
+ end
46
+
data/bin/check_require ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen-string-literal: true
3
+
4
+ require 'optparse'
5
+ require File.join(File.expand_path(__FILE__), '../../lib/refrigerator')
6
+
7
+ cr_opts = {:modules => [], :classes => [], :depends => [], :exclude => []}
8
+
9
+ options = OptionParser.new do |opts|
10
+ opts.banner = "Usage: check_require [options] path"
11
+ opts.separator ""
12
+ opts.separator "Options:"
13
+
14
+ opts.on_tail("-h", "-?", "--help", "Show this message") do
15
+ puts opts
16
+ exit
17
+ end
18
+
19
+ opts.on("-m", "--modules [Module1,Module2]", "define given modules under Object before freezing core classes") do |v|
20
+ cr_opts[:modules].concat v.split(',').map(&:to_sym)
21
+ end
22
+
23
+ opts.on("-c", "--classes [Class1,Class2]", "define given modules under Object before freezing core classes") do |v|
24
+ cr_opts[:classes].concat v.split(',').map(&:to_sym)
25
+ end
26
+
27
+ opts.on("-r", "--require [foo,bar/baz]", "require given libraries before freezing core classes") do |v|
28
+ cr_opts[:depends].concat v.split(',')
29
+ end
30
+
31
+ opts.on("-e", "--exclude [Object,Array]", "exclude given core classes from freezing") do |v|
32
+ cr_opts[:exclude].concat v.split(',')
33
+ end
34
+ end
35
+ options.parse!
36
+
37
+ if ARGV.length != 1
38
+ puts options
39
+ exit 1
40
+ end
41
+
42
+ Refrigerator.check_require(ARGV.first, cr_opts)
@@ -0,0 +1,39 @@
1
+ # Refrigerator allows for easily freezing core classes and modules.
2
+ module Refrigerator
3
+ version_int = RUBY_VERSION[0..2].sub('.', '').to_i
4
+ version_int = 24 if version_int > 24
5
+
6
+ # Array of strings containing class or module names.
7
+ CORE_MODULES = File.read(File.expand_path(File.join(File.expand_path(__FILE__), "../../module_names/#{version_int}.txt"))).
8
+ split.
9
+ select{|m| eval("defined?(#{m})")}.
10
+ each(&:freeze).
11
+ freeze
12
+
13
+ # Default frozen options hash
14
+ OPTS = {}.freeze
15
+
16
+ # Freeze core classes and modules. Options:
17
+ # :except :: Don't freeze any of the classes modules listed (array of strings)
18
+ def self.freeze_core(opts=OPTS)
19
+ (CORE_MODULES - Array(opts[:except])).each{|m| eval(m).freeze}
20
+ nil
21
+ end
22
+
23
+ # Check that requiring a given file does not modify any core classes. Options:
24
+ # :depends :: Require the given files before freezing the core (array of strings)
25
+ # :modules :: Define the given modules in the Object namespace before freezing the core (array of module name symbols)
26
+ # :classes :: Define the given classes in the Object namespace before freezing the core (array of either class name Symbols or
27
+ # array with class Name Symbol and Superclass name string)
28
+ # :except :: Don't freeze any of the classes modules listed (array of strings)
29
+ def self.check_require(file, opts=OPTS)
30
+ require 'rubygems'
31
+ Array(opts[:depends]).each{|f| require f}
32
+ Array(opts[:modules]).each{|m| Object.const_set(m, Module.new)}
33
+ Array(opts[:classes]).each{|c, *sc| Object.const_set(c, Class.new(sc.empty? ? Object : eval(sc.first)))}
34
+ freeze_core(:except=>%w'Gem Gem::Specification'+Array(opts[:exclude]))
35
+ require file
36
+ end
37
+
38
+ freeze
39
+ end
@@ -0,0 +1,157 @@
1
+ ArgumentError
2
+ Array
3
+ Bignum
4
+ Binding
5
+ Class
6
+ Comparable
7
+ Continuation
8
+ Data
9
+ Dir
10
+ EOFError
11
+ Enumerable
12
+ Enumerable::Enumerator
13
+ Errno
14
+ Errno::E2BIG
15
+ Errno::EACCES
16
+ Errno::EADDRINUSE
17
+ Errno::EADDRNOTAVAIL
18
+ Errno::EAFNOSUPPORT
19
+ Errno::EAGAIN
20
+ Errno::EALREADY
21
+ Errno::EBADF
22
+ Errno::EBUSY
23
+ Errno::ECHILD
24
+ Errno::ECONNABORTED
25
+ Errno::ECONNREFUSED
26
+ Errno::ECONNRESET
27
+ Errno::EDEADLK
28
+ Errno::EDESTADDRREQ
29
+ Errno::EDOM
30
+ Errno::EDQUOT
31
+ Errno::EEXIST
32
+ Errno::EFAULT
33
+ Errno::EFBIG
34
+ Errno::EHOSTDOWN
35
+ Errno::EHOSTUNREACH
36
+ Errno::EIDRM
37
+ Errno::EILSEQ
38
+ Errno::EINPROGRESS
39
+ Errno::EINTR
40
+ Errno::EINVAL
41
+ Errno::EIO
42
+ Errno::EISCONN
43
+ Errno::EISDIR
44
+ Errno::ELOOP
45
+ Errno::EMFILE
46
+ Errno::EMLINK
47
+ Errno::EMSGSIZE
48
+ Errno::ENAMETOOLONG
49
+ Errno::ENETDOWN
50
+ Errno::ENETRESET
51
+ Errno::ENETUNREACH
52
+ Errno::ENFILE
53
+ Errno::ENOBUFS
54
+ Errno::ENODEV
55
+ Errno::ENOENT
56
+ Errno::ENOEXEC
57
+ Errno::ENOLCK
58
+ Errno::ENOMEM
59
+ Errno::ENOMSG
60
+ Errno::ENOPROTOOPT
61
+ Errno::ENOSPC
62
+ Errno::ENOSYS
63
+ Errno::ENOTBLK
64
+ Errno::ENOTCONN
65
+ Errno::ENOTDIR
66
+ Errno::ENOTEMPTY
67
+ Errno::ENOTSOCK
68
+ Errno::ENOTTY
69
+ Errno::ENXIO
70
+ Errno::EOPNOTSUPP
71
+ Errno::EOVERFLOW
72
+ Errno::EPERM
73
+ Errno::EPFNOSUPPORT
74
+ Errno::EPIPE
75
+ Errno::EPROTONOSUPPORT
76
+ Errno::EPROTOTYPE
77
+ Errno::ERANGE
78
+ Errno::EREMOTE
79
+ Errno::EROFS
80
+ Errno::ESHUTDOWN
81
+ Errno::ESOCKTNOSUPPORT
82
+ Errno::ESPIPE
83
+ Errno::ESRCH
84
+ Errno::ESTALE
85
+ Errno::ETIMEDOUT
86
+ Errno::ETOOMANYREFS
87
+ Errno::ETXTBSY
88
+ Errno::EUSERS
89
+ Errno::EXDEV
90
+ Exception
91
+ FalseClass
92
+ File
93
+ File::Constants
94
+ File::Stat
95
+ FileTest
96
+ Fixnum
97
+ Float
98
+ FloatDomainError
99
+ GC
100
+ Hash
101
+ IO
102
+ IOError
103
+ IndexError
104
+ Integer
105
+ Interrupt
106
+ Kernel
107
+ LoadError
108
+ LocalJumpError
109
+ Marshal
110
+ MatchData
111
+ Math
112
+ Method
113
+ Module
114
+ NameError
115
+ NameError::message
116
+ NilClass
117
+ NoMemoryError
118
+ NoMethodError
119
+ NotImplementedError
120
+ Numeric
121
+ Object
122
+ ObjectSpace
123
+ Precision
124
+ Proc
125
+ Process
126
+ Process::GID
127
+ Process::Status
128
+ Process::Sys
129
+ Process::UID
130
+ Range
131
+ RangeError
132
+ Regexp
133
+ RegexpError
134
+ RuntimeError
135
+ ScriptError
136
+ SecurityError
137
+ Signal
138
+ SignalException
139
+ StandardError
140
+ StopIteration
141
+ String
142
+ Struct
143
+ Struct::Tms
144
+ Symbol
145
+ SyntaxError
146
+ SystemCallError
147
+ SystemExit
148
+ SystemStackError
149
+ Thread
150
+ ThreadError
151
+ ThreadGroup
152
+ Time
153
+ TrueClass
154
+ TypeError
155
+ UnboundMethod
156
+ ZeroDivisionError
157
+ fatal