lib/flok/user_compiler.rb in flok-0.0.40 vs lib/flok/user_compiler.rb in flok-0.0.41
- old
+ new
@@ -1,7 +1,9 @@
#Compile a controller ruby file into a javascript string
+require 'active_support'
+require 'active_support/core_ext/numeric'
require 'erb'
module Flok
module UserCompiler
#Compile a ruby file containing flok controller definitions (from the user)
def self.compile rb_src
@@ -58,40 +60,12 @@
#Event handler inside an action
class UserCompilerOn
attr_accessor :controller_name, :action_name, :name
end
- class UserCompilerAction
- attr_accessor :controller, :name, :on_entry_src, :ons
-
- def initialize controller, name, ctx, &block
- @controller = controller
- @name = name
- @ctx = ctx
- @ons = [] #Event handlers
-
- self.instance_eval(&block)
- end
-
- def on_entry js_src
- #returns a string
- @on_entry_src = macro(js_src)
- end
-
- def on name, js_src
- @ons << {:name => name, :src => macro(js_src)}
- end
-
- def macro js_src
- lines = js_src.split("\n").map do |line|
-
- end
-
- return lines.join("\n")
- end
-
- def macro text
+ module UserCompilerMacro
+ def _macro text
out = StringIO.new
text.split("\n").each do |l|
#EMBED(vc_name, spot_name, context) macro
if l =~ /Embed/
@@ -182,11 +156,12 @@
#Switch the actions, reset embeds, and call on_entry
res = %{
var old_action = __info__.action;
__info__.action = "#{action_name}";
- //Remove all views
+ //Remove all views, we don't have to recurse because removal of a view
+ //is supposed to remove *all* view controllers of that tree as well.
var embeds = __info__.embeds;
for (var i = 0; i < __info__.embeds.length; ++i) {
for (var j = 0; j < __info__.embeds[i].length; ++j) {
//Free +1 because that will be the 'main' view
main_q.push([1, "if_free_view", embeds[i][j]+1]);
@@ -249,25 +224,37 @@
lvar = le[0].strip #Probably var x =
exp = le[1].strip
#For CopyPage(original_page), page_var is original_page
#This only supports variable names at this time
- exp.match /\((.*)\);?/
+ exp.match /\((.*?),(.*?)\);?/
+ exp.match /\((.*)\)/ if $1 == nil
+
#Get the id value the user wants, but we have to be careful
#because if nothing is passed, then we need to set it to null
- id_var = $1.strip
- if id_var == ""
- id_var = "null"
- end
+ type_var = $1
+ id_var = $2
+ type_var = type_var.gsub(/"/, "").strip
+ id_var = (id_var || "null").strip
+
+ raise "NewPage was not given a type" if type_var == ""
+ raise "NewPage type is not valid #{type_var.inspect}" unless ["array", "hash"].include? type_var
+
+ type_var_to_entries = {
+ "array" => "[]",
+ "hash" => "{}",
+ }
+
out << %{
#{lvar} {
_head: null,
_next: null,
- entries: [],
+ entries: #{type_var_to_entries[type_var]},
_id: #{id_var},
+ _type: "#{type_var}",
}
}
elsif l =~ /CopyPage/
le = (l.split /CopyPage/)
lvar = le[0].strip #Probably var x =
@@ -277,22 +264,33 @@
#This only supports variable names at this time
exp.match /\((.*)\);?/
page_var = $1
out << %{
+
var __page__ = {
_head: #{page_var}._head,
_next: #{page_var}._next,
_id: #{page_var}._id,
- entries: [],
+ _type: #{page_var}._type,
}
//This is a shallow clone, but we own this array
//When a mutable entry needs to be created, an entry will be cloned
//and swappend out
- for (var i = 0; i < #{page_var}.entries.length; ++i) {
- __page__.entries.push(#{page_var}.entries[i]);
+ if (#{page_var}._type === "array") {
+ __page__.entries = [];
+ for (var i = 0; i < #{page_var}.entries.length; ++i) {
+ __page__.entries.push(#{page_var}.entries[i]);
+ }
+ } else if (#{page_var}._type === "hash") {
+ __page__.entries = {};
+ var keys = Object.keys(#{page_var}.entries);
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ __page__.entries[key] = #{page_var}.entries[key];
+ }
}
#{lvar} __page__;
}
elsif l =~ /EntryDel/
@@ -305,11 +303,15 @@
exp.match /\((.*?),(.*)\);?/
page_var = $1
index_var = $2
out << %{
- #{page_var}.entries.splice(#{index_var}, 1);
+ if (#{page_var}._type === "array") {
+ #{page_var}.entries.splice(#{index_var}, 1);
+ } else if (#{page_var}._type === "hash") {
+ delete #{page_var}.entries[#{index_var}];
+ }
}
elsif l =~ /EntryInsert/
le = (l.split /EntryInsert/)
lvar = le[0].strip #Probably var x =
@@ -320,14 +322,25 @@
exp.match /\((.*?),(.*),(.*)\);?/
page_var = $1
index_var = $2
entry_var = $3
+ page_var.strip!
+ index_var.strip!
+ entry_var.strip!
+
out << %{
- #{entry_var}._id = gen_id();
- #{entry_var}._sig = gen_id();
- #{page_var}.entries.splice(#{index_var}, 0, #{entry_var});
+
+ if (#{page_var}._type === "array") {
+ #{entry_var}._id = gen_id();
+ #{entry_var}._sig = gen_id();
+ #{page_var}.entries.splice(#{index_var}, 0, #{entry_var});
+ } else if (#{page_var}._type === "hash") {
+ #{entry_var}._sig = gen_id();
+ #{page_var}.entries[#{index_var}] = #{entry_var};
+ }
+
}
elsif l =~ /SetPageNext/
le = (l.split /SetPageNext/)
lvar = le[0].strip #Probably var x =
@@ -367,22 +380,79 @@
#This only supports variable names at this time
exp.match /\((.*?),(.*)\);?/
page_var = $1
index_var = $2
+
out << %{
- //Duplicate entry
- #{page_var}.entries.splice(#{index_var}, 1, JSON.parse(JSON.stringify(#{page_var}.entries[#{index_var}])));
+ if (#{page_var}._type === "array") {
+ //Duplicate entry
+ #{page_var}.entries.splice(#{index_var}, 1, JSON.parse(JSON.stringify(#{page_var}.entries[#{index_var}])));
+
+ //Here's our new entry
+ var ne = #{page_var}.entries[#{index_var}];
+ ne._sig = gen_id();
+
+ #{lvar} #{page_var}.entries[#{index_var}];
+ } else if (#{page_var}._type === "hash") {
+ //Duplicate entry
+ #{page_var}.entries[#{index_var}] = JSON.parse(JSON.stringify(#{page_var}.entries[#{index_var}]));
+
+ //Here's our new entry
+ var ne = #{page_var}.entries[#{index_var}];
+ ne._sig = gen_id();
+
+ #{lvar} #{page_var}.entries[#{index_var}];
+
+ }
}
else
out.puts l
end
end
return out.string
end
+ end
+
+ class UserCompilerAction
+ attr_accessor :controller, :name, :ons, :every_handlers
+ include UserCompilerMacro
+
+ def initialize controller, name, ctx, &block
+ @controller = controller
+ @name = name
+ @ctx = ctx
+ @_on_entry_src = ""
+ @ons = [] #Event handlers
+ @every_handlers = []
+
+ self.instance_eval(&block)
+ end
+
+ def on_entry js_src
+ #returns a string
+ @_on_entry_src = _macro(js_src)
+ end
+
+ def on_entry_src
+ return @_on_entry_src
+ end
+
+ def on name, js_src
+ @ons << {:name => name, :src => _macro(js_src)}
+ end
+
+ def every seconds, str
+ @every_handlers << {
+ :name => "#{seconds}_sec_#{SecureRandom.hex[0..6]}",
+ :ticks => seconds*4,
+ :src => _macro(str)
+ }
+ end
+
#You can def things in controller and use them as macros inside actions
#But these defs. live in the UserCompilerController instance and we need
#to delegate these calls to the controller that are not available in the action
def method_missing method, *args, &block
if macro = @controller.macros[method]
@@ -393,11 +463,13 @@
end
end
end
class UserCompilerController
- attr_accessor :name, :spots, :macros, :_services
+ include UserCompilerMacro
+
+ attr_accessor :name, :spots, :macros, :_services, :_on_entry
def initialize name, ctx, &block
@name = name
@ctx = ctx
@spots = ['main']
@macros = {}
@@ -407,9 +479,13 @@
end
#Create an action macro
def macro name, &block
@macros[name] = block
+ end
+
+ def on_entry str
+ @_on_entry = _macro(str)
end
#Names of spots
def spots *spots
@spots += spots