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