Permalink
Browse files

Major refactoring to support multiple apps

  • Loading branch information...
1 parent 6f1327e commit a571b28116fc9e59283741cc52dbda4eccface4e Evan Miller committed Jul 16, 2011
Showing with 1,426 additions and 3,417 deletions.
  1. +1 −0 Makefile
  2. +15 −2 README.md
  3. +1 −3 README_DATABASE
  4. +25 −2 README_UPGRADE
  5. +0 −1 doc-src/api-config.html
  6. +33 −29 doc-src/api-controller.html
  7. +20 −4 doc-src/api-mail-controller.html
  8. +1 −1 doc-src/api-mq.html
  9. +1 −1 doc-src/api-record.html
  10. +4 −4 doc-src/api-view.html
  11. +3 −3 rebar.config
  12. +15 −46 skel.template
  13. +3 −2 skel/Makefile
  14. +0 −345 skel/admin/admin_controller.erl
  15. +0 −59 skel/admin/admin_lib.erl
  16. +0 −18 skel/admin/static/jquery-1.6.1.min.js
  17. +0 −75 skel/admin/static/lang.js
  18. +0 −43 skel/admin/static/news.js
  19. +0 −326 skel/admin/static/stylesheets/application.css
  20. +0 −732 skel/admin/static/stylesheets/application_orig.css
  21. +0 −105 skel/admin/test/admin_test.erl
  22. +0 −9 skel/admin/view/access_denied.html
  23. +0 −59 skel/admin/view/big_red_button.html
  24. +0 −28 skel/admin/view/create.html
  25. +0 −65 skel/admin/view/create_lang.html
  26. +0 −11 skel/admin/view/delete.html
  27. +0 −11 skel/admin/view/delete_lang.html
  28. +0 −28 skel/admin/view/edit.html
  29. +0 −46 skel/admin/view/index.html
  30. +0 −171 skel/admin/view/lang.html
  31. +0 −58 skel/admin/view/layouts/admin.html
  32. +0 −8 skel/admin/view/layouts/shared/_alerts.html
  33. +0 −7 skel/admin/view/layouts/shared/_main_menu.html
  34. +0 −33 skel/admin/view/layouts/shared/_sidebar.html
  35. +0 −88 skel/admin/view/model.html
  36. +0 −68 skel/admin/view/record.html
  37. +0 −39 skel/admin/view/routes.html
  38. +0 −5 skel/admin/view/shared/_lang_left_submenu.html
  39. +0 −3 skel/admin/view/shared/_lang_right_submenu.html
  40. +0 −21 skel/admin/view/splash.html
  41. +0 −27 skel/admin/view/upgrade.html
  42. +0 −16 skel/admin/view/upload.html
  43. +6 −2 skel/boss.config
  44. +0 −21 skel/boss.routes
  45. +13 −0 skel/priv/boss.routes
  46. BIN skel/{ → priv}/static/chicago-boss.png
  47. BIN skel/{ → priv}/static/favicon.ico
  48. 0 skel/{ → src}/init/news.erl
  49. 0 skel/{ → src}/mail/incoming_mail_controller.erl
  50. 0 skel/{ → src}/mail/outgoing_mail_controller.erl
  51. 0 skel/{ → src}/mail/view/test_message.html
  52. 0 skel/{ → src}/mail/view/test_message.txt
  53. +4 −1 skel/start-dev.sh
  54. +0 −19 skel/test/mail_test.erl
  55. +4 −2 src/boss/boss_compiler.erl
  56. +62 −0 src/boss/boss_controller_compiler.erl
  57. +12 −3 src/boss/boss_env.erl
  58. +27 −0 src/boss/boss_erlydtl_tags.erl
  59. +49 −30 src/boss/boss_files.erl
  60. 0 skel/admin/view/error.html → src/boss/boss_html_error_template.dtl
  61. +41 −0 src/boss/boss_json.erl
  62. +44 −38 src/boss/boss_lang.erl
  63. +80 −59 src/boss/boss_load.erl
  64. +25 −23 src/boss/boss_mail.erl
  65. +1 −5 src/boss/boss_news.erl
  66. +106 −141 src/boss/boss_news_controller.erl
  67. +2 −2 src/boss/boss_record_compiler.erl
  68. +13 −3 src/boss/boss_record_lib.erl
  69. +20 −126 src/boss/boss_router.erl
  70. +203 −0 src/boss/boss_router_controller.erl
  71. +22 −0 src/boss/boss_router_sup.erl
  72. +42 −17 src/boss/boss_smtp_server.erl
  73. +1 −1 src/boss/boss_sup.erl
  74. +19 −20 src/boss/boss_translator.erl
  75. +0 −10 src/boss/boss_translator_app.erl
  76. +25 −11 src/boss/boss_translator_controller.erl
  77. +2 −2 src/boss/boss_translator_sup.erl
  78. +33 −0 src/boss/boss_web.erl
  79. +384 −222 src/boss/boss_web_controller.erl
  80. +1 −5 src/boss/db_adapters/boss_db_adapter_mongodb.erl
  81. +1 −2 src/boss/db_adapters/boss_db_adapter_mysql.erl
  82. +1 −2 src/boss/db_adapters/boss_db_adapter_pgsql.erl
  83. +1 −3 src/boss/db_adapters/boss_db_adapter_riak.erl
  84. +1 −2 src/boss/db_adapters/boss_db_adapter_tyrant.erl
  85. +33 −17 src/erlydtl/erlydtl_compiler.erl
  86. +11 −11 src/simple_bridge/misultin_bridge_modules/misultin_request_bridge.erl
  87. +15 −15 src/simple_bridge/mochiweb_bridge_modules/mochiweb_request_bridge.erl
View
@@ -8,6 +8,7 @@ SESSION_CONFIG_DIR=src/boss/session_adapters/test_config
all:
@$(REBAR) compile skip_deps=true
+ $(ERL) -pa ebin -eval 'erlydtl:compile("src/boss/boss_html_error_template.dtl", boss_html_error_template, [{out_dir, "ebin"}])' -noshell -s init stop
clean:
@$(REBAR) clean
View
@@ -39,6 +39,14 @@ Dependencies
2. make mochiweb with msys or cygwin
+Admin Interface
+---------------
+
+You probably want to install the CB admin interface. Download it from
+
+ <https://github.com/evanmiller/chicagoboss_admin
+
+
Upgrades
--------
@@ -130,12 +138,17 @@ for storing and retrieving session information.
*Routes*. By default, Chicago Boss uses the same routing conventions as Ruby on
Rails (`/controller/action/id`). You can customize the routes and provide
-a base URL in the routes.config file. Of course, most routing occurs with the
-pattern- matching controller logic, e.g.
+a base URL in the priv/application.routes file. Of course, most routing occurs
+with the pattern-matching controller logic, e.g.
posts('GET', ["category", Category]) ->
...
+You can then generate URLs to match controller patterns in your templates like
+so:
+
+ {% url action="posts" category="some category" %}
+
*Email*. Chicago Boss ships with a miniature MVC for sending multipart emails.
Emails can be templated with ErlyDTL, and it is easy to provide plain-text and
HTML versions of the same email. In testing environments, email can be sent
View
@@ -79,6 +79,4 @@ Riak is a NoSQL database developed by Basho Technologies.
4. Open up boss.config and set db_adapter to riak.
-The current implementation of the Riak adapter supports only simple CRUD
-operations: Type:new, BossRecord:save/0, boss_db:delete/1, boss_db:find/1.
-Queries with predicates have not been implemented yet as well as associations.
+For query operations to work, you need to have Riak Search installed.
View
@@ -1,5 +1,28 @@
-Upgrade: From 0.5.x
--------------------
+Upgrade: From 0.5 to 0.6
+------------------------
+
+Starting with Chicago Boss 0.6, projects follow an OTP layout, and the admin
+interface is a standalone OTP application.
+
+To upgrade, you will need to do the following in your project:
+
+ mkdir src
+ mkdir priv
+ mv controller/ init/ lib/ mail/ model/ test/ view/ src/
+ mv lang/ static/ priv/
+ PROJECT=my_application mv boss.routes priv/$PROJECT.routes
+
+In addition, you will need to prefix your controller modules with the application
+name. E.g.
+
+ mv blog_controller.erl my_application_blog_controller.erl
+
+This avoids module naming conflicts when multiple applications are installed on
+the same server (which was not possible prior to 0.6).
+
+
+Upgrade: From 0.5.x to 0.5.y
+----------------------------
Starting with Chicago Boss 0.5, the framework source is kept separate from your
project source. After downloading and building a new copy of CB, you need to update
View
@@ -2,7 +2,6 @@
{% block api_content %}
<p>All configuration takes place in <code>boss.config</code> in your project directory. Valid configuration options are:</p>
<ul>
- <li><code>admin_ip_blocks</code> - List of IP blocks that can access the admin interface. Defaults to <code>["127.0.0.1", "192.168.0.0/16", "10.0.0.0/16"]</code></li>
<li><code>assume_locale</code> - The presumed locale of translatable strings. Defaults to "en".</li>
<li><code>cache_adapter</code> - The cache adapter to use. Currently the only valid value is <code>memcached_bin</code>.</li>
<li><code>cache_servers</code> - A list of cache servers (<code>{Host, Port, PoolSize}</code>). Defaults to <code>[{"localhost", 11211, 1}]</code>.
@@ -8,8 +8,8 @@
&nbsp; <a href="#simplebridge">SimpleBridge request object</a></p>
<p>Chicago Boss associates each URL with a function of a controller.
The URL <nobr>/foo/bar</nobr> will call the function <code>foo_controller:bar</code>.
-Each controller module should go into your project's controller/ directory and the file name should end with "_controller.erl".
-Helper functions should go into your project's lib/ directory.
+Each controller module should go into your project's src/controller/ directory and the file name should end with "_controller.erl".
+Helper functions should go into your project's src/lib/ directory.
Controllers can take one parameter or two parameters: the <a href="#simplebridge">SimpleBridge request object</a>, and an optional session ID (if <a href="api-session.html">sessions</a> are enabled). Declare it like:</p>
<div class="code">
<span class="attr">-module</span>(my_controller, [Req]).
@@ -45,37 +45,35 @@
&nbsp;&nbsp;&nbsp;&nbsp;...<br />
</div>
+<p>These function clauses act as templates for constructing URLs in the view; for each CamelCase variable, simply use the lower-cased underscored equivalent as the parameter name. To continue the example above, you can construct URLs to match the above controllers with the following view tags:</p>
+
+<div class="code">
+ {{ '{% url action="view" %}' }}<br />
+ {{ '{% url action="view" id="1234" %}' }}<br />
+ {{ '{% url action="view" tag="funny" %}' }}<br />
+ {{ '{% url action="view" tag="funny" author="saint-paul" %}' }}<br />
+ {{ '{% url action="view" year="2009" month="08" %}' }}<br />
+</div>
+
+<p>Template variables can of course be used in place of string literals.</p>
+
<a name="routes"></a>
<h3>Routes</h3>
-<p>The routing system is simple, in the file boss.routes you have some special routes and free-hand named routes, the routes are loaded at startup in memory (ets table) and reloaded on /admin/upgrade.</p>
-
-<p>The file contains a list of erlang terms, one per line finished with a dot.</p>
+<p>Most routing takes place in the controller pattern-matching code. You can define additional routes in <code>priv/my_application.routes</code>. The file contains a list of erlang terms, one per line finished with a dot. Each term is a tuple with a URL or an HTTP status code as the first term, and a <code>{Controller, Action}</code> or <code>{Controller, Action, Parameters}</code> tuple as the second term.</p>
<p>A few examples:</p>
<div class="code">
- <span class="Comment">%% The {Controller, Action} pair to be used on the home page. Defaults to {"hello", "world"} </span><br />
- <span class="Comment">%% Format: {"Controller", "Action"}</span><br />
- {<span class="Constant">front_page</span>, {<span class="String"><span class="String">&quot;</span>hello<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>world<span class="String">&quot;</span></span>}}<br />.
- <span class="Comment">%%</span><br />
- <span class="Comment">%% The action to call if none is specified. Defaults to "index" </span><br />
- <span class="Comment">%% Format: "Action" </span><br />
-{<span class="Constant">default_action</span>, <span class="String"><span class="String">&quot;</span>index<span class="String">&quot;</span></span>}.
-<span class="Comment">%%</span><br />
-<span class="Comment">%% A proplist of default actions to call if none is specified, keyed by controller name</span> <br />
-<span class="Comment">%% Format: [ {"Controller1", "Action"}, {"Controller2", "Action"}]</span> <br />
-{<span class="Constant">default_actions</span>, [{<span class="String"><span class="String">&quot;</span>admin<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>model<span class="String">&quot;</span></span>}]}.<br />
-<span class="Comment">%%</span><br />
-<span class="Comment">%% Route for not-found actions, is called if not named route is found a the first url token</span>
-<span class="Comment">%% don't pertain to a controller</span> <br />
-<span class="Comment">%% Format: {"Controller", "Action", ["Param1", "ParamN"]}</span>
-{<span class="Constant">not_found_route</span>, {<span class="String"><span class="String">&quot;</span>hello<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>world<span class="String">&quot;</span></span>, []}}.<br />
-<span class="Comment">%%</span><br />
-<span class="Comment">%% Named routes, will take precedence over the previous</span> <br />
-<span class="Comment">%% Format: {"Full Url", {"Controller", "Action", ["Param1", "ParamN"]}}</span> <br />
-{<span class="String"><span class="String">&quot;</span>/login<span class="String">&quot;</span></span>, {<span class="String"><span class="String">&quot;</span>account<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>login<span class="String">&quot;</span></span>, []}}.<br />
-{<span class="String"><span class="String">&quot;</span>/signup<span class="String">&quot;</span></span>, {<span class="String"><span class="String">&quot;</span>accounts<span class="String">&quot;</span></span>, <span class="String"><span class="String">&quot;</span>signup<span class="String">&quot;</span></span>, []}}.<br />
+ {"/", {"hello", "world"{{ "}}" }}.<br />
+ {"/signup", {"account", "create"{{ "}}" }}.<br />
+ {404, {"main", "not_found"{{ "}}" }}.
+</div>
+
+<p>To define a default action for a controller, simply add a <code>default_action</code> attribute to the controller like so:</p>
+
+<div class="code">
+-default_action(index).
</div>
<a name="auth"></a>
@@ -126,11 +124,11 @@
<p><code>Variables</code> will be passed into the associated Django template, and <code>Headers</code> are HTTP headers you want to set (e.g., <code>Content-Type</code>).</p>
<div class="code">
- {redirect, Location<span class="typevar">::string()</span>}
+ {redirect, Location}
</div>
-<p>Perform a 302 HTTP redirect to <code>Location</code>.</p>
+<p>Perform a 302 HTTP redirect to <code>Location</code>, which may be a URL string or a proplist of parameters that will be converted to a URL using the routes system.</p>
<div class="code">
- {redirect, Location<span class="typevar">::string()</span>, Headers<span class="typevar">::proplist()</span>}
+ {redirect, Location, Headers<span class="typevar">::proplist()</span>}
</div>
<p>Perform a 302 HTTP redirect to <code>Location</code> and set additional HTTP <code>Headers</code>.</p>
@@ -181,6 +179,12 @@
<p>Return <code>Data</code> to the client as a JSON object while setting additional HTTP <code>Headers</code>.</p>
+<div class="code">
+ not_found
+</div>
+
+<p>Invoke the 404 File Not Found handler.</p>
+
<br />
<a name="filter"></a>
<h3>Post-processing</h3>
@@ -1,6 +1,6 @@
{% extends "api.html" %}
{% block api_content %}
-<p>Chicago Boss provides three API functions for sending emails. No SMTP configuration or server is necessary.</p>
+<p>Chicago Boss provides API functions for sending and receiving emails. No SMTP configuration or server is necessary.</p>
<h2>Sending a simple email</h2>
@@ -20,11 +20,11 @@
<h2>Sending email with templates</h2>
<p>If the simple method doesn't meet your needs, Chicago Boss provides a way to send email using controllers and views.</p>
-<p>Mail controller logic should go into <code>mail/outgoing_mail_controller.erl</code> in your project directory, and templates should go into <code>mail/view/</code>. To send an email from a web controller, call:</p>
+<p>Mail controller logic should go into <code>src/mail/my_application_outgoing_mail_controller.erl</code> in your project directory, and templates should go into <code>src/mail/view/</code>. To send an email from a web controller, call:</p>
<div class="code">
- boss_mail:send(foo_message, [Arg1, Arg2, ...])
+ boss_mail:send_template(my_application, foo_message, [Arg1, Arg2, ...])
</div>
-<p>That will invoke <code>outgoing_mail_controller:foo_message(Arg1, Arg2, ...)</code> and use the return value to populate the templates <code>mail/view/foo_message.txt</code> and <code>mail/view/foo_message.html</code>, then send the email in a background process. Your mail controller function should return:</div>
+<p>That will invoke <code>outgoing_mail_controller:foo_message(Arg1, Arg2, ...)</code> and use the return value to populate the templates <code>src/mail/view/foo_message.txt</code> and <code>src/mail/view/foo_message.html</code>, then send the email in a background process. Your mail controller function should return:</div>
<div class="code">
{ok, FromAddress, ToAddress, HeaderFields} | <br />
{ok, FromAddress, ToAddress, HeaderFields, Variables} | <br />
@@ -44,4 +44,20 @@
</ul>
<p><strong>Formatting.</strong> If templates ending only in ".txt" are present, the message will be sent in plain-text; if templates ending only in ".html" are present, the message will be sent as HTML; but if both ".txt" and ".html" templates are present, the message will be sent as a a MIME multi-part message with alternative plain-text and HTML representations.</p>
<p><strong>I18n.</strong> To use Chicago Boss's i18n machinery in emails, specify the desired language in the "Content-Language" header field returned from the mail controller.</p>
+
+<h2>Receiving email</h2>
+
+<p>To receive email, you must first enable the SMTP server in your configuration. You can then define endpoints in <code>src/mail/my_application_incoming_mail_controller.erl</code>. Each function should take two arguments:</p>
+<ul>
+ <li>The address that the email is from</li>
+ <li>A parsed version of the email</li>
+</ul>
+<p>The return value is ignored.</p>
+<p>To implement authentication for incoming email, simply define an <code>authorize_/3</code> function in your incoming mail controller. It should take three arguments:</p>
+<ul>
+ <li>The user name of the sender</li>
+ <li>The domain name of the sender</li>
+ <li>The IP address of the sender</li>
+</ul>
+<p>The <code>authorize_/3</code> function should return <code>true</code> if the user should be permitted to send email to the controller, or <code>false</code> otherwise.</p>
{% endblock %}
View
@@ -16,7 +16,7 @@
boss_mq:push("my-channel", &lt;&lt;"Secret Message"&gt;&gt;)
</div>
-<p>Currently, only an in-memory message queue is supported, so all messaging must occur on the same CB server. Additional adapters will be added in the future to support more complex installations.</p>
+<p>Currently, only an in-memory message queue is supported, so all messaging must occur in the same CB cluster. Additional adapters will be added in the future to support more complex installations.</p>
<h2>boss_mq API</h2>
View
@@ -6,7 +6,7 @@
&nbsp; <a href="#hooks">Save hooks</a>
</p>
<p>
-BossRecords are <em>specially compiled parameterized modules</em> that follow the "active record" pattern. BossRecords go into your project's model/ folder and will have functions generated for saving them into the database and for accessing related BossRecords. Important aspects of BossRecords:
+BossRecords are <em>specially compiled parameterized modules</em> that follow the "active record" pattern. BossRecords go into your project's src/model/ folder and will have functions generated for saving them into the database and for accessing related BossRecords. Important aspects of BossRecords:
</p>
<ul>
View
@@ -1,15 +1,15 @@
{% extends "api.html" %}
{% block api_content %}
-<p>Chicago Boss liked <a href="http://docs.djangoproject.com/en/dev/topics/templates/">Django's templating language</a> so much, he decided to steal it. Template files go in your project's view/ folder (in subdirectories that correspond to the controller name), and will have access to the variables you pass from your <a href="api-controller.html#nav">Controller</a>. The template file associated with the function <code>foo_controller:bar</code> will be view/foo/bar.html.</p>
+<p>Chicago Boss liked <a href="http://docs.djangoproject.com/en/dev/topics/templates/">Django's templating language</a> so much, he decided to steal it. Template files go in your project's src/view/ folder (in subdirectories that correspond to the controller name), and will have access to the variables you pass from your <a href="api-controller.html#nav">Controller</a>. The template file associated with the function <code>foo_controller:bar</code> will be src/view/foo/bar.html.</p>
-<p>Note: if you use the the <code>extends</code> tag, the file path should be relative to your project's view/ directory.</p>
+<p>Note: if you use the the <code>extends</code> tag, the file path should be relative to your project's src/view/ directory.</p>
<p>Chicago Boss has support for the most common features of the Django Template Language, so many existing Django templates should work out of the box. Your templates will be compiled down to Erlang BEAM code using <a href="http://code.google.com/p/erlydtl/">ErlyDTL</a> to give you the fastest Erlang templates this side of Stockholm.</p>
-<p>As of CB 0.5.0, 100% of Django filters are supported. The only unsupported tags are csrf_token, url, ifchanged, and regroup.</p>
+<p>As of CB 0.5.0, 100% of Django filters are supported. The only unsupported tags are csrf_token, ifchanged, and regroup.</p>
<h2>Custom tags</h2>
-<p>If you repeated logic or template snippets, you can put helper templates into your project's view/lib/ directory. The variables appearing in helper templates must be supplied by the caller. For example, if you have a file called "view/lib/my_custom_tag.html" which uses a variable called "foo", you can invoke it from other templates like:</p>
+<p>If you repeated logic or template snippets, you can put helper templates into your project's src/view/lib/ directory. The variables appearing in helper templates must be supplied by the caller. For example, if you have a file called "src/view/lib/my_custom_tag.html" which uses a variable called "foo", you can invoke it from other templates like:</p>
<div class="code">
{{ "{% " }}my_custom_tag foo="bar"{{ " %}" }}
</div>
View
@@ -1,6 +1,6 @@
{erl_opts, [debug_info]}.
{template_dir, "."}.
{deps, [
- {riakc, "1.1.*", {git, "git://github.com/basho/riak-erlang-client", {tag, "aa5c64a6a04192662d9c"}}},
- {riakpool, "0.1", {git, "git://github.com/dweldon/riakpool", {tag, "HEAD"}}}
- ]}.
+ {riakc, "1.1.*", {git, "git://github.com/basho/riak-erlang-client", {tag, "aa5c64a6a04192662d9c"}}},
+ {riakpool, "0.1", {git, "git://github.com/dweldon/riakpool", {tag, "HEAD"}}}
+ ]}.
Oops, something went wrong.

0 comments on commit a571b28

Please sign in to comment.