refrigerator 1.0.0

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