Class: H8::Context

Inherits:
Object show all
Defined in:
ext/h8/main.cpp,
lib/h8/context.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(noglobals: false, **kwargs) ⇒ Context

Create new context optionally providing variables hash



77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/h8/context.rb', line 77

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

Returns:

  • (Boolean)


222
223
224
225
# File 'lib/h8/context.rb', line 222

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



213
214
215
216
217
218
219
220
# File 'lib/h8/context.rb', line 213

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

Returns:

  • (Value)

    wrapped object returned by the script



134
135
136
# File 'lib/h8/context.rb', line 134

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.



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
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
# File 'lib/h8/context.rb', line 144

def self.secure_call instance, method, args=nil
  # p [:sc, instance, method, args]
  if instance.is_a?(Array)
    method == 'select' and method = '_select_js'
  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
    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.


104
105
106
# File 'lib/h8/context.rb', line 104

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.



127
128
129
# File 'lib/h8/context.rb', line 127

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.

Parameters:

  • timeout (Int) (defaults to: 0)

    if is not 0 then maximum execution time in millis (therubyracer compatibility)

  • max_time (Float) (defaults to: 0)

    if is not 0 then maximum execution time in seconds. Has precedence over timeout

Yields:

  • (_self)

Yield Parameters:

  • _self (H8::Context)

    the object that the method was called on

Returns:

  • (Value)

    wrapped object returned by the script

Raises:



117
118
119
120
121
# File 'lib/h8/context.rb', line 117

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:



196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/h8/context.rb', line 196

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



93
94
95
96
97
# File 'lib/h8/context.rb', line 93

def set_all **kwargs
  kwargs.each { |name, value|
    set_var(name.to_s, value)
  }
end