Skip to content

Commit

Permalink
[feature] opa-create: new tool to generate application skeletons
Browse files Browse the repository at this point in the history
  • Loading branch information
cedricss committed Jun 6, 2012
1 parent 8665198 commit 2b39809
Show file tree
Hide file tree
Showing 13 changed files with 341 additions and 4 deletions.
16 changes: 14 additions & 2 deletions Makefile
Expand Up @@ -63,7 +63,7 @@ opa-packages: $(MYOCAMLBUILD)
.PHONY: stdlib
stdlib: opa-packages

DISTRIB_TOOLS = opa-bin opa-plugin-builder-bin opa-plugin-browser-bin bslServerLib.ml opa-db-server opa-db-tool opa-cloud opatop opa-translate
DISTRIB_TOOLS = opa-bin opa-plugin-builder-bin opa-plugin-browser-bin bslServerLib.ml opa-db-server opa-db-tool opa-cloud opatop opa-translate opa-create

.PHONY: distrib
distrib: $(MYOCAMLBUILD)
Expand All @@ -74,6 +74,18 @@ distrib: $(MYOCAMLBUILD)
manpages: $(MYOCAMLBUILD)
$(MAKE) -C manpages OCAMLBUILD="$(OCAMLBUILD)" BLDDIR=../$(BUILD_DIR)

.PHONY: opa-create
opa-create: $(MYOCAMLBUILD)
$(OCAMLBUILD) tools/opa-create/src/opa-create.exe
@$(copy-tool-opa-create)
.PHONY: install-opa-create
install-opa-create:
@$(install-opa-create)
target-tool-opa-create = tools/opa-create/src/opa-create.exe
copy-tool-opa-create = mkdir -p $(BUILD_DIR)/bin && $(INSTALL) $(BUILD_DIR)/tools/opa-create/src/opa-create.exe $(BUILD_DIR)/bin/opa-create
install-opa-create = mkdir -p $(PREFIX)/bin && $(INSTALL) $(BUILD_DIR)/bin/opa-create $(INSTALL_DIR)/bin/opa-create


##
## INSTALLATION
##
Expand Down Expand Up @@ -140,7 +152,7 @@ install-all-plugins: $(addprefix install-plugin-,$(OPA_PLUGINS))



install-bin:
install-bin: opa-create
@printf "Installing into $(INSTALL_DIR)/bin\r"
@mkdir -p $(INSTALL_DIR)/bin
@$(if $(wildcard $(BUILD_DIR)/bin/*),$(INSTALL) -r $(BUILD_DIR)/bin/* $(INSTALL_DIR)/bin)
Expand Down
22 changes: 22 additions & 0 deletions build_rules.ml
Expand Up @@ -772,6 +772,7 @@ rule "opa bash_completion: opa-bin -> bash_completion"
(fun env build ->
Seq[Cmd(S[get_tool "opa-bin"; A"--bash-completion"])]);


(* -- OPA packages -- *)

let package_to_dir s0 =
Expand Down Expand Up @@ -848,7 +849,28 @@ rule "all.plugins"
])
);

let opa_create_prefix = "tools/opa-create/src/opa-create" in
let opa_create_src = opa_create_prefix ^ ".opa" in
let opa_create_dst = opa_create_prefix ^ ".exe" in

let dir_all_files dir =
List.filter (fun p -> (not (Pathname.is_directory p))) (dirlist dir)
in

let dir_rec_all_files dir =
let dirs = rec_subdirs [ dir ] in
List.fold_right (fun dir acc -> dir_all_files dir @ acc) dirs []
in

rule "opa application creator"
~deps:((dir_rec_all_files "tools/opa-create"))
~prods: [opa_create_dst]
(fun env build ->
Cmd(S[
Sh("MLSTATELIBS=\""^ opa_prefix ^"\"");
get_tool "opa-bin";
A"-o"; P opa_create_dst; P opa_create_src
]));

let package_building ~name ~stamp ~stdlib_only ~rebuild =
rule name
Expand Down
1 change: 0 additions & 1 deletion build_tools
Expand Up @@ -94,4 +94,3 @@ internal opa-cloud dissemination/spawn.sh
internal gen_opa_manpage opa/gen_opa_manpage.native
internal gen_opatop_manpage opatop/gen_opatop_manpage.native
internal genman.native manpages/genman.native

1 change: 1 addition & 0 deletions tools/_tags
Expand Up @@ -16,3 +16,4 @@
###
# jschecker
<jschecker>: traverse
<opa-create/**>: traverse
71 changes: 71 additions & 0 deletions tools/opa-create/src/opa-create.opa
@@ -0,0 +1,71 @@
/*
Copyright © 2011 MLstate
This file is part of OPA.
OPA is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License, version 3, as published by
the Free Software Foundation.
OPA is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
more details.
You should have received a copy of the GNU Affero General Public License
along with OPA. If not, see <http://www.gnu.org/licenses/>.
*/

/**
* opa-create tool
*
* @category tools
* @author Cedric Soulas
* @destination PUBLIC
* @stability experimental
*/

import stdlib.{system, io.file}

SRC_DIR = "tools/opa-create/template/mvc"
resources = @static_content_directory("tools/opa-create/template/mvc")

function CommandLine_ident(names,description,param_doc)(up) {
{CommandLine.default_parser with
~names, ~description, ~param_doc,
function on_param(o) { parser { case int=Rule.ident : {no_params:up(int,o)} } }
}
}

list(CommandLine.parser({string name})) cmdline_parsers = [
CommandLine_ident(["--name", "-n"],
"Application name, without spaces, \"/\" or any other special characters",
"app_name (no special characters)")({ function(name, r){ r with ~name}}
)
]

options =
cmdline = {
init : { name : "wiki" },
parsers : cmdline_parsers,
anonymous : [],
title : "Opa Application Generator"
}
CommandLine.filter(cmdline)

function write(file, content) {
%%BslFile.of_string%%("./{file}", content)
}

function iter(file, f_content) {
n = String.length(SRC_DIR)
file = "{options.name}{String.sub(n, String.length(file) - n, file)}"
if (File.exists(file)) { warning("File {file} already exists. \nPlease delete it and try again."); System.exit(1) }
jlog("Generating {file}")
content = String.replace("application_name", options.name, f_content())
write(file, content)
}

StringMap.iter(iter, resources)

jlog("\nNow you can type:\n$ cd {options.name}\n$ make run")
26 changes: 26 additions & 0 deletions tools/opa-create/template/mvc/Makefile
@@ -0,0 +1,26 @@
########################################
# USER VARIABLES

EXE = application_name.exe
PACKNAME =
SRC =
PCKDIR = ./plugins/
PCK =
PLUGIN =
PLUGINDIR =
OTHER_DEPENDS = resources/*
CONF_FILE = configuration

#Compiler variables
OPACOMPILER ?= opa
FLAG = --opx-dir _build --import-package stdlib.database.mongo
PORT = 8080

RUN_OPT = --db-local db/

default: exe

run: exe
./$(EXE) $(RUN_OPT) || true

include Makefile.common
69 changes: 69 additions & 0 deletions tools/opa-create/template/mvc/Makefile.common
@@ -0,0 +1,69 @@
########################################
# MAKEFILE VARIABLES
OPACOMPILER ?= opa
OPA = $(OPACOMPILER) $(FLAG) $(OPAOPT)
PWD ?= $(shell pwd)
BUILDDIR ?= $(PWD)/_build
export BUILDDIR
OPAGENAPI ?= $(OPACOMPILER) --api-only
DEPENDS = $(SRC) $(PCK:%=$(BUILDDIR)/%) $(PLUGIN:%=$(BUILDDIR)/%)

ifdef CONF_FILE
CONFIG = --conf $(CONF_FILE) --conf-opa-files
else
CONFIG =
endif

_ = $(shell mkdir -p $(BUILDDIR))

########################################
# MAIN RULE
exe : $(EXE)
pack : $(PACKNAME)
doc : $(PACKDOC) doc.sub

########################################
# MAIN PACKAGE BUILDING
$(PACKNAME) : $(BUILDDIR)/$(PACKNAME)

$(BUILDDIR)/$(PACKNAME) : $(DEPENDS)
@echo "### Building package $(PACKNAME)"
mkdir -p $(BUILDDIR)
$(OPA) --autocompile $(SRC) $(PLUGIN:%=$(BUILDDIR)/%) --build-dir $(BUILDDIR) -I $(BUILDDIR) $(OPAOPT)
@rm -rf $(BUILDDIR)/$(PACKNAME)
@mv $(PACKNAME) $(BUILDDIR)/

########################################
# SUBS PACKAGE/PLUGIN BUILDING
$(BUILDDIR)/%.opx :
make $(@:$(BUILDDIR)/%.opx=-C $(PCKDIR)/%) pack

$(BUILDDIR)/%.opp :
make $(@:$(BUILDDIR)/%.opp=-C $(PLUGINDIR)/%)

########################################
# EXECUTABLE BUILDING
$(EXE) : pack $(SRC) $(MAINSRC) $(DEPENDS) $(OTHER_DEPENDS)
@echo "### Building executable $(EXE) "
mkdir -p $(BUILDDIR)
$(OPA) $(COMPILOPT) $(MAINSRC) $(CONFIG) $(PLUGIN:%=$(BUILDDIR)/%) -o $@ --build-dir $(BUILDDIR)/$(EXE)

$(EXE:%.exe=%.run) : $(EXE)
./$(EXE) -p $(PORT)

########################################
# Used by recursives makefile
pack.depends :
@echo $(PCK) $(PLUGIN)

########################################
# CLEANING
clean ::
@$(PCK:%.opx=make -C $(PCKDIR)/% clean &&) $(PLUGIN:%.opp=make -C $(PLUGINDIR)/% clean &&) echo "### Cleaning $(BUILDDIR)";
@rm -rf $(BUILDDIR)/* _tracks/*;
@if [ -n "$(EXE)" ]; then rm -rf $(EXE); fi
@if [ -n "$(PACKDOC)" ]; then rm -rf $(PACKDOC); fi

deep-clean :: clean
@rm -rf opa-debug

11 changes: 11 additions & 0 deletions tools/opa-create/template/mvc/configuration
@@ -0,0 +1,11 @@
application_name.controller:
import application_name.view
src/controller.opa

application_name.view:
import application_name.model
import stdlib.themes.bootstrap
src/view.opa

application_name.model:
src/model.opa
7 changes: 7 additions & 0 deletions tools/opa-create/template/mvc/resources/css/style.css
@@ -0,0 +1,7 @@
#main {
margin-top: 50px;
}

textarea {
width: 100%;
}
20 changes: 20 additions & 0 deletions tools/opa-create/template/mvc/src/controller.opa
@@ -0,0 +1,20 @@
resources = @static_resource_directory("resources")

custom = {
parser {
case r={Server.resource_map(resources)} : r
case "/" : Resource.default_redirection_page("/index.html")
case "/statistics" : Resource.page("Display", View.statistics())
case p=(.*) : path = Text.to_string(p); Resource.page(path, View.page(path))
}
}

Server.start(Server.http, [
{ register :
[ { doctype : { html5 } },
{ js : [ ] },
{ css : [ "/resources/css/style.css"] }
]
},
{ ~custom }
])
27 changes: 27 additions & 0 deletions tools/opa-create/template/mvc/src/model.opa
@@ -0,0 +1,27 @@
type page = {
string path,
string content,
int counter
}

database application_name {
page /page[{path}]
}

module Model {

function get_content(path) {
/application_name/page[{~path}]/counter++;
/application_name/page[{~path}]/content
}

function set_content(path, content) {
/application_name/page[{~path}]/content <- content
}

function statistics() {
DbSet.iterator(/application_name/page)
}

}

55 changes: 55 additions & 0 deletions tools/opa-create/template/mvc/src/view.opa
@@ -0,0 +1,55 @@
module View {

function template(content) {
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="./index.html">application_name</a>
<div class="nav-collapse collapse">
<ul class="nav">
<li><a href="/statistics">Statistics</a></li>
</ul>
</div>
</div>
</div>
</div>
<div id=#main class="container-fluid">
<div class="row-fluid">
{content}
</div>
<hr>
<footer>
<p>application_name</p>
</footer>
</div>

}

function statistics() {
tbody = Iter.fold(
function(page, acc) { <>{acc}<tr><td>{page.path}</td><td>{page.counter}</td></tr></> },
Model.statistics(), <></>
)
content =
<h3>Page statistics</h3>
<table class="table table-bordered">
<thead><tr><td>Path</td><td>Counter</td></tr></thead>
<tbody>{tbody}</tbody>
</table>
template(content)
}

function page(path) {
content =
<div class="hero-unit">
<h3>{path}</h3>
<textarea id=#content rows="15">{ Model.get_content(path) }</textarea>
<button class="btn" onclick={ function(_) Model.set_content(path, Dom.get_value(#content)) }>
Save
</button>
</div>;
template(content)
}

}

0 comments on commit 2b39809

Please sign in to comment.