Class: H8::Context

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

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

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

Create new context optionally providing variables hash



37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/h8/context.rb', line 37

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)


176
177
178
179
# File 'lib/h8/context.rb', line 176

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



167
168
169
170
171
172
173
174
# File 'lib/h8/context.rb', line 167

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



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

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.



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/h8/context.rb', line 104

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
  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] == '='
      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.


64
65
66
# File 'lib/h8/context.rb', line 64

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.



87
88
89
# File 'lib/h8/context.rb', line 87

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:



77
78
79
80
81
# File 'lib/h8/context.rb', line 77

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:



150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/h8/context.rb', line 150

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



53
54
55
56
57
# File 'lib/h8/context.rb', line 53

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