Class: H8::Context
Overview
Context is an environment where javscripts and coffeescripts can be executed. Context holds its state between execution and can therefore consume large amount of memory if called repeatedly for a long time. You can release some of its memory by setting to null its global level variables, but the best is to allocate new Context as need leaving old instances fo GC. Please note that Context can not be GC’d if any of it objects is gated and held by ruby somewhere.
Class Method Summary collapse
- .can_access?(owner) ⇒ Boolean
-
.delete_handler(object, args) ⇒ Object
:nodoc: Internal handler to properly delete fields/keys from ruby Hashes or OpenStruct.
-
.eval(script, file_name: nil, **kwargs) ⇒ Value
Execute script in a new context with optionally set vars.
-
.secure_call(instance, method, args = nil) ⇒ Object
Secure gate for JS to securely access ruby class properties (methods with no args) and methods.
Instance Method Summary collapse
-
#[]=(name, value) ⇒ Object
Set variable/class for the context.
-
#coffee(script, **kwargs) ⇒ Object
Compile and execute coffeescript, taking same arguments as #eval.
-
#eval(script, max_time: 0, timeout: 0, file_name: nil) {|_self| ... } ⇒ Value
Execute a given script on the current context with optionally limited execution time.
-
#initialize(noglobals: false, **kwargs) ⇒ Context
constructor
Create new context optionally providing variables hash.
-
#safe_proc_call(proc, args) ⇒ Object
This is workaround for buggy rb_proc_call which produces segfaults if proc is not exactly a proc, so we call it like this:.
-
#set_all(**kwargs) ⇒ Object
set variables from keyword arguments to this context see #[]= for details.
Constructor Details
#initialize(noglobals: false, **kwargs) ⇒ Context
Create new context optionally providing variables hash
96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/h8/context.rb', line 96 def initialize noglobals: false, **kwargs @idcount = 0 set_all **kwargs _set_var '___create_ruby_class', -> (cls, args) { _do_create_ruby_class cls, args } self[:debug] = -> (*args) { puts args.join(' ') } noglobals or execute_script 'globals.coffee' end |
Class Method Details
.can_access?(owner) ⇒ Boolean
244 245 246 247 |
# File 'lib/h8/context.rb', line 244 def self.can_access?(owner) return true if owner.is_a?(Array.class) owner != Object.class && owner != Kernel && owner != BasicObject.class end |
.delete_handler(object, args) ⇒ Object
:nodoc: Internal handler to properly delete fields/keys from ruby Hashes or OpenStruct
235 236 237 238 239 240 241 242 |
# File 'lib/h8/context.rb', line 235 def self.delete_handler object, args name = args[0] if object.is_a?(OpenStruct) object.delete_field name else object.delete name end end |
.eval(script, file_name: nil, **kwargs) ⇒ Value
Execute script in a new context with optionally set vars. @see H8#set_all
153 154 155 |
# File 'lib/h8/context.rb', line 153 def self.eval script, file_name: nil, ** kwargs Context.new(** kwargs).eval script, file_name: file_name end |
.secure_call(instance, method, args = nil) ⇒ Object
Secure gate for JS to securely access ruby class properties (methods with no args) and methods. This class implements security policy. Overriding this method could breach security and provide full access to ruby object trees.
It has very complex logic so the security model update should be done somehow else.
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/h8/context.rb', line 163 def self.secure_call instance, method, args=nil # p [:sc, instance, method, args] if instance.is_a?(Array) method == 'select' and method = '__js_select' end immediate_call = if method[0] == '!' method = method[1..-1] true else false end method = method.to_sym begin m = instance.public_method(method) owner = m.owner if can_access?(owner) return m.call(*args) if method[0] == '[' || method[-1] == '=' || immediate_call if m.arity != 0 return ProcGate.new( -> (*args) { m.call *args } ) else return m.call end end rescue NameError # No exact method, calling []/[]= if any method, args = if method[-1] == '=' [:[]=, [method[0..-2].to_s, args[0]]] else [:[], [method.to_s]] end begin m = instance.public_method(method) if can_access?(owner) if method == :[] if instance.is_a?(Hash) return m.call(*args) || m.call(args[0].to_sym) else return m.call(*args) end else return m.call(*args) end end rescue NameError # It means there is no [] or []=, e.g. undefined rescue TypeError raise unless $!.to_s =~ /no implicit conversion of String into Integer/ # This also means that property is not found end end H8::Undefined end |
Instance Method Details
#[]=(name, value) ⇒ Object
Set variable/class for the context. It can set variable to hold:
* primitive type (like string, integer, nil, float and like) - by value!
* any other ruby object - by reference
* ruby Class - creating a javascript constructor function that creates ruby
class instance (any arguments) and gates it to use with js.
123 124 125 |
# File 'lib/h8/context.rb', line 123 def []= name, value set_all name.to_sym => value end |
#coffee(script, **kwargs) ⇒ Object
Compile and execute coffeescript, taking same arguments as #eval.
If you need to execute same script more than once consider first H8::Coffee.compile and cache compiled script.
146 147 148 |
# File 'lib/h8/context.rb', line 146 def coffee script, ** kwargs eval Coffee.compile script, ** kwargs end |
#eval(script, max_time: 0, timeout: 0, file_name: nil) {|_self| ... } ⇒ Value
Execute a given script on the current context with optionally limited execution time.
136 137 138 139 140 |
# File 'lib/h8/context.rb', line 136 def eval script, max_time: 0, timeout: 0, file_name: nil timeout = max_time * 1000 if max_time > 0 yield(self) if block_given? _eval script, timeout.to_i, file_name end |
#safe_proc_call(proc, args) ⇒ Object
This is workaround for buggy rb_proc_call which produces segfaults if proc is not exactly a proc, so we call it like this:
218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/h8/context.rb', line 218 def safe_proc_call proc, args if proc.respond_to?(:call) proc.call(*args) else if args.length == 0 proc # Popular bug: call no-arg method not like a property else raise NoMethodError, "Invalid callable" end end # proc.is_a?(Array) ? proc : proc.call(*args) end |
#set_all(**kwargs) ⇒ Object
set variables from keyword arguments to this context see #[]= for details
112 113 114 115 116 |
# File 'lib/h8/context.rb', line 112 def set_all **kwargs kwargs.each { |name, value| set_var(name.to_s, value) } end |