├── LICENSE.txt
├── Makefile
├── README.textile
├── deps
├── erlydtl
│ ├── README
│ ├── ebin
│ │ └── .gitignore
│ ├── priv
│ │ └── custom_tags
│ │ │ └── flashvideo
│ └── src
│ │ ├── Makefile
│ │ ├── erlydtl.erl
│ │ ├── erlydtl_compiler.erl
│ │ ├── erlydtl_dateformat.erl
│ │ ├── erlydtl_deps.erl
│ │ ├── erlydtl_filters.erl
│ │ ├── erlydtl_parser.erl
│ │ ├── erlydtl_parser.yrl
│ │ ├── erlydtl_runtime.erl
│ │ └── erlydtl_scanner.erl
└── mochiweb
│ ├── LICENSE
│ ├── README
│ ├── ebin
│ └── .gitignore
│ └── src
│ ├── Makefile
│ ├── mochifmt.erl
│ ├── mochifmt_records.erl
│ ├── mochifmt_std.erl
│ ├── mochihex.erl
│ ├── mochijson.erl
│ ├── mochijson2.erl
│ ├── mochinum.erl
│ ├── mochiweb.app
│ ├── mochiweb.erl
│ ├── mochiweb_app.erl
│ ├── mochiweb_charref.erl
│ ├── mochiweb_cookies.erl
│ ├── mochiweb_echo.erl
│ ├── mochiweb_headers.erl
│ ├── mochiweb_html.erl
│ ├── mochiweb_http.erl
│ ├── mochiweb_multipart.erl
│ ├── mochiweb_request.erl
│ ├── mochiweb_response.erl
│ ├── mochiweb_skel.erl
│ ├── mochiweb_socket_server.erl
│ ├── mochiweb_sup.erl
│ ├── mochiweb_util.erl
│ └── reloader.erl
├── doc
├── beepbeep.html
├── beepbeep_args.html
├── beepbeep_router.html
├── edoc-info
├── erlang.png
├── index.html
├── modules-frame.html
├── overview-summary.html
├── overview.edoc
├── packages-frame.html
└── stylesheet.css
├── ebin
└── .gitignore
├── example
└── blog
│ ├── Makefile
│ ├── ebin
│ └── .gitignore
│ ├── src
│ ├── Makefile
│ ├── blog_database.erl
│ ├── blog_example.app
│ ├── blog_example.erl
│ ├── blog_example_app.erl
│ ├── blog_example_deps.erl
│ ├── blog_example_sup.erl
│ ├── blog_example_web.erl
│ ├── home_controller.erl
│ └── login_controller.erl
│ ├── start-server.sh
│ ├── support
│ └── include.mk
│ ├── views
│ ├── base.html
│ ├── flash.html
│ ├── home
│ │ ├── index.html
│ │ └── new.html
│ └── login
│ │ └── new.html
│ └── www
│ └── stylesheets
│ └── style.css
├── include
└── beepbeep.hrl
├── priv
└── skel
│ ├── Makefile
│ ├── deps
│ └── .gitignore
│ ├── ebin
│ └── .gitignore
│ ├── src
│ ├── Makefile
│ ├── home_controller.erl
│ ├── skel.app
│ ├── skel.erl
│ ├── skel_app.erl
│ ├── skel_deps.erl
│ ├── skel_sup.erl
│ └── skel_web.erl
│ ├── start-server.sh
│ ├── support
│ └── include.mk
│ ├── views
│ ├── base.html
│ ├── flash.html
│ └── home
│ │ ├── index.html
│ │ └── show.html
│ └── www
│ └── stylesheets
│ └── style.css
├── script
└── new_beep.erl
├── src
├── Makefile
├── beepbeep.erl
├── beepbeep_args.erl
├── beepbeep_router.erl
├── beepbeep_session_server.erl
├── beepbeep_skel.erl
└── mochiweb_env.erl
└── support
└── include.mk
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2009 Dave Bryson (miceda.org)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | all:
2 | (cd src;$(MAKE))
3 | (cd deps/erlydtl/src;$(MAKE))
4 | (cd deps/mochiweb/src;$(MAKE))
5 | (cd example/blog;$(MAKE))
6 |
7 | docs:
8 | erl -pa `pwd`/ebin \
9 | -noshell \
10 | -run edoc_run application "'BeepBeep'" '"."' '[no_packages]'
11 |
12 | clean:
13 | (cd src;$(MAKE) clean)
14 | (cd deps/erlydtl/src;$(MAKE) clean)
15 | (cd deps/mochiweb/src;$(MAKE) clean)
16 | (cd example/blog;$(MAKE) clean)
17 |
--------------------------------------------------------------------------------
/README.textile:
--------------------------------------------------------------------------------
1 | h2. BeepBeep a simple web application for Erlang
2 |
3 | BeepBeep is a simple Web Application framework for Erlang inspired by Rails and Merb. It follows the principle of convention over configuration - meaning if you follow the code structure layout and a few rules when building your app, it'll require no extra work on you behalf to map Url requests to your Controllers and Views.
4 |
5 | BeepBeep is built on "MochiWeb":http://code.google.com/p/mochiweb/ and "ErlyDTL":http://code.google.com/p/erlydtl, providing a super fast web server and the abiity to define your templates with the Django template language.
6 |
7 |
8 | It looks like one of the many Google Groups I created for the project is finally available:
9 | "Google Group":http://groups.google.com/group/beepbeep-web-framework
10 |
11 | h3. Features
12 |
13 | * A script to generate a new web application (based on mochiweb's approach)
14 | * Session Server to store your application state
15 | * Before filter on your controllers for things like authentication
16 | * Django templates for the view
17 |
18 | h3. Getting Started
19 |
20 | # download the code
21 | # CD into the beepbeep directory
22 | # run make
23 | # generate a new web application by running ./script/new_beep.erl YouAppName "DestinationDirectory
24 |
25 | This will create a web app with everything you need. It includes a Sample controller (main_controller.erl).
26 |
27 | To run the sample:
28 |
29 | # cd into the new application's directory you created above
30 | # Run make to compile the new project
31 | # Start the server: ./start-server.sh
32 | # Open a browser and visit "http://localhost:8000
33 |
34 | h3. How it works:
35 |
36 | You write a controller with a set of methods that look like this:
37 |
38 |
39 | handle_request(Action,Params)
40 |
41 |
42 |
43 | Where Action is a string that will match to the request in the Url.
44 |
45 | And Params is an Array of optional parameters that will be passed to variables in your controller.
46 |
47 | BeepBeep will automatically map Url requests to controller and functions (or actions). For example a request to "/hello/show" would map to the "hello_controller" and invoke the "handle_request("show",[])" function.
48 |
49 | Here's an example:
50 |
51 |
74 |
75 | From "handle_request" we return a tuple that tells the framework what view to use. Views are located in the views directory. In our example we'll use the view located in the subdirectory "hello" and the file "show.html"
76 |
77 | Here's an example of the "show.html" template:
78 |
79 |
80 |
Hello from {{ name }}
81 |
82 |
83 | Which will result in:
84 |
85 |
Hello from BeepBeep
86 |
87 |
88 | The "name" key set in the controller is passed to the template and expanded using the Django format via erlyDTL.
89 |
90 | This approach provides a clean separation of the erlang logic in the controller and the html code in the template.
91 |
92 | You can also implement the before_filter to check requests before the matching "handle_request" is called. Filters that pass should simply return the atom ok, otherwise they should return one of the request responses such as {render...} or {redirect..."
93 |
94 | For more information, see the documention and example blog app included with the source code. And there's a small tutorial on the "Wiki":http://wiki.github.com/davebryson/beepbeep
95 |
--------------------------------------------------------------------------------
/deps/erlydtl/README:
--------------------------------------------------------------------------------
1 | ErlyDTL
2 | =======
3 |
4 | ErlyDTL implements most but not all of the Django Template Language.
5 |
6 | Project homepage: http://code.google.com/p/erlydtl/
7 |
8 |
9 | Compilation
10 | -----------
11 |
12 | To compile ErlyDTL, type "make" in this directory.
13 |
14 |
15 | Template compilation
16 | --------------------
17 |
18 | Four ways:
19 |
20 | erlydtl:compile("/path/to/template.dtl", my_module_name)
21 |
22 | erlydtl:compile("/path/to/template.dtl", my_module_name, Options)
23 |
24 | erlydtl:compile(<<"{{ foo }}">>, my_module_name)
25 |
26 | erlydtl:compile(<<"{{ foo }}">>, my_module_name, Options)
27 |
28 | Options is a proplist possibly containing:
29 |
30 | doc_root - Included template paths will be relative to this directory;
31 | defaults to the compiled template's directory.
32 |
33 | custom_tags_dir - Directory of DTL files (no extension) includable as tags.
34 | E.g. if $custom_tags_dir/foo contains "{{ bar }}", then
35 | "{{ foo bar=100 }}" will evaluate to "100". Get it?
36 |
37 | vars - Variables (and their values) to evaluate at compile-time rather than
38 | render-time.
39 |
40 | reader - {module, function} tuple that takes a path to a template and returns
41 | a binary with the file contents. Defaults to {file, read_file}. Useful
42 | for reading templates from a network resource.
43 |
44 | compiler_options - Proplist passed directly to compiler:forms/2
45 |
46 | force_recompile - Recompile the module even if the source's checksum has not
47 | changed. Useful for debugging.
48 |
49 |
50 | Usage (of a compiled template)
51 | ------------------------------
52 |
53 | my_compiled_template:render(Variables) -> {ok, IOList} | {error, Err}
54 |
55 | Variables is a proplist, dict, gb_tree, or a parameterized module
56 | (whose method names correspond to variable names). The variable
57 | values can be atoms, strings, binaries, or (nested) variables.
58 |
59 | IOList is the rendered template.
60 |
61 | my_compiled_template:source() -> {FileName, CheckSum}
62 |
63 | Name and checksum of the original template file.
64 |
65 | my_compiled_template:dependencies() -> [{FileName, CheckSum}]
66 |
67 | List of names/checksums of templates included by the original template
68 | file. Useful for frameworks that recompile a template only when the
69 | template's dependencies change.
70 |
71 |
72 | Tests
73 | -----
74 |
75 | From a Unix shell, run:
76 |
77 | make test
78 |
79 | Note that the tests will create some output in examples/rendered_output.
80 |
--------------------------------------------------------------------------------
/deps/erlydtl/ebin/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davebryson/beepbeep/62db46d268c6cb6ad86345562b3c77f8ff070b27/deps/erlydtl/ebin/.gitignore
--------------------------------------------------------------------------------
/deps/erlydtl/priv/custom_tags/flashvideo:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/deps/erlydtl/src/Makefile:
--------------------------------------------------------------------------------
1 | include ../../../support/include.mk
2 |
3 | all: $(EBIN_FILES_NO_DOCS)
4 |
5 | debug:
6 | $(MAKE) DEBUG=-DDEBUG
7 |
8 | clean:
9 | rm -rf $(EBIN_FILES_NO_DOCS)
10 |
--------------------------------------------------------------------------------
/deps/erlydtl/src/erlydtl.erl:
--------------------------------------------------------------------------------
1 | %%%-------------------------------------------------------------------
2 | %%% File: erlydtl.erl
3 | %%% @author Roberto Saccon [http://rsaccon.com]
4 | %%% @author Evan Miller
5 | %%% @copyright 2008 Roberto Saccon, Evan Miller
6 | %%% @doc
7 | %%% Public interface for ErlyDTL
8 | %%% @end
9 | %%%
10 | %%% The MIT License
11 | %%%
12 | %%% Copyright (c) 2008 Roberto Saccon, Evan Miller
13 | %%%
14 | %%% Permission is hereby granted, free of charge, to any person obtaining a copy
15 | %%% of this software and associated documentation files (the "Software"), to deal
16 | %%% in the Software without restriction, including without limitation the rights
17 | %%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18 | %%% copies of the Software, and to permit persons to whom the Software is
19 | %%% furnished to do so, subject to the following conditions:
20 | %%%
21 | %%% The above copyright notice and this permission notice shall be included in
22 | %%% all copies or substantial portions of the Software.
23 | %%%
24 | %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 | %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 | %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 | %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 | %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29 | %%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30 | %%% THE SOFTWARE.
31 | %%%
32 | %%% @since 2007-11-11 by Roberto Saccon, Evan Miller
33 | %%%-------------------------------------------------------------------
34 | -module(erlydtl).
35 | -author('rsaccon@gmail.com').
36 | -author('emmiller@gmail.com').
37 |
38 | %% API
39 | -export([compile/2, compile/3]).
40 |
41 | compile(FileOrBinary, Module) ->
42 | erlydtl_compiler:compile(FileOrBinary, Module).
43 |
44 | compile(FileOrBinary, Module, Options) ->
45 | erlydtl_compiler:compile(FileOrBinary, Module, Options).
46 |
--------------------------------------------------------------------------------
/deps/erlydtl/src/erlydtl_dateformat.erl:
--------------------------------------------------------------------------------
1 | -module(erlydtl_dateformat).
2 | -export([format/1, format/2]).
3 |
4 | -define(TAG_SUPPORTED(C),
5 | C =:= $a orelse
6 | C =:= $A orelse
7 | C =:= $b orelse
8 | C =:= $B orelse
9 | C =:= $d orelse
10 | C =:= $D orelse
11 | C =:= $f orelse
12 | C =:= $F orelse
13 | C =:= $g orelse
14 | C =:= $G orelse
15 | C =:= $h orelse
16 | C =:= $H orelse
17 | C =:= $i orelse
18 | C =:= $I orelse
19 | C =:= $j orelse
20 | C =:= $l orelse
21 | C =:= $L orelse
22 | C =:= $m orelse
23 | C =:= $M orelse
24 | C =:= $n orelse
25 | C =:= $N orelse
26 | C =:= $O orelse
27 | C =:= $P orelse
28 | C =:= $r orelse
29 | C =:= $s orelse
30 | C =:= $S orelse
31 | C =:= $t orelse
32 | C =:= $T orelse
33 | C =:= $U orelse
34 | C =:= $w orelse
35 | C =:= $W orelse
36 | C =:= $y orelse
37 | C =:= $Y orelse
38 | C =:= $z orelse
39 | C =:= $Z
40 | ).
41 |
42 | %
43 | % Format the current date/time
44 | %
45 | format(FormatString) ->
46 | {Date, Time} = erlang:localtime(),
47 | replace_tags(Date, Time, FormatString).
48 | %
49 | % Format a tuple of the form {{Y,M,D},{H,M,S}}
50 | % This is the format returned by erlang:localtime()
51 | % and other standard date/time BIFs
52 | %
53 | format({{_,_,_} = Date,{_,_,_} = Time}, FormatString) ->
54 | replace_tags(Date, Time, FormatString);
55 | %
56 | % Format a tuple of the form {Y,M,D}
57 | %
58 | format({_,_,_} = Date, FormatString) ->
59 | replace_tags(Date, {0,0,0}, FormatString);
60 | format(DateTime, FormatString) ->
61 | io:format("Unrecognised date paramater : ~p~n", [DateTime]),
62 | FormatString.
63 |
64 | replace_tags(Date, Time, Input) ->
65 | replace_tags(Date, Time, Input, [], noslash).
66 | replace_tags(_Date, _Time, [], Out, _State) ->
67 | lists:reverse(Out);
68 | replace_tags(Date, Time, [C|Rest], Out, noslash) when ?TAG_SUPPORTED(C) ->
69 | replace_tags(Date, Time, Rest,
70 | lists:reverse(tag_to_value(C, Date, Time)) ++ Out, noslash);
71 | replace_tags(Date, Time, [$\\|Rest], Out, noslash) ->
72 | replace_tags(Date, Time, Rest, Out, slash);
73 | replace_tags(Date, Time, [C|Rest], Out, slash) ->
74 | replace_tags(Date, Time, Rest, [C|Out], noslash);
75 | replace_tags(Date, Time, [C|Rest], Out, _State) ->
76 | replace_tags(Date, Time, Rest, [C|Out], noslash).
77 |
78 |
79 | %-----------------------------------------------------------
80 | % Time formatting
81 | %-----------------------------------------------------------
82 |
83 | % 'a.m.' or 'p.m.'
84 | tag_to_value($a, _, {H, _, _}) when H > 11 -> "p.m.";
85 | tag_to_value($a, _, _) -> "a.m.";
86 |
87 | % 'AM' or 'PM'
88 | tag_to_value($A, _, {H, _, _}) when H > 11 -> "PM";
89 | tag_to_value($A, _, _) -> "AM";
90 |
91 | % Swatch Internet time
92 | tag_to_value($B, _, _) ->
93 | ""; % NotImplementedError
94 |
95 | %
96 | % Time, in 12-hour hours and minutes, with minutes
97 | % left off if they're zero.
98 | %
99 | % Examples: '1', '1:30', '2:05', '2'
100 | %
101 | % Proprietary extension.
102 | %
103 | tag_to_value($f, Date, {H, 0, S}) ->
104 | % If min is zero then return the hour only
105 | tag_to_value($g, Date, {H, 0, S});
106 | tag_to_value($f, Date, Time) ->
107 | % Otherwise return hours and mins
108 | tag_to_value($g, Date, Time)
109 | ++ ":" ++ tag_to_value($i, Date, Time);
110 |
111 | % Hour, 12-hour format without leading zeros; i.e. '1' to '12'
112 | tag_to_value($g, _, {H,_,_}) ->
113 | integer_to_list(hour_24to12(H));
114 |
115 | % Hour, 24-hour format without leading zeros; i.e. '0' to '23'
116 | tag_to_value($G, _, {H,_,_}) ->
117 | integer_to_list(H);
118 |
119 | % Hour, 12-hour format; i.e. '01' to '12'
120 | tag_to_value($h, _, {H,_,_}) ->
121 | integer_to_list_zerofill(integer_to_list(hour_24to12(H)));
122 |
123 | % Hour, 24-hour format; i.e. '00' to '23'
124 | tag_to_value($H, _, {H,_,_}) ->
125 | integer_to_list_zerofill(H);
126 |
127 | % Minutes; i.e. '00' to '59'
128 | tag_to_value($i, _, {_,M,_}) ->
129 | integer_to_list_zerofill(M);
130 |
131 | % Time, in 12-hour hours, minutes and 'a.m.'/'p.m.', with minutes left off
132 | % if they're zero and the strings 'midnight' and 'noon' if appropriate.
133 | % Examples: '1 a.m.', '1:30 p.m.', 'midnight', 'noon', '12:30 p.m.'
134 | % Proprietary extension.
135 | tag_to_value($P, _, {0, 0, _}) -> "midnight";
136 | tag_to_value($P, _, {12, 0, _}) -> "noon";
137 | tag_to_value($P, Date, Time) ->
138 | tag_to_value($f, Date, Time)
139 | ++ " " ++ tag_to_value($a, Date, Time);
140 |
141 | % Seconds; i.e. '00' to '59'
142 | tag_to_value($s, _, {_,_,S}) ->
143 | integer_to_list_zerofill(S);
144 |
145 | %-----------------------------------------------------------
146 | % Date formatting
147 | %-----------------------------------------------------------
148 |
149 | % Month, textual, 3 letters, lowercase; e.g. 'jan'
150 | tag_to_value($b, {_,M,_}, _) ->
151 | string:sub_string(monthname(M), 1, 3);
152 |
153 | % Day of the month, 2 digits with leading zeros; i.e. '01' to '31'
154 | tag_to_value($d, {_, _, D}, _) ->
155 | integer_to_list_zerofill(D);
156 |
157 | % Day of the week, textual, 3 letters; e.g. 'Fri'
158 | tag_to_value($D, Date, _) ->
159 | Dow = calendar:day_of_the_week(Date),
160 | ucfirst(string:sub_string(dayname(Dow), 1, 3));
161 |
162 | % Month, textual, long; e.g. 'January'
163 | tag_to_value($F, {_,M,_}, _) ->
164 | ucfirst(monthname(M));
165 |
166 | % '1' if Daylight Savings Time, '0' otherwise.
167 | tag_to_value($I, _, _) ->
168 | "TODO";
169 |
170 | % Day of the month without leading zeros; i.e. '1' to '31'
171 | tag_to_value($j, {_, _, D}, _) ->
172 | integer_to_list(D);
173 |
174 | % Day of the week, textual, long; e.g. 'Friday'
175 | tag_to_value($l, Date, _) ->
176 | ucfirst(dayname(calendar:day_of_the_week(Date)));
177 |
178 | % Boolean for whether it is a leap year; i.e. True or False
179 | tag_to_value($L, {Y,_,_}, _) ->
180 | case calendar:is_leap_year(Y) of
181 | true -> "True";
182 | _ -> "False"
183 | end;
184 |
185 | % Month; i.e. '01' to '12'
186 | tag_to_value($m, {_, M, _}, _) ->
187 | integer_to_list_zerofill(M);
188 |
189 | % Month, textual, 3 letters; e.g. 'Jan'
190 | tag_to_value($M, {_,M,_}, _) ->
191 | ucfirst(string:sub_string(monthname(M), 1, 3));
192 |
193 | % Month without leading zeros; i.e. '1' to '12'
194 | tag_to_value($n, {_, M, _}, _) ->
195 | integer_to_list(M);
196 |
197 | % Month abbreviation in Associated Press style. Proprietary extension.
198 | tag_to_value($N, {_,M,_}, _) when M =:= 9 ->
199 | % Special case - "Sept."
200 | ucfirst(string:sub_string(monthname(M), 1, 4)) ++ ".";
201 | tag_to_value($N, {_,M,_}, _) when M < 3 orelse M > 7 ->
202 | % Jan, Feb, Aug, Oct, Nov, Dec are all
203 | % abbreviated with a full-stop appended.
204 | ucfirst(string:sub_string(monthname(M), 1, 3)) ++ ".";
205 | tag_to_value($N, {_,M,_}, _) ->
206 | % The rest are the fullname.
207 | ucfirst(monthname(M));
208 |
209 | % Difference to Greenwich time in hours; e.g. '+0200'
210 | tag_to_value($O, Date, Time) ->
211 | Diff = utc_diff(Date, Time),
212 | Offset = case utc_diff(Date, Time) of
213 | Diff when abs(Diff) > 2400 -> "+0000";
214 | Diff when Diff < 0 ->
215 | io_lib:format("-~4..0w", [trunc(abs(Diff))]);
216 | _ ->
217 | io_lib:format("+~4..0w", [trunc(abs(Diff))])
218 | end,
219 | lists:flatten(Offset);
220 |
221 | % RFC 2822 formatted date; e.g. 'Thu, 21 Dec 2000 16:01:07 +0200'
222 | tag_to_value($r, Date, Time) ->
223 | replace_tags(Date, Time, "D, j M Y H:i:s O");
224 |
225 | % English ordinal suffix for the day of the month, 2 characters;
226 | % i.e. 'st', 'nd', 'rd' or 'th'
227 | tag_to_value($S, {_, _, D}, _) when
228 | D rem 100 =:= 11 orelse
229 | D rem 100 =:= 12 orelse
230 | D rem 100 =:= 13 -> "th";
231 | tag_to_value($S, {_, _, D}, _) when D rem 10 =:= 1 -> "st";
232 | tag_to_value($S, {_, _, D}, _) when D rem 10 =:= 2 -> "nd";
233 | tag_to_value($S, {_, _, D}, _) when D rem 10 =:= 3 -> "rd";
234 | tag_to_value($S, _, _) -> "th";
235 |
236 | % Number of days in the given month; i.e. '28' to '31'
237 | tag_to_value($t, {Y,M,_}, _) ->
238 | integer_to_list(calendar:last_day_of_the_month(Y,M));
239 |
240 | % Time zone of this machine; e.g. 'EST' or 'MDT'
241 | tag_to_value($T, _, _) ->
242 | "TODO";
243 |
244 | % Seconds since the Unix epoch (January 1 1970 00:00:00 GMT)
245 | tag_to_value($U, Date, Time) ->
246 | EpochSecs = calendar:datetime_to_gregorian_seconds({Date, Time})
247 | - calendar:datetime_to_gregorian_seconds({{1970,1,1},{0,0,0}}),
248 | integer_to_list(EpochSecs);
249 |
250 | % Day of the week, numeric, i.e. '0' (Sunday) to '6' (Saturday)
251 | tag_to_value($w, Date, _) ->
252 | % Note: calendar:day_of_the_week returns
253 | % 1 | .. | 7. Monday = 1, Tuesday = 2, ..., Sunday = 7
254 | integer_to_list(calendar:day_of_the_week(Date) rem 7);
255 |
256 | % ISO-8601 week number of year, weeks starting on Monday
257 | tag_to_value($W, {Y,M,D}, _) ->
258 | integer_to_list(year_weeknum(Y,M,D));
259 |
260 | % Year, 2 digits; e.g. '99'
261 | tag_to_value($y, {Y, _, _}, _) ->
262 | string:sub_string(integer_to_list(Y), 3);
263 |
264 | % Year, 4 digits; e.g. '1999'
265 | tag_to_value($Y, {Y, _, _}, _) ->
266 | integer_to_list(Y);
267 |
268 | % Day of the year; i.e. '0' to '365'
269 | tag_to_value($z, {Y,M,D}, _) ->
270 | integer_to_list(day_of_year(Y,M,D));
271 |
272 | % Time zone offset in seconds (i.e. '-43200' to '43200'). The offset for
273 | % timezones west of UTC is always negative, and for those east of UTC is
274 | % always positive.
275 | tag_to_value($Z, _, _) ->
276 | "TODO";
277 |
278 | tag_to_value(C, Date, Time) ->
279 | io:format("Unimplemented tag : ~p [Date : ~p] [Time : ~p]",
280 | [C, Date, Time]),
281 | "".
282 |
283 | % Date helper functions
284 | day_of_year(Y,M,D) ->
285 | day_of_year(Y,M,D,0).
286 | day_of_year(_Y,M,D,Count) when M =< 1 ->
287 | D + Count;
288 | day_of_year(Y,M,D,Count) when M =< 12 ->
289 | day_of_year(Y, M - 1, D, Count + calendar:last_day_of_the_month(Y,M));
290 | day_of_year(Y,_M,D,_Count) ->
291 | day_of_year(Y, 12, D, 0).
292 |
293 | hour_24to12(0) -> 12;
294 | hour_24to12(H) when H < 13 -> H;
295 | hour_24to12(H) when H < 24 -> H - 12;
296 | hour_24to12(H) -> H.
297 |
298 | year_weeknum(Y,M,D) ->
299 | First = (calendar:day_of_the_week(Y, 1, 1) rem 7) - 1,
300 | Wk = ((((calendar:date_to_gregorian_days(Y, M, D) -
301 | calendar:date_to_gregorian_days(Y, 1, 1)) + First) div 7)
302 | + (case First < 4 of true -> 1; _ -> 0 end)),
303 | case Wk of
304 | 0 -> weeks_in_year(Y - 1);
305 | _ -> case weeks_in_year(Y) of
306 | WksInThisYear when Wk > WksInThisYear -> 1;
307 | _ -> Wk
308 | end
309 | end.
310 |
311 | weeks_in_year(Y) ->
312 | D1 = calendar:day_of_the_week(Y, 1, 1),
313 | D2 = calendar:day_of_the_week(Y, 12, 31),
314 | if (D1 =:= 4 orelse D2 =:= 4) -> 53; true -> 52 end.
315 |
316 | utc_diff(Date, Time) ->
317 | % Note: this code is plagarised from yaws_log
318 | UTime = erlang:universaltime(),
319 | DiffSecs = calendar:datetime_to_gregorian_seconds({Date,Time})
320 | - calendar:datetime_to_gregorian_seconds(UTime),
321 | ((DiffSecs/3600)*100).
322 |
323 | dayname(1) -> "monday";
324 | dayname(2) -> "tuesday";
325 | dayname(3) -> "wednesday";
326 | dayname(4) -> "thursday";
327 | dayname(5) -> "friday";
328 | dayname(6) -> "saturday";
329 | dayname(7) -> "sunday";
330 | dayname(_) -> "???".
331 |
332 | monthname(1) -> "january";
333 | monthname(2) -> "february";
334 | monthname(3) -> "march";
335 | monthname(4) -> "april";
336 | monthname(5) -> "may";
337 | monthname(6) -> "june";
338 | monthname(7) -> "july";
339 | monthname(8) -> "august";
340 | monthname(9) -> "september";
341 | monthname(10) -> "october";
342 | monthname(11) -> "november";
343 | monthname(12) -> "december";
344 | monthname(_) -> "???".
345 |
346 | % Utility functions
347 | integer_to_list_zerofill(N) when N < 10 ->
348 | lists:flatten(io_lib:format("~2..0B", [N]));
349 | integer_to_list_zerofill(N) ->
350 | integer_to_list(N).
351 |
352 | ucfirst([First | Rest]) when First >= $a, First =< $z ->
353 | [First-($a-$A) | Rest];
354 | ucfirst(Other) ->
355 | Other.
356 |
357 |
358 |
--------------------------------------------------------------------------------
/deps/erlydtl/src/erlydtl_deps.erl:
--------------------------------------------------------------------------------
1 | %%%-------------------------------------------------------------------
2 | %%% File: erlydtl_deps.erl
3 | %%% @author Roberto Saccon [http://rsaccon.com]
4 | %%% @author Evan Miller
5 | %%% @copyright 2008 Roberto Saccon, Evan Miller
6 | %%% @doc
7 | %%% ErlyDTL helper module
8 | %%% @end
9 | %%%
10 | %%% The MIT License
11 | %%%
12 | %%% Copyright (c) 2007 Roberto Saccon, Evan Miller
13 | %%%
14 | %%% Permission is hereby granted, free of charge, to any person obtaining a copy
15 | %%% of this software and associated documentation files (the "Software"), to deal
16 | %%% in the Software without restriction, including without limitation the rights
17 | %%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18 | %%% copies of the Software, and to permit persons to whom the Software is
19 | %%% furnished to do so, subject to the following conditions:
20 | %%%
21 | %%% The above copyright notice and this permission notice shall be included in
22 | %%% all copies or substantial portions of the Software.
23 | %%%
24 | %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 | %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 | %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 | %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 | %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29 | %%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30 | %%% THE SOFTWARE.
31 | %%%
32 | %%% @since 2007-12-16 by Roberto Saccon, Evan Miller
33 | %%%-------------------------------------------------------------------
34 | -module(erlydtl_deps).
35 | -author('rsaccon@gmail.com').
36 | -author('emmiller@gmail.com').
37 |
38 | %% API
39 | -export([get_base_dir/0, get_base_dir/1]).
40 |
41 | %%====================================================================
42 | %% API
43 | %%====================================================================
44 | %% @spec get_base_dir(Module) -> string()
45 | %% @doc Return the application directory for Module. It assumes Module is in
46 | %% a standard OTP layout application in the ebin or src directory.
47 | get_base_dir(Module) ->
48 | {file, Here} = code:is_loaded(Module),
49 | filename:dirname(filename:dirname(Here)).
50 |
51 | %% @spec get_base_dir() -> string()
52 | %% @doc Return the application directory for this application. Equivalent to
53 | %% get_base_dir(?MODULE).
54 | get_base_dir() ->
55 | get_base_dir(?MODULE).
56 |
57 | %%====================================================================
58 | %% Internal functions
59 | %%====================================================================
60 |
61 |
--------------------------------------------------------------------------------
/deps/erlydtl/src/erlydtl_filters.erl:
--------------------------------------------------------------------------------
1 | %%%-------------------------------------------------------------------
2 | %%% File: erlydtl_filters.erl
3 | %%% @author Roberto Saccon [http://rsaccon.com]
4 | %%% @author Evan Miller
5 | %%% @copyright 2008 Roberto Saccon, Evan Miller
6 | %%% @doc
7 | %%% Template filters
8 | %%% @end
9 | %%%
10 | %%% The MIT License
11 | %%%
12 | %%% Copyright (c) 2007 Roberto Saccon, Evan Miller
13 | %%%
14 | %%% Permission is hereby granted, free of charge, to any person obtaining a copy
15 | %%% of this software and associated documentation files (the "Software"), to deal
16 | %%% in the Software without restriction, including without limitation the rights
17 | %%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18 | %%% copies of the Software, and to permit persons to whom the Software is
19 | %%% furnished to do so, subject to the following conditions:
20 | %%%
21 | %%% The above copyright notice and this permission notice shall be included in
22 | %%% all copies or substantial portions of the Software.
23 | %%%
24 | %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 | %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 | %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 | %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 | %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29 | %%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30 | %%% THE SOFTWARE.
31 | %%%
32 | %%% @since 2007-11-11 by Roberto Saccon, Evan Miller
33 | %%%-------------------------------------------------------------------
34 | -module(erlydtl_filters).
35 | -author('rsaccon@gmail.com').
36 | -author('emmiller@gmail.com').
37 |
38 | -compile(export_all).
39 |
40 | -define(NO_ENCODE(C), ((C >= $a andalso C =< $z) orelse
41 | (C >= $A andalso C =< $Z) orelse
42 | (C >= $0 andalso C =< $9) orelse
43 | (C =:= $\. orelse C =:= $-
44 | orelse C =:= $~ orelse C =:= $_))).
45 |
46 | add([Input], Number) when is_list(Input) or is_binary(Input) ->
47 | add(Input, Number);
48 | add(Input, Number) when is_binary(Input) ->
49 | list_to_binary(add(binary_to_list(Input), Number));
50 | add(Input, Number) when is_list(Input) ->
51 | integer_to_list(add(list_to_integer(Input), Number));
52 | add(Input, Number) when is_integer(Input) ->
53 | Input + Number.
54 |
55 | capfirst([Input]) when is_list(Input) or is_binary (Input) ->
56 | capfirst(Input);
57 | capfirst([H|T]) when H >= $a andalso H =< $z ->
58 | [H + $A - $a | T];
59 | capfirst(<>) when Byte >= $a andalso Byte =< $z ->
60 | [<<(Byte + $A - $a)>>, Binary].
61 |
62 | center([Input], Number) when is_list(Input) or is_binary(Input) ->
63 | center(Input, Number);
64 | center(Input, Number) when is_binary(Input) ->
65 | list_to_binary(center(binary_to_list(Input), Number));
66 | center(Input, Number) when is_list(Input) ->
67 | string:centre(Input, Number).
68 |
69 | date([Input], FormatStr) when is_list(Input) or is_binary(Input) ->
70 | date(Input, FormatStr);
71 | date(Input, FormatStr) when is_binary(Input) ->
72 | list_to_binary(date(binary_to_list(Input), FormatStr));
73 | date([{{_,_,_} = Date,{_,_,_} = Time}], FormatStr) ->
74 | erlydtl_dateformat:format({Date, Time}, FormatStr);
75 | date([{_,_,_} = Date], FormatStr) ->
76 | erlydtl_dateformat:format(Date, FormatStr);
77 | date(Input, _FormatStr) when is_list(Input) ->
78 | io:format("Unexpected date parameter : ~p~n", [Input]),
79 | "".
80 |
81 |
82 | escapejs([Input]) when is_list(Input) or is_binary(Input) ->
83 | escapejs(Input);
84 | escapejs(Input) when is_binary(Input) ->
85 | escapejs(Input, 0);
86 | escapejs(Input) when is_list(Input) ->
87 | escapejs(Input, []).
88 |
89 | first([Input]) when is_list(Input) or is_binary(Input) ->
90 | first(Input);
91 | first([First|_Rest]) ->
92 | [First];
93 | first(<>) ->
94 | <>.
95 |
96 | fix_ampersands([Input]) when is_list(Input) or is_binary(Input) ->
97 | fix_ampersands(Input);
98 | fix_ampersands(Input) when is_binary(Input) ->
99 | fix_ampersands(Input, 0);
100 | fix_ampersands(Input) when is_list(Input) ->
101 | fix_ampersands(Input, []).
102 |
103 | force_escape([Input]) when is_list(Input) or is_binary(Input) ->
104 | force_escape(Input);
105 | force_escape(Input) when is_list(Input) ->
106 | escape(Input, []);
107 | force_escape(Input) when is_binary(Input) ->
108 | escape(Input, 0).
109 |
110 | format_integer(Input) when is_integer(Input) ->
111 | integer_to_list(Input);
112 | format_integer(Input) ->
113 | Input.
114 |
115 | format_number(Input) when is_integer(Input) ->
116 | integer_to_list(Input);
117 | format_number(Input) when is_float(Input) ->
118 | io_lib:format("~p", [Input]);
119 | format_number(Input) ->
120 | Input.
121 |
122 | join([Input], Separator) when is_list(Input) ->
123 | string:join(Input, Separator).
124 |
125 | last([Input]) when is_list(Input) or is_binary(Input) ->
126 | last(Input);
127 | last(Input) when is_binary(Input) ->
128 | case size(Input) of
129 | 0 -> Input;
130 | N ->
131 | Offset = N - 1,
132 | <<_:Offset/binary, Byte/binary>> = Input,
133 | Byte
134 | end;
135 | last(Input) when is_list(Input) ->
136 | [lists:last(Input)].
137 |
138 | length([]) -> "0";
139 | length([Input]) when is_list(Input) ->
140 | integer_to_list(erlang:length(Input));
141 | length([Input]) when is_binary(Input) ->
142 | integer_to_list(size(Input)).
143 |
144 | length_is(Input, Number) when is_list(Input), is_integer(Number) ->
145 | length_is(Input, integer_to_list(Number));
146 | length_is(Input, Number) when is_list(Input), is_list(Number) ->
147 | ?MODULE:length(Input) =:= Number.
148 |
149 | linebreaksbr([Input]) when is_list(Input) or is_binary(Input) ->
150 | linebreaksbr(Input);
151 | linebreaksbr(Input) when is_binary(Input) ->
152 | linebreaksbr(Input, 0);
153 | linebreaksbr(Input) ->
154 | linebreaksbr(Input, []).
155 |
156 | ljust([Input], Number) when is_list(Input) or is_binary(Input) ->
157 | ljust(Input, Number);
158 | ljust(Input, Number) when is_binary(Input) ->
159 | list_to_binary(ljust(binary_to_list(Input), Number));
160 | ljust(Input, Number) when is_list(Input) ->
161 | string:left(Input, Number).
162 |
163 | lower([Input]) when is_list(Input) or is_binary(Input) ->
164 | lower(Input);
165 | lower(Input) when is_binary(Input) ->
166 | lower(Input, 0);
167 | lower(Input) ->
168 | string:to_lower(Input).
169 |
170 | rjust([Input], Number) when is_list(Input) or is_binary(Input) ->
171 | rjust(Input, Number);
172 | rjust(Input, Number) when is_binary(Input) ->
173 | list_to_binary(rjust(binary_to_list(Input), Number));
174 | rjust(Input, Number) ->
175 | string:right(Input, Number).
176 |
177 | upper([Input]) when is_list(Input) or is_binary(Input) ->
178 | upper(Input);
179 | upper(Input) when is_binary(Input) ->
180 | list_to_binary(upper(binary_to_list(Input)));
181 | upper(Input) ->
182 | string:to_upper(Input).
183 |
184 | urlencode([Input]) when is_list(Input) or is_binary(Input) ->
185 | urlencode(Input);
186 | urlencode(Input) when is_binary(Input) ->
187 | urlencode(Input, 0);
188 | urlencode(Input) when is_list(Input) ->
189 | urlencode(Input, []).
190 |
191 | % internal
192 |
193 | escape(Binary, Index) when is_binary(Binary) ->
194 | case Binary of
195 | <> ->
196 | process_binary_match(Pre, <<"<">>, size(Post), escape(Post, 0));
197 | <, Post/binary>> ->
198 | process_binary_match(Pre, <<">">>, size(Post), escape(Post, 0));
199 | <> ->
200 | process_binary_match(Pre, <<"&">>, size(Post), escape(Post, 0));
201 | <> ->
202 | process_binary_match(Pre, <<""">>, size(Post), escape(Post, 0));
203 | <> ->
204 | process_binary_match(Pre, <<"'">>, size(Post), escape(Post, 0));
205 | <<_:Index/binary, _, _/binary>> ->
206 | escape(Binary, Index + 1);
207 | Binary ->
208 | Binary
209 | end;
210 | escape([], Acc) ->
211 | lists:reverse(Acc);
212 | escape("<" ++ Rest, Acc) ->
213 | escape(Rest, lists:reverse("<", Acc));
214 | escape(">" ++ Rest, Acc) ->
215 | escape(Rest, lists:reverse(">", Acc));
216 | escape("&" ++ Rest, Acc) ->
217 | escape(Rest, lists:reverse("&", Acc));
218 | escape("\"" ++ Rest, Acc) ->
219 | escape(Rest, lists:reverse(""", Acc));
220 | escape("'" ++ Rest, Acc) ->
221 | escape(Rest, lists:reverse("'", Acc));
222 | escape([C | Rest], Acc) ->
223 | escape(Rest, [C | Acc]).
224 |
225 |
226 | escapejs([], Acc) ->
227 | lists:reverse(Acc);
228 | escapejs("'" ++ Rest, Acc) ->
229 | escapejs(Rest, lists:reverse("\\'", Acc));
230 | escapejs("\"" ++ Rest, Acc) ->
231 | escapejs(Rest, lists:reverse("\\\"", Acc));
232 | escapejs([C | Rest], Acc) ->
233 | escapejs(Rest, [C | Acc]);
234 | escapejs(Binary, Index) when is_binary(Binary) ->
235 | case Binary of
236 | <> ->
237 | process_binary_match(Pre, <<"\\'">>, size(Post), escapejs(Post, 0));
238 | <> ->
239 | process_binary_match(Pre, <<"\\\"">>, size(Post), escapejs(Post, 0));
240 | <<_:Index/binary, _/binary>> ->
241 | escapejs(Binary, Index + 1);
242 | _ ->
243 | Binary
244 | end.
245 |
246 | fix_ampersands(Input, Index) when is_binary(Input) ->
247 | case Input of
248 | <> ->
249 | process_binary_match(Pre, <<"&">>, size(Post), Post);
250 | <<_:Index/binary, _/binary>> ->
251 | fix_ampersands(Input, Index + 1);
252 | _ ->
253 | Input
254 | end;
255 | fix_ampersands([], Acc) ->
256 | lists:reverse(Acc);
257 | fix_ampersands("&" ++ Rest, Acc) ->
258 | fix_ampersands(Rest, lists:reverse("&", Acc));
259 | fix_ampersands([C | Rest], Acc) ->
260 | fix_ampersands(Rest, [C | Acc]).
261 |
262 | linebreaksbr(Input, Index) when is_binary(Input) ->
263 | Break = <<" ">>,
264 | case Input of
265 | <> ->
266 | process_binary_match(Pre, Break, size(Post), linebreaksbr(Post, 0));
267 | <> ->
268 | process_binary_match(Pre, Break, size(Post), linebreaksbr(Post, 0));
269 | <<_:Index/binary, _/binary>> ->
270 | linebreaksbr(Input, Index + 1);
271 | _ ->
272 | Input
273 | end;
274 | linebreaksbr([], Acc) ->
275 | lists:reverse(Acc);
276 | linebreaksbr("\r\n" ++ Rest, Acc) ->
277 | linebreaksbr(Rest, lists:reverse(" ", Acc));
278 | linebreaksbr("\n" ++ Rest, Acc) ->
279 | linebreaksbr(Rest, lists:reverse(" ", Acc));
280 | linebreaksbr([C | Rest], Acc) ->
281 | linebreaksbr(Rest, [C | Acc]).
282 |
283 | lower(Input, Index) ->
284 | case Input of
285 | <> when Byte >= $A andalso Byte =< $Z ->
286 | process_binary_match(Pre, <<(Byte - $A + $a)>>, size(Post), lower(Post, 0));
287 | <<_:Index/binary, _/binary>> ->
288 | lower(Input, Index + 1);
289 | _ ->
290 | Input
291 | end.
292 |
293 | % Taken from quote_plus of mochiweb_util
294 |
295 | urlencode(Input, Index) when is_binary(Input) ->
296 | case Input of
297 | <<_:Index/binary, Byte, _/binary>> when ?NO_ENCODE(Byte) ->
298 | urlencode(Input, Index + 1);
299 | <> ->
300 | process_binary_match(Pre, <<"+">>, size(Post), urlencode(Post, 0));
301 | <> ->
302 | HiDigit = hexdigit(Hi),
303 | LoDigit = hexdigit(Lo),
304 | Code = <<$\%, HiDigit, LoDigit>>,
305 | process_binary_match(Pre, Code, size(Post), urlencode(Post, 0));
306 | Input ->
307 | Input
308 | end;
309 | urlencode([], Acc) ->
310 | lists:reverse(Acc);
311 | urlencode([C | Rest], Acc) when ?NO_ENCODE(C) ->
312 | urlencode(Rest, [C | Acc]);
313 | urlencode([$\s | Rest], Acc) ->
314 | urlencode(Rest, [$+ | Acc]);
315 | urlencode([C | Rest], Acc) ->
316 | <> = <>,
317 | urlencode(Rest, [hexdigit(Lo), hexdigit(Hi), $\% | Acc]).
318 |
319 | hexdigit(C) when C < 10 -> $0 + C;
320 | hexdigit(C) when C < 16 -> $A + (C - 10).
321 |
322 | process_binary_match(Pre, Insertion, SizePost, Post) ->
323 | case {size(Pre), SizePost} of
324 | {0, 0} -> Insertion;
325 | {0, _} -> [Insertion, Post];
326 | {_, 0} -> [Pre, Insertion];
327 | _ -> [Pre, Insertion, Post]
328 | end.
329 |
--------------------------------------------------------------------------------
/deps/erlydtl/src/erlydtl_parser.yrl:
--------------------------------------------------------------------------------
1 | %%%-------------------------------------------------------------------
2 | %%% File: erlydtl_parser.erl
3 | %%% @author Roberto Saccon [http://rsaccon.com]
4 | %%% @author Evan Miller
5 | %%% @copyright 2008 Roberto Saccon, Evan Miller
6 | %%% @doc Template language grammar
7 | %%% @reference See http://erlydtl.googlecode.com for more information
8 | %%% @end
9 | %%%
10 | %%% The MIT License
11 | %%%
12 | %%% Copyright (c) 2007 Roberto Saccon, Evan Miller
13 | %%%
14 | %%% Permission is hereby granted, free of charge, to any person obtaining a copy
15 | %%% of this software and associated documentation files (the "Software"), to deal
16 | %%% in the Software without restriction, including without limitation the rights
17 | %%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18 | %%% copies of the Software, and to permit persons to whom the Software is
19 | %%% furnished to do so, subject to the following conditions:
20 | %%%
21 | %%% The above copyright notice and this permission notice shall be included in
22 | %%% all copies or substantial portions of the Software.
23 | %%%
24 | %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 | %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 | %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 | %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 | %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29 | %%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30 | %%% THE SOFTWARE.
31 | %%%
32 | %%% @since 2007-11-11 by Roberto Saccon, Evan Miller
33 | %%%-------------------------------------------------------------------
34 |
35 | Nonterminals
36 | Elements
37 | Literal
38 |
39 | ValueBraced
40 |
41 | ExtendsTag
42 | IncludeTag
43 | NowTag
44 |
45 | BlockBlock
46 | BlockBraced
47 | EndBlockBraced
48 |
49 | CommentBlock
50 | CommentBraced
51 | EndCommentBraced
52 |
53 | CycleTag
54 | CycleNames
55 | CycleNamesCompat
56 |
57 | ForBlock
58 | ForBraced
59 | EndForBraced
60 | ForExpression
61 | ForGroup
62 |
63 | IfBlock
64 | IfBraced
65 | IfExpression
66 | ElseBraced
67 | EndIfBraced
68 |
69 | IfEqualBlock
70 | IfEqualBraced
71 | IfEqualExpression
72 | EndIfEqualBraced
73 |
74 | IfNotEqualBlock
75 | IfNotEqualBraced
76 | IfNotEqualExpression
77 | EndIfNotEqualBraced
78 |
79 | AutoEscapeBlock
80 | AutoEscapeBraced
81 | EndAutoEscapeBraced
82 |
83 | Value
84 | Variable
85 | Filter
86 |
87 | LoadTag
88 | LoadNames
89 |
90 | CustomTag
91 | Args
92 |
93 | CallTag
94 | CallWithTag.
95 |
96 | Terminals
97 | autoescape_keyword
98 | block_keyword
99 | call_keyword
100 | close_tag
101 | close_var
102 | comment_keyword
103 | colon
104 | comma
105 | cycle_keyword
106 | dot
107 | else_keyword
108 | endautoescape_keyword
109 | endblock_keyword
110 | endcomment_keyword
111 | endfor_keyword
112 | endif_keyword
113 | endifequal_keyword
114 | endifnotequal_keyword
115 | equal
116 | extends_keyword
117 | for_keyword
118 | identifier
119 | if_keyword
120 | ifequal_keyword
121 | ifnotequal_keyword
122 | in_keyword
123 | include_keyword
124 | load_keyword
125 | not_keyword
126 | now_keyword
127 | number_literal
128 | open_tag
129 | open_var
130 | pipe
131 | string_literal
132 | text
133 | with_keyword.
134 |
135 | Rootsymbol
136 | Elements.
137 |
138 | Elements -> '$empty' : [].
139 | Elements -> Elements text : '$1' ++ ['$2'].
140 | Elements -> Elements ValueBraced : '$1' ++ ['$2'].
141 | Elements -> Elements ExtendsTag : '$1' ++ ['$2'].
142 | Elements -> Elements IncludeTag : '$1' ++ ['$2'].
143 | Elements -> Elements NowTag : '$1' ++ ['$2'].
144 | Elements -> Elements LoadTag : '$1' ++ ['$2'].
145 | Elements -> Elements CycleTag : '$1' ++ ['$2'].
146 | Elements -> Elements BlockBlock : '$1' ++ ['$2'].
147 | Elements -> Elements ForBlock : '$1' ++ ['$2'].
148 | Elements -> Elements IfBlock : '$1' ++ ['$2'].
149 | Elements -> Elements IfEqualBlock : '$1' ++ ['$2'].
150 | Elements -> Elements IfNotEqualBlock : '$1' ++ ['$2'].
151 | Elements -> Elements AutoEscapeBlock : '$1' ++ ['$2'].
152 | Elements -> Elements CommentBlock : '$1' ++ ['$2'].
153 | Elements -> Elements CustomTag : '$1' ++ ['$2'].
154 | Elements -> Elements CallTag : '$1' ++ ['$2'].
155 | Elements -> Elements CallWithTag : '$1' ++ ['$2'].
156 |
157 | ValueBraced -> open_var Value close_var : '$2'.
158 |
159 | Value -> Value pipe Filter : {apply_filter, '$1', '$3'}.
160 | Value -> Variable : '$1'.
161 | Value -> Literal : '$1'.
162 |
163 | Variable -> identifier : {variable, '$1'}.
164 | Variable -> Value dot identifier : {attribute, {'$3', '$1'}}.
165 |
166 | ExtendsTag -> open_tag extends_keyword string_literal close_tag : {extends, '$3'}.
167 | IncludeTag -> open_tag include_keyword string_literal close_tag : {include, '$3'}.
168 | NowTag -> open_tag now_keyword string_literal close_tag : {date, now, '$3'}.
169 |
170 | LoadTag -> open_tag load_keyword LoadNames close_tag : {load, '$3'}.
171 | LoadNames -> identifier : ['$1'].
172 | LoadNames -> LoadNames identifier : '$1' ++ ['$2'].
173 |
174 | BlockBlock -> BlockBraced Elements EndBlockBraced : {block, '$1', '$2'}.
175 | BlockBraced -> open_tag block_keyword identifier close_tag : '$3'.
176 | EndBlockBraced -> open_tag endblock_keyword close_tag.
177 |
178 | CommentBlock -> CommentBraced Elements EndCommentBraced : {comment, '$2'}.
179 | CommentBraced -> open_tag comment_keyword close_tag.
180 | EndCommentBraced -> open_tag endcomment_keyword close_tag.
181 |
182 | CycleTag -> open_tag cycle_keyword CycleNamesCompat close_tag : {cycle_compat, '$3'}.
183 | CycleTag -> open_tag cycle_keyword CycleNames close_tag : {cycle, '$3'}.
184 |
185 | CycleNames -> Value : ['$1'].
186 | CycleNames -> CycleNames Value : '$1' ++ ['$2'].
187 |
188 | CycleNamesCompat -> identifier comma : ['$1'].
189 | CycleNamesCompat -> CycleNamesCompat identifier comma : '$1' ++ ['$2'].
190 | CycleNamesCompat -> CycleNamesCompat identifier : '$1' ++ ['$2'].
191 |
192 | ForBlock -> ForBraced Elements EndForBraced : {for, '$1', '$2'}.
193 | ForBraced -> open_tag for_keyword ForExpression close_tag : '$3'.
194 | EndForBraced -> open_tag endfor_keyword close_tag.
195 | ForExpression -> ForGroup in_keyword Variable : {'in', '$1', '$3'}.
196 | ForGroup -> identifier : ['$1'].
197 | ForGroup -> ForGroup comma identifier : '$1' ++ ['$3'].
198 |
199 | IfBlock -> IfBraced Elements ElseBraced Elements EndIfBraced : {ifelse, '$1', '$2', '$4'}.
200 | IfBlock -> IfBraced Elements EndIfBraced : {'if', '$1', '$2'}.
201 | IfBraced -> open_tag if_keyword IfExpression close_tag : '$3'.
202 | IfExpression -> not_keyword IfExpression : {'not', '$2'}.
203 | IfExpression -> Value : '$1'.
204 |
205 | ElseBraced -> open_tag else_keyword close_tag.
206 | EndIfBraced -> open_tag endif_keyword close_tag.
207 |
208 | IfEqualBlock -> IfEqualBraced Elements ElseBraced Elements EndIfEqualBraced : {ifequalelse, '$1', '$2', '$4'}.
209 | IfEqualBlock -> IfEqualBraced Elements EndIfEqualBraced : {ifequal, '$1', '$2'}.
210 | IfEqualBraced -> open_tag ifequal_keyword IfEqualExpression Value close_tag : ['$3', '$4'].
211 | IfEqualExpression -> Value : '$1'.
212 | EndIfEqualBraced -> open_tag endifequal_keyword close_tag.
213 |
214 | IfNotEqualBlock -> IfNotEqualBraced Elements ElseBraced Elements EndIfNotEqualBraced : {ifnotequalelse, '$1', '$2', '$4'}.
215 | IfNotEqualBlock -> IfNotEqualBraced Elements EndIfNotEqualBraced : {ifnotequal, '$1', '$2'}.
216 | IfNotEqualBraced -> open_tag ifnotequal_keyword IfNotEqualExpression Value close_tag : ['$3', '$4'].
217 | IfNotEqualExpression -> Value : '$1'.
218 | EndIfNotEqualBraced -> open_tag endifnotequal_keyword close_tag.
219 |
220 | AutoEscapeBlock -> AutoEscapeBraced Elements EndAutoEscapeBraced : {autoescape, '$1', '$2'}.
221 | AutoEscapeBraced -> open_tag autoescape_keyword identifier close_tag : '$3'.
222 | EndAutoEscapeBraced -> open_tag endautoescape_keyword close_tag.
223 |
224 | Filter -> identifier : ['$1'].
225 | Filter -> identifier colon Literal : ['$1', '$3'].
226 |
227 | Literal -> string_literal : '$1'.
228 | Literal -> number_literal : '$1'.
229 |
230 | CustomTag -> open_tag identifier Args close_tag : {tag, '$2', '$3'}.
231 |
232 | Args -> '$empty' : [].
233 | Args -> Args identifier equal Value : '$1' ++ [{'$2', '$4'}].
234 |
235 | CallTag -> open_tag call_keyword identifier close_tag : {call, '$3'}.
236 | CallWithTag -> open_tag call_keyword identifier with_keyword Value close_tag : {call, '$3', '$5'}.
237 |
--------------------------------------------------------------------------------
/deps/erlydtl/src/erlydtl_runtime.erl:
--------------------------------------------------------------------------------
1 | -module(erlydtl_runtime).
2 |
3 | -compile(export_all).
4 |
5 | find_value(Key, L) when is_list(L) ->
6 | proplists:get_value(Key, L);
7 | find_value(Key, {GBSize, GBData}) when is_integer(GBSize) ->
8 | case gb_trees:lookup(Key, {GBSize, GBData}) of
9 | {value, Val} ->
10 | Val;
11 | _ ->
12 | undefined
13 | end;
14 | find_value(Key, Tuple) when is_tuple(Tuple) ->
15 | Module = element(1, Tuple),
16 | case Module of
17 | dict ->
18 | case dict:find(Key, Tuple) of
19 | {ok, Val} ->
20 | Val;
21 | _ ->
22 | undefined
23 | end;
24 | Module ->
25 | case proplists:get_value(Key, Module:module_info(exports)) of
26 | 1 ->
27 | Tuple:Key();
28 | _ ->
29 | undefined
30 | end
31 | end.
32 |
33 | fetch_value(Key, Data) ->
34 | case find_value(Key, Data) of
35 | undefined ->
36 | throw({undefined_variable, Key});
37 | Val ->
38 | Val
39 | end.
40 |
41 | are_equal(Arg1, Arg2) when Arg1 =:= Arg2 ->
42 | true;
43 | are_equal(Arg1, Arg2) when is_binary(Arg1) ->
44 | are_equal(binary_to_list(Arg1), Arg2);
45 | are_equal(Arg1, Arg2) when is_binary(Arg2) ->
46 | are_equal(Arg1, binary_to_list(Arg2));
47 | are_equal(Arg1, Arg2) when is_integer(Arg1) ->
48 | are_equal(integer_to_list(Arg1), Arg2);
49 | are_equal(Arg1, Arg2) when is_integer(Arg2) ->
50 | are_equal(Arg1, integer_to_list(Arg2));
51 | are_equal([Arg1], Arg2) when is_list(Arg1) ->
52 | are_equal(Arg1, Arg2);
53 | are_equal(Arg1, [Arg2]) when is_list(Arg1) ->
54 | are_equal(Arg1, Arg2);
55 | are_equal(_, _) ->
56 | false.
57 |
58 | is_false("") ->
59 | true;
60 | is_false(false) ->
61 | true;
62 | is_false(undefined) ->
63 | true;
64 | is_false("0") ->
65 | true;
66 | is_false(<<"0">>) ->
67 | true;
68 | is_false(<<>>) ->
69 | true;
70 | is_false(_) ->
71 | false.
72 |
73 | stringify_final(In) ->
74 | stringify_final(In, []).
75 | stringify_final([], Out) ->
76 | lists:reverse(Out);
77 | stringify_final([El | Rest], Out) when is_atom(El) ->
78 | stringify_final(Rest, [atom_to_list(El) | Out]);
79 | stringify_final([El | Rest], Out) ->
80 | stringify_final(Rest, [El | Out]).
81 |
82 | init_counter_stats(List) ->
83 | init_counter_stats(List, undefined).
84 |
85 | init_counter_stats(List, Parent) ->
86 | [{counter, 1},
87 | {counter0, 0},
88 | {revcounter, length(List)},
89 | {revcounter0, length(List) - 1},
90 | {first, true},
91 | {last, length(List) =:= 1},
92 | {parentloop, Parent}].
93 |
94 | increment_counter_stats([{counter, Counter}, {counter0, Counter0}, {revcounter, RevCounter},
95 | {revcounter0, RevCounter0}, {first, _}, {last, _}, {parentloop, Parent}]) ->
96 | [{counter, Counter + 1},
97 | {counter0, Counter0 + 1},
98 | {revcounter, RevCounter - 1},
99 | {revcounter0, RevCounter0 - 1},
100 | {first, false}, {last, RevCounter0 =:= 1},
101 | {parentloop, Parent}].
102 |
103 | cycle(NamesTuple, Counters) when is_tuple(NamesTuple) ->
104 | element(fetch_value(counter0, Counters) rem size(NamesTuple) + 1, NamesTuple).
105 |
--------------------------------------------------------------------------------
/deps/erlydtl/src/erlydtl_scanner.erl:
--------------------------------------------------------------------------------
1 | %%%-------------------------------------------------------------------
2 | %%% File: erlydtl_scanner.erl
3 | %%% @author Roberto Saccon [http://rsaccon.com]
4 | %%% @author Evan Miller
5 | %%% @copyright 2008 Roberto Saccon, Evan Miller
6 | %%% @doc
7 | %%% Template language scanner
8 | %%% @end
9 | %%%
10 | %%% The MIT License
11 | %%%
12 | %%% Copyright (c) 2007 Roberto Saccon, Evan Miller
13 | %%%
14 | %%% Permission is hereby granted, free of charge, to any person obtaining a copy
15 | %%% of this software and associated documentation files (the "Software"), to deal
16 | %%% in the Software without restriction, including without limitation the rights
17 | %%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18 | %%% copies of the Software, and to permit persons to whom the Software is
19 | %%% furnished to do so, subject to the following conditions:
20 | %%%
21 | %%% The above copyright notice and this permission notice shall be included in
22 | %%% all copies or substantial portions of the Software.
23 | %%%
24 | %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 | %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 | %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 | %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 | %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29 | %%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30 | %%% THE SOFTWARE.
31 | %%%
32 | %%% @since 2007-11-11 by Roberto Saccon, Evan Miller
33 | %%%-------------------------------------------------------------------
34 | -module(erlydtl_scanner).
35 | -author('rsaccon@gmail.com').
36 | -author('emmiller@gmail.com').
37 |
38 | -export([scan/1]).
39 |
40 |
41 | %%====================================================================
42 | %% API
43 | %%====================================================================
44 | %%--------------------------------------------------------------------
45 | %% @spec scan(T::template()) -> {ok, S::tokens()} | {error, Reason}
46 | %% @type template() = string() | binary(). Template to parse
47 | %% @type tokens() = [tuple()].
48 | %% @doc Scan the template string T and return the a token list or
49 | %% an error.
50 | %% @end
51 | %%--------------------------------------------------------------------
52 | scan(Template) ->
53 | scan(Template, [], {1, 1}, in_text).
54 |
55 | scan([], Scanned, _, in_text) ->
56 | {ok, lists:reverse(lists:map(
57 | fun
58 | ({identifier, Pos, String}) ->
59 | RevString = lists:reverse(String),
60 | Keywords = ["for", "endfor", "in", "include", "block", "endblock",
61 | "extends", "autoescape", "endautoescape", "if", "else", "endif",
62 | "not", "or", "and", "comment", "endcomment", "cycle", "firstof",
63 | "ifchanged", "ifequal", "endifequal", "ifnotequal", "endifnotequal",
64 | "now", "regroup", "spaceless", "endspaceless", "ssi", "templatetag",
65 | "load", "call", "with"],
66 | Type = case lists:member(RevString, Keywords) of
67 | true ->
68 | list_to_atom(RevString ++ "_keyword");
69 | _ ->
70 | identifier
71 | end,
72 | {Type, Pos, RevString};
73 | ({Type, Pos, String}) ->
74 | {Type, Pos, lists:reverse(String)}
75 | end, Scanned))};
76 |
77 | scan([], _Scanned, _, {in_comment, _}) ->
78 | {error, "Reached end of file inside a comment."};
79 |
80 | scan([], _Scanned, _, _) ->
81 | {error, "Reached end of file inside a code block."};
82 |
83 | scan(""});
85 |
86 | scan("{{" ++ T, Scanned, {Row, Column}, in_text) ->
87 | scan(T, [{open_var, {Row, Column}, "{{"} | Scanned], {Row, Column + 2}, {in_code, "}}"});
88 |
89 | scan(""});
91 |
92 | scan("{#" ++ T, Scanned, {Row, Column}, in_text) ->
93 | scan(T, Scanned, {Row, Column + 2}, {in_comment, "#}"});
94 |
95 | scan("#}-->" ++ T, Scanned, {Row, Column}, {in_comment, "#}-->"}) ->
96 | scan(T, Scanned, {Row, Column + length("#}-->")}, in_text);
97 |
98 | scan("#}" ++ T, Scanned, {Row, Column}, {in_comment, "#}"}) ->
99 | scan(T, Scanned, {Row, Column + 2}, in_text);
100 |
101 | scan(""});
104 |
105 | scan("{%" ++ T, Scanned, {Row, Column}, in_text) ->
106 | scan(T, [{open_tag, {Row, Column}, lists:reverse("{%")} | Scanned],
107 | {Row, Column + 2}, {in_code, "%}"});
108 |
109 | scan([_ | T], Scanned, {Row, Column}, {in_comment, Closer}) ->
110 | scan(T, Scanned, {Row, Column + 1}, {in_comment, Closer});
111 |
112 | scan("\n" ++ T, Scanned, {Row, Column}, in_text) ->
113 | scan(T, append_text_char(Scanned, {Row, Column}, $\n), {Row + 1, 1}, in_text);
114 |
115 | scan([H | T], Scanned, {Row, Column}, in_text) ->
116 | scan(T, append_text_char(Scanned, {Row, Column}, H), {Row, Column + 1}, in_text);
117 |
118 | scan("\"" ++ T, Scanned, {Row, Column}, {in_code, Closer}) ->
119 | scan(T, [{string_literal, {Row, Column}, "\""} | Scanned], {Row, Column + 1}, {in_double_quote, Closer});
120 |
121 | scan("\"" ++ T, Scanned, {Row, Column}, {in_identifier, Closer}) ->
122 | scan(T, [{string_literal, {Row, Column}, "\""} | Scanned], {Row, Column + 1}, {in_double_quote, Closer});
123 |
124 | scan("\'" ++ T, Scanned, {Row, Column}, {in_code, Closer}) ->
125 | scan(T, [{string_literal, {Row, Column}, "\""} | Scanned], {Row, Column + 1}, {in_single_quote, Closer});
126 |
127 | scan("\'" ++ T, Scanned, {Row, Column}, {in_identifier, Closer}) ->
128 | scan(T, [{string_literal, {Row, Column}, "\""} | Scanned], {Row, Column + 1}, {in_single_quote, Closer});
129 |
130 | scan([$\\ | T], Scanned, {Row, Column}, {in_double_quote, Closer}) ->
131 | scan(T, append_char(Scanned, $\\), {Row, Column + 1}, {in_double_quote_slash, Closer});
132 |
133 | scan([H | T], Scanned, {Row, Column}, {in_double_quote_slash, Closer}) ->
134 | scan(T, append_char(Scanned, H), {Row, Column + 1}, {in_double_quote, Closer});
135 |
136 | scan([$\\ | T], Scanned, {Row, Column}, {in_single_quote, Closer}) ->
137 | scan(T, append_char(Scanned, $\\), {Row, Column + 1}, {in_single_quote_slash, Closer});
138 |
139 | scan([H | T], Scanned, {Row, Column}, {in_single_quote_slash, Closer}) ->
140 | scan(T, append_char(Scanned, H), {Row, Column + 1}, {in_single_quote, Closer});
141 |
142 | % end quote
143 | scan("\"" ++ T, Scanned, {Row, Column}, {in_double_quote, Closer}) ->
144 | scan(T, append_char(Scanned, 34), {Row, Column + 1}, {in_code, Closer});
145 |
146 | % treat single quotes the same as double quotes
147 | scan("\'" ++ T, Scanned, {Row, Column}, {in_single_quote, Closer}) ->
148 | scan(T, append_char(Scanned, 34), {Row, Column + 1}, {in_code, Closer});
149 |
150 | scan([H | T], Scanned, {Row, Column}, {in_double_quote, Closer}) ->
151 | scan(T, append_char(Scanned, H), {Row, Column + 1}, {in_double_quote, Closer});
152 |
153 | scan([H | T], Scanned, {Row, Column}, {in_single_quote, Closer}) ->
154 | scan(T, append_char(Scanned, H), {Row, Column + 1}, {in_single_quote, Closer});
155 |
156 |
157 | scan("," ++ T, Scanned, {Row, Column}, {_, Closer}) ->
158 | scan(T, [{comma, {Row, Column}, ","} | Scanned], {Row, Column + 1}, {in_code, Closer});
159 |
160 | scan("|" ++ T, Scanned, {Row, Column}, {_, Closer}) ->
161 | scan(T, [{pipe, {Row, Column}, "|"} | Scanned], {Row, Column + 1}, {in_code, Closer});
162 |
163 | scan("=" ++ T, Scanned, {Row, Column}, {_, Closer}) ->
164 | scan(T, [{equal, {Row, Column}, "="} | Scanned], {Row, Column + 1}, {in_code, Closer});
165 |
166 | scan(":" ++ T, Scanned, {Row, Column}, {_, Closer}) ->
167 | scan(T, [{colon, {Row, Column}, ":"} | Scanned], {Row, Column + 1}, {in_code, Closer});
168 |
169 | scan("." ++ T, Scanned, {Row, Column}, {_, Closer}) ->
170 | scan(T, [{dot, {Row, Column}, "."} | Scanned], {Row, Column + 1}, {in_code, Closer});
171 |
172 | scan(" " ++ T, Scanned, {Row, Column}, {_, Closer}) ->
173 | scan(T, Scanned, {Row, Column + 1}, {in_code, Closer});
174 |
175 | scan("}}-->" ++ T, Scanned, {Row, Column}, {in_code, "}}-->"}) ->
176 | scan(T, [{close_var, {Row, Column}, lists:reverse("}}-->")} | Scanned],
177 | {Row, Column + 2}, in_text);
178 |
179 | scan("}}" ++ T, Scanned, {Row, Column}, {in_code, "}}"}) ->
180 | scan(T, [{close_var, {Row, Column}, "}}"} | Scanned], {Row, Column + 2}, in_text);
181 |
182 | scan("%}-->" ++ T, Scanned, {Row, Column}, {in_code, "%}-->"}) ->
183 | scan(T, [{close_tag, {Row, Column}, lists:reverse("%}-->")} | Scanned],
184 | {Row, Column + 2}, in_text);
185 |
186 | scan("%}" ++ T, Scanned, {Row, Column}, {in_code, "%}"}) ->
187 | scan(T, [{close_tag, {Row, Column}, lists:reverse("%}")} | Scanned],
188 | {Row, Column + 2}, in_text);
189 |
190 | scan([H | T], Scanned, {Row, Column}, {in_code, Closer}) ->
191 | case char_type(H) of
192 | letter_underscore ->
193 | scan(T, [{identifier, {Row, Column}, [H]} | Scanned], {Row, Column + 1}, {in_identifier, Closer});
194 | digit ->
195 | scan(T, [{number_literal, {Row, Column}, [H]} | Scanned], {Row, Column + 1}, {in_number, Closer});
196 | _ ->
197 | {error, io_lib:format("Illegal character line ~p column ~p", [Row, Column])}
198 | end;
199 |
200 | scan([H | T], Scanned, {Row, Column}, {in_number, Closer}) ->
201 | case char_type(H) of
202 | digit ->
203 | scan(T, append_char(Scanned, H), {Row, Column + 1}, {in_number, Closer});
204 | _ ->
205 | {error, io_lib:format("Illegal character line ~p column ~p", [Row, Column])}
206 | end;
207 |
208 | scan([H | T], Scanned, {Row, Column}, {in_identifier, Closer}) ->
209 | case char_type(H) of
210 | letter_underscore ->
211 | scan(T, append_char(Scanned, H), {Row, Column + 1}, {in_identifier, Closer});
212 | digit ->
213 | scan(T, append_char(Scanned, H), {Row, Column + 1}, {in_identifier, Closer});
214 | _ ->
215 | {error, io_lib:format("Illegal character line ~p column ~p", [Row, Column])}
216 | end.
217 |
218 | % internal functions
219 |
220 | append_char(Scanned, Char) ->
221 | [String | Scanned1] = Scanned,
222 | [setelement(3, String, [Char | element(3, String)]) | Scanned1].
223 |
224 | append_text_char(Scanned, {Row, Column}, Char) ->
225 | case length(Scanned) of
226 | 0 ->
227 | [{text, {Row, Column}, [Char]}];
228 | _ ->
229 | [Token | Scanned1] = Scanned,
230 | case element(1, Token) of
231 | text ->
232 | [{text, element(2, Token), [Char | element(3, Token)]} | Scanned1];
233 | _ ->
234 | [{text, element(2, Token), [Char]} | Scanned]
235 | end
236 | end.
237 |
238 | char_type(Char) ->
239 | case Char of
240 | C when ((C >= $a) and (C =< $z)) or ((C >= $A) and (C =< $Z)) or (C == $_) ->
241 | letter_underscore;
242 | C when ((C >= $0) and (C =< $9)) ->
243 | digit;
244 | _ ->
245 | undefined
246 | end.
247 |
--------------------------------------------------------------------------------
/deps/mochiweb/LICENSE:
--------------------------------------------------------------------------------
1 | This is the MIT license.
2 |
3 | Copyright (c) 2007 Mochi Media, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/deps/mochiweb/README:
--------------------------------------------------------------------------------
1 | MochiWeb is an Erlang library for building lightweight HTTP servers.
2 |
--------------------------------------------------------------------------------
/deps/mochiweb/ebin/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davebryson/beepbeep/62db46d268c6cb6ad86345562b3c77f8ff070b27/deps/mochiweb/ebin/.gitignore
--------------------------------------------------------------------------------
/deps/mochiweb/src/Makefile:
--------------------------------------------------------------------------------
1 | include ../../../support/include.mk
2 |
3 | all: $(EBIN_FILES_NO_DOCS)
4 |
5 | debug:
6 | $(MAKE) DEBUG=-DDEBUG
7 |
8 | clean:
9 | rm -rf $(EBIN_FILES_NO_DOCS)
10 |
11 |
--------------------------------------------------------------------------------
/deps/mochiweb/src/mochifmt_records.erl:
--------------------------------------------------------------------------------
1 | %% @author Bob Ippolito
2 | %% @copyright 2008 Mochi Media, Inc.
3 |
4 | %% @doc Formatter that understands records.
5 | %%
6 | %% Usage:
7 | %%
8 | %% 1> M = mochifmt_records:new([{rec, record_info(fields, rec)}]),
9 | %% M:format("{0.bar}", [#rec{bar=foo}]).
10 | %% foo
11 |
12 | -module(mochifmt_records, [Recs]).
13 | -author('bob@mochimedia.com').
14 | -export([get_value/2]).
15 |
16 | get_value(Key, Rec) when is_tuple(Rec) and is_atom(element(1, Rec)) ->
17 | try begin
18 | Atom = list_to_existing_atom(Key),
19 | {_, Fields} = proplists:lookup(element(1, Rec), Recs),
20 | element(get_rec_index(Atom, Fields, 2), Rec)
21 | end
22 | catch error:_ -> mochifmt:get_value(Key, Rec)
23 | end;
24 | get_value(Key, Args) ->
25 | mochifmt:get_value(Key, Args).
26 |
27 | get_rec_index(Atom, [Atom | _], Index) ->
28 | Index;
29 | get_rec_index(Atom, [_ | Rest], Index) ->
30 | get_rec_index(Atom, Rest, 1 + Index).
31 |
--------------------------------------------------------------------------------
/deps/mochiweb/src/mochifmt_std.erl:
--------------------------------------------------------------------------------
1 | %% @author Bob Ippolito
2 | %% @copyright 2008 Mochi Media, Inc.
3 |
4 | %% @doc Template module for a mochifmt formatter.
5 |
6 | -module(mochifmt_std, []).
7 | -author('bob@mochimedia.com').
8 | -export([format/2, get_value/2, format_field/2, get_field/2, convert_field/2]).
9 |
10 | format(Format, Args) ->
11 | mochifmt:format(Format, Args, THIS).
12 |
13 | get_field(Key, Args) ->
14 | mochifmt:get_field(Key, Args, THIS).
15 |
16 | convert_field(Key, Args) ->
17 | mochifmt:convert_field(Key, Args).
18 |
19 | get_value(Key, Args) ->
20 | mochifmt:get_value(Key, Args).
21 |
22 | format_field(Arg, Format) ->
23 | mochifmt:format_field(Arg, Format, THIS).
24 |
--------------------------------------------------------------------------------
/deps/mochiweb/src/mochihex.erl:
--------------------------------------------------------------------------------
1 | %% @author Bob Ippolito
2 | %% @copyright 2006 Mochi Media, Inc.
3 |
4 | %% @doc Utilities for working with hexadecimal strings.
5 |
6 | -module(mochihex).
7 | -author('bob@mochimedia.com').
8 |
9 | -export([test/0, to_hex/1, to_bin/1, to_int/1, dehex/1, hexdigit/1]).
10 |
11 | %% @type iolist() = [char() | binary() | iolist()]
12 | %% @type iodata() = iolist() | binary()
13 |
14 | %% @spec to_hex(integer | iolist()) -> string()
15 | %% @doc Convert an iolist to a hexadecimal string.
16 | to_hex(0) ->
17 | "0";
18 | to_hex(I) when is_integer(I), I > 0 ->
19 | to_hex_int(I, []);
20 | to_hex(B) ->
21 | to_hex(iolist_to_binary(B), []).
22 |
23 | %% @spec to_bin(string()) -> binary()
24 | %% @doc Convert a hexadecimal string to a binary.
25 | to_bin(L) ->
26 | to_bin(L, []).
27 |
28 | %% @spec to_int(string()) -> integer()
29 | %% @doc Convert a hexadecimal string to an integer.
30 | to_int(L) ->
31 | erlang:list_to_integer(L, 16).
32 |
33 | %% @spec dehex(char()) -> integer()
34 | %% @doc Convert a hex digit to its integer value.
35 | dehex(C) when C >= $0, C =< $9 ->
36 | C - $0;
37 | dehex(C) when C >= $a, C =< $f ->
38 | C - $a + 10;
39 | dehex(C) when C >= $A, C =< $F ->
40 | C - $A + 10.
41 |
42 | %% @spec hexdigit(integer()) -> char()
43 | %% @doc Convert an integer less than 16 to a hex digit.
44 | hexdigit(C) when C >= 0, C =< 9 ->
45 | C + $0;
46 | hexdigit(C) when C =< 15 ->
47 | C + $a - 10.
48 |
49 | %% @spec test() -> ok
50 | %% @doc Test this module.
51 | test() ->
52 | "ff000ff1" = to_hex([255, 0, 15, 241]),
53 | <<255, 0, 15, 241>> = to_bin("ff000ff1"),
54 | 16#ff000ff1 = to_int("ff000ff1"),
55 | "ff000ff1" = to_hex(16#ff000ff1),
56 | ok.
57 |
58 |
59 | %% Internal API
60 |
61 | to_hex(<<>>, Acc) ->
62 | lists:reverse(Acc);
63 | to_hex(<>, Acc) ->
64 | to_hex(Rest, [hexdigit(C2), hexdigit(C1) | Acc]).
65 |
66 | to_hex_int(0, Acc) ->
67 | Acc;
68 | to_hex_int(I, Acc) ->
69 | to_hex_int(I bsr 4, [hexdigit(I band 15) | Acc]).
70 |
71 | to_bin([], Acc) ->
72 | iolist_to_binary(lists:reverse(Acc));
73 | to_bin([C1, C2 | Rest], Acc) ->
74 | to_bin(Rest, [(dehex(C1) bsl 4) bor dehex(C2) | Acc]).
75 |
76 |
--------------------------------------------------------------------------------
/deps/mochiweb/src/mochinum.erl:
--------------------------------------------------------------------------------
1 | %% @copyright 2007 Mochi Media, Inc.
2 | %% @author Bob Ippolito
3 |
4 | %% @doc Useful numeric algorithms for floats that cover some deficiencies
5 | %% in the math module. More interesting is digits/1, which implements
6 | %% the algorithm from:
7 | %% http://www.cs.indiana.edu/~burger/fp/index.html
8 | %% See also "Printing Floating-Point Numbers Quickly and Accurately"
9 | %% in Proceedings of the SIGPLAN '96 Conference on Programming Language
10 | %% Design and Implementation.
11 |
12 | -module(mochinum).
13 | -author("Bob Ippolito ").
14 | -export([digits/1, frexp/1, int_pow/2, int_ceil/1, test/0]).
15 |
16 | %% IEEE 754 Float exponent bias
17 | -define(FLOAT_BIAS, 1022).
18 | -define(MIN_EXP, -1074).
19 | -define(BIG_POW, 4503599627370496).
20 |
21 | %% External API
22 |
23 | %% @spec digits(number()) -> string()
24 | %% @doc Returns a string that accurately represents the given integer or float
25 | %% using a conservative amount of digits. Great for generating
26 | %% human-readable output, or compact ASCII serializations for floats.
27 | digits(N) when is_integer(N) ->
28 | integer_to_list(N);
29 | digits(0.0) ->
30 | "0.0";
31 | digits(Float) ->
32 | {Frac, Exp} = frexp(Float),
33 | Exp1 = Exp - 53,
34 | Frac1 = trunc(abs(Frac) * (1 bsl 53)),
35 | [Place | Digits] = digits1(Float, Exp1, Frac1),
36 | R = insert_decimal(Place, [$0 + D || D <- Digits]),
37 | case Float < 0 of
38 | true ->
39 | [$- | R];
40 | _ ->
41 | R
42 | end.
43 |
44 | %% @spec frexp(F::float()) -> {Frac::float(), Exp::float()}
45 | %% @doc Return the fractional and exponent part of an IEEE 754 double,
46 | %% equivalent to the libc function of the same name.
47 | %% F = Frac * pow(2, Exp).
48 | frexp(F) ->
49 | frexp1(unpack(F)).
50 |
51 | %% @spec int_pow(X::integer(), N::integer()) -> Y::integer()
52 | %% @doc Moderately efficient way to exponentiate integers.
53 | %% int_pow(10, 2) = 100.
54 | int_pow(_X, 0) ->
55 | 1;
56 | int_pow(X, N) when N > 0 ->
57 | int_pow(X, N, 1).
58 |
59 | %% @spec int_ceil(F::float()) -> integer()
60 | %% @doc Return the ceiling of F as an integer. The ceiling is defined as
61 | %% F when F == trunc(F);
62 | %% trunc(F) when F < 0;
63 | %% trunc(F) + 1 when F > 0.
64 | int_ceil(X) ->
65 | T = trunc(X),
66 | case (X - T) of
67 | Neg when Neg < 0 -> T;
68 | Pos when Pos > 0 -> T + 1;
69 | _ -> T
70 | end.
71 |
72 |
73 | %% Internal API
74 |
75 | int_pow(X, N, R) when N < 2 ->
76 | R * X;
77 | int_pow(X, N, R) ->
78 | int_pow(X * X, N bsr 1, case N band 1 of 1 -> R * X; 0 -> R end).
79 |
80 | insert_decimal(0, S) ->
81 | "0." ++ S;
82 | insert_decimal(Place, S) when Place > 0 ->
83 | L = length(S),
84 | case Place - L of
85 | 0 ->
86 | S ++ ".0";
87 | N when N < 0 ->
88 | {S0, S1} = lists:split(L + N, S),
89 | S0 ++ "." ++ S1;
90 | N when N < 6 ->
91 | %% More places than digits
92 | S ++ lists:duplicate(N, $0) ++ ".0";
93 | _ ->
94 | insert_decimal_exp(Place, S)
95 | end;
96 | insert_decimal(Place, S) when Place > -6 ->
97 | "0." ++ lists:duplicate(abs(Place), $0) ++ S;
98 | insert_decimal(Place, S) ->
99 | insert_decimal_exp(Place, S).
100 |
101 | insert_decimal_exp(Place, S) ->
102 | [C | S0] = S,
103 | S1 = case S0 of
104 | [] ->
105 | "0";
106 | _ ->
107 | S0
108 | end,
109 | Exp = case Place < 0 of
110 | true ->
111 | "e-";
112 | false ->
113 | "e+"
114 | end,
115 | [C] ++ "." ++ S1 ++ Exp ++ integer_to_list(abs(Place - 1)).
116 |
117 |
118 | digits1(Float, Exp, Frac) ->
119 | Round = ((Frac band 1) =:= 0),
120 | case Exp >= 0 of
121 | true ->
122 | BExp = 1 bsl Exp,
123 | case (Frac /= ?BIG_POW) of
124 | true ->
125 | scale((Frac * BExp * 2), 2, BExp, BExp,
126 | Round, Round, Float);
127 | false ->
128 | scale((Frac * BExp * 4), 4, (BExp * 2), BExp,
129 | Round, Round, Float)
130 | end;
131 | false ->
132 | case (Exp == ?MIN_EXP) orelse (Frac /= ?BIG_POW) of
133 | true ->
134 | scale((Frac * 2), 1 bsl (1 - Exp), 1, 1,
135 | Round, Round, Float);
136 | false ->
137 | scale((Frac * 4), 1 bsl (2 - Exp), 2, 1,
138 | Round, Round, Float)
139 | end
140 | end.
141 |
142 | scale(R, S, MPlus, MMinus, LowOk, HighOk, Float) ->
143 | Est = int_ceil(math:log10(abs(Float)) - 1.0e-10),
144 | %% Note that the scheme implementation uses a 326 element look-up table
145 | %% for int_pow(10, N) where we do not.
146 | case Est >= 0 of
147 | true ->
148 | fixup(R, S * int_pow(10, Est), MPlus, MMinus, Est,
149 | LowOk, HighOk);
150 | false ->
151 | Scale = int_pow(10, -Est),
152 | fixup(R * Scale, S, MPlus * Scale, MMinus * Scale, Est,
153 | LowOk, HighOk)
154 | end.
155 |
156 | fixup(R, S, MPlus, MMinus, K, LowOk, HighOk) ->
157 | TooLow = case HighOk of
158 | true ->
159 | (R + MPlus) >= S;
160 | false ->
161 | (R + MPlus) > S
162 | end,
163 | case TooLow of
164 | true ->
165 | [(K + 1) | generate(R, S, MPlus, MMinus, LowOk, HighOk)];
166 | false ->
167 | [K | generate(R * 10, S, MPlus * 10, MMinus * 10, LowOk, HighOk)]
168 | end.
169 |
170 | generate(R0, S, MPlus, MMinus, LowOk, HighOk) ->
171 | D = R0 div S,
172 | R = R0 rem S,
173 | TC1 = case LowOk of
174 | true ->
175 | R =< MMinus;
176 | false ->
177 | R < MMinus
178 | end,
179 | TC2 = case HighOk of
180 | true ->
181 | (R + MPlus) >= S;
182 | false ->
183 | (R + MPlus) > S
184 | end,
185 | case TC1 of
186 | false ->
187 | case TC2 of
188 | false ->
189 | [D | generate(R * 10, S, MPlus * 10, MMinus * 10,
190 | LowOk, HighOk)];
191 | true ->
192 | [D + 1]
193 | end;
194 | true ->
195 | case TC2 of
196 | false ->
197 | [D];
198 | true ->
199 | case R * 2 < S of
200 | true ->
201 | [D];
202 | false ->
203 | [D + 1]
204 | end
205 | end
206 | end.
207 |
208 | unpack(Float) ->
209 | <> = <>,
210 | {Sign, Exp, Frac}.
211 |
212 | frexp1({_Sign, 0, 0}) ->
213 | {0.0, 0};
214 | frexp1({Sign, 0, Frac}) ->
215 | Exp = log2floor(Frac),
216 | <> = <>,
217 | {Frac1, -(?FLOAT_BIAS) - 52 + Exp};
218 | frexp1({Sign, Exp, Frac}) ->
219 | <> = <>,
220 | {Frac1, Exp - ?FLOAT_BIAS}.
221 |
222 | log2floor(Int) ->
223 | log2floor(Int, 0).
224 |
225 | log2floor(0, N) ->
226 | N;
227 | log2floor(Int, N) ->
228 | log2floor(Int bsr 1, 1 + N).
229 |
230 |
231 | test() ->
232 | ok = test_frexp(),
233 | ok = test_int_ceil(),
234 | ok = test_int_pow(),
235 | ok = test_digits(),
236 | ok.
237 |
238 | test_int_ceil() ->
239 | 1 = int_ceil(0.0001),
240 | 0 = int_ceil(0.0),
241 | 1 = int_ceil(0.99),
242 | 1 = int_ceil(1.0),
243 | -1 = int_ceil(-1.5),
244 | -2 = int_ceil(-2.0),
245 | ok.
246 |
247 | test_int_pow() ->
248 | 1 = int_pow(1, 1),
249 | 1 = int_pow(1, 0),
250 | 1 = int_pow(10, 0),
251 | 10 = int_pow(10, 1),
252 | 100 = int_pow(10, 2),
253 | 1000 = int_pow(10, 3),
254 | ok.
255 |
256 | test_digits() ->
257 | "0" = digits(0),
258 | "0.0" = digits(0.0),
259 | "1.0" = digits(1.0),
260 | "-1.0" = digits(-1.0),
261 | "0.1" = digits(0.1),
262 | "0.01" = digits(0.01),
263 | "0.001" = digits(0.001),
264 | ok.
265 |
266 | test_frexp() ->
267 | %% zero
268 | {0.0, 0} = frexp(0.0),
269 | %% one
270 | {0.5, 1} = frexp(1.0),
271 | %% negative one
272 | {-0.5, 1} = frexp(-1.0),
273 | %% small denormalized number
274 | %% 4.94065645841246544177e-324
275 | <> = <<0,0,0,0,0,0,0,1>>,
276 | {0.5, -1073} = frexp(SmallDenorm),
277 | %% large denormalized number
278 | %% 2.22507385850720088902e-308
279 | <> = <<0,15,255,255,255,255,255,255>>,
280 | {0.99999999999999978, -1022} = frexp(BigDenorm),
281 | %% small normalized number
282 | %% 2.22507385850720138309e-308
283 | <> = <<0,16,0,0,0,0,0,0>>,
284 | {0.5, -1021} = frexp(SmallNorm),
285 | %% large normalized number
286 | %% 1.79769313486231570815e+308
287 | <> = <<127,239,255,255,255,255,255,255>>,
288 | {0.99999999999999989, 1024} = frexp(LargeNorm),
289 | ok.
290 |
--------------------------------------------------------------------------------
/deps/mochiweb/src/mochiweb.app:
--------------------------------------------------------------------------------
1 | {application, mochiweb,
2 | [{description, "MochiMedia Web Server"},
3 | {vsn, "0.01"},
4 | {modules, [
5 | mochihex,
6 | mochijson,
7 | mochijson2,
8 | mochinum,
9 | mochiweb,
10 | mochiweb_app,
11 | mochiweb_charref,
12 | mochiweb_cookies,
13 | mochiweb_echo,
14 | mochiweb_headers,
15 | mochiweb_html,
16 | mochiweb_http,
17 | mochiweb_multipart,
18 | mochiweb_request,
19 | mochiweb_response,
20 | mochiweb_skel,
21 | mochiweb_socket_server,
22 | mochiweb_sup,
23 | mochiweb_util,
24 | reloader
25 | ]},
26 | {registered, []},
27 | {mod, {mochiweb_app, []}},
28 | {env, []},
29 | {applications, [kernel, stdlib]}]}.
30 |
--------------------------------------------------------------------------------
/deps/mochiweb/src/mochiweb.erl:
--------------------------------------------------------------------------------
1 | %% @author Bob Ippolito
2 | %% @copyright 2007 Mochi Media, Inc.
3 |
4 | %% @doc Start and stop the MochiWeb server.
5 |
6 | -module(mochiweb).
7 | -author('bob@mochimedia.com').
8 |
9 | -export([start/0, stop/0]).
10 | -export([new_request/1, new_response/1]).
11 | -export([all_loaded/0, all_loaded/1, reload/0]).
12 | -export([test/0]).
13 |
14 | %% @spec start() -> ok
15 | %% @doc Start the MochiWeb server.
16 | start() ->
17 | ensure_started(crypto),
18 | application:start(mochiweb).
19 |
20 | %% @spec stop() -> ok
21 | %% @doc Stop the MochiWeb server.
22 | stop() ->
23 | Res = application:stop(mochiweb),
24 | application:stop(crypto),
25 | Res.
26 |
27 | %% @spec test() -> ok
28 | %% @doc Run all of the tests for MochiWeb.
29 | test() ->
30 | mochiweb_util:test(),
31 | mochiweb_headers:test(),
32 | mochiweb_cookies:test(),
33 | mochihex:test(),
34 | mochinum:test(),
35 | mochijson:test(),
36 | mochiweb_charref:test(),
37 | mochiweb_html:test(),
38 | mochifmt:test(),
39 | test_request(),
40 | ok.
41 |
42 | reload() ->
43 | [c:l(Module) || Module <- all_loaded()].
44 |
45 | all_loaded() ->
46 | all_loaded(filename:dirname(code:which(?MODULE))).
47 |
48 | all_loaded(Base) when is_atom(Base) ->
49 | [];
50 | all_loaded(Base) ->
51 | FullBase = Base ++ "/",
52 | F = fun ({_Module, Loaded}, Acc) when is_atom(Loaded) ->
53 | Acc;
54 | ({Module, Loaded}, Acc) ->
55 | case lists:prefix(FullBase, Loaded) of
56 | true ->
57 | [Module | Acc];
58 | false ->
59 | Acc
60 | end
61 | end,
62 | lists:foldl(F, [], code:all_loaded()).
63 |
64 |
65 | %% @spec new_request({Socket, Request, Headers}) -> MochiWebRequest
66 | %% @doc Return a mochiweb_request data structure.
67 | new_request({Socket, {Method, {abs_path, Uri}, Version}, Headers}) ->
68 | mochiweb_request:new(Socket,
69 | Method,
70 | Uri,
71 | Version,
72 | mochiweb_headers:make(Headers));
73 | % this case probably doesn't "exist".
74 | new_request({Socket, {Method, {absoluteURI, _Protocol, _Host, _Port, Uri},
75 | Version}, Headers}) ->
76 | mochiweb_request:new(Socket,
77 | Method,
78 | Uri,
79 | Version,
80 | mochiweb_headers:make(Headers));
81 | %% Request-URI is "*"
82 | %% From http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
83 | new_request({Socket, {Method, '*'=Uri, Version}, Headers}) ->
84 | mochiweb_request:new(Socket,
85 | Method,
86 | Uri,
87 | Version,
88 | mochiweb_headers:make(Headers)).
89 |
90 | %% @spec new_response({Request, integer(), Headers}) -> MochiWebResponse
91 | %% @doc Return a mochiweb_response data structure.
92 | new_response({Request, Code, Headers}) ->
93 | mochiweb_response:new(Request,
94 | Code,
95 | mochiweb_headers:make(Headers)).
96 |
97 | %% Internal API
98 |
99 | test_request() ->
100 | R = mochiweb_request:new(z, z, "/foo/bar/baz%20wibble+quux?qs=2", z, []),
101 | "/foo/bar/baz wibble quux" = R:get(path),
102 | ok.
103 |
104 | ensure_started(App) ->
105 | case application:start(App) of
106 | ok ->
107 | ok;
108 | {error, {already_started, App}} ->
109 | ok
110 | end.
111 |
--------------------------------------------------------------------------------
/deps/mochiweb/src/mochiweb_app.erl:
--------------------------------------------------------------------------------
1 | %% @author Bob Ippolito
2 | %% @copyright 2007 Mochi Media, Inc.
3 |
4 | %% @doc Callbacks for the mochiweb application.
5 |
6 | -module(mochiweb_app).
7 | -author('bob@mochimedia.com').
8 |
9 | -behaviour(application).
10 | -export([start/2,stop/1]).
11 |
12 | %% @spec start(_Type, _StartArgs) -> ServerRet
13 | %% @doc application start callback for mochiweb.
14 | start(_Type, _StartArgs) ->
15 | mochiweb_sup:start_link().
16 |
17 | %% @spec stop(_State) -> ServerRet
18 | %% @doc application stop callback for mochiweb.
19 | stop(_State) ->
20 | ok.
21 |
--------------------------------------------------------------------------------
/deps/mochiweb/src/mochiweb_charref.erl:
--------------------------------------------------------------------------------
1 | %% @author Bob Ippolito
2 | %% @copyright 2007 Mochi Media, Inc.
3 |
4 | %% @doc Converts HTML 4 charrefs and entities to codepoints.
5 | -module(mochiweb_charref).
6 | -export([charref/1, test/0]).
7 |
8 | %% External API.
9 |
10 | %% @spec charref(S) -> integer() | undefined
11 | %% @doc Convert a decimal charref, hex charref, or html entity to a unicode
12 | %% codepoint, or return undefined on failure.
13 | %% The input should not include an ampersand or semicolon.
14 | %% charref("#38") = 38, charref("#x26") = 38, charref("amp") = 38.
15 | charref(B) when is_binary(B) ->
16 | charref(binary_to_list(B));
17 | charref([$#, C | L]) when C =:= $x orelse C =:= $X ->
18 | try erlang:list_to_integer(L, 16)
19 | catch
20 | error:badarg -> undefined
21 | end;
22 | charref([$# | L]) ->
23 | try list_to_integer(L)
24 | catch
25 | error:badarg -> undefined
26 | end;
27 | charref(L) ->
28 | entity(L).
29 |
30 | %% @spec test() -> ok
31 | %% @doc Run tests for mochiweb_charref.
32 | test() ->
33 | 1234 = charref("#1234"),
34 | 255 = charref("#xfF"),
35 | 255 = charref("#XFf"),
36 | 38 = charref("amp"),
37 | undefined = charref("not_an_entity"),
38 | ok.
39 |
40 | %% Internal API.
41 |
42 | entity("nbsp") -> 160;
43 | entity("iexcl") -> 161;
44 | entity("cent") -> 162;
45 | entity("pound") -> 163;
46 | entity("curren") -> 164;
47 | entity("yen") -> 165;
48 | entity("brvbar") -> 166;
49 | entity("sect") -> 167;
50 | entity("uml") -> 168;
51 | entity("copy") -> 169;
52 | entity("ordf") -> 170;
53 | entity("laquo") -> 171;
54 | entity("not") -> 172;
55 | entity("shy") -> 173;
56 | entity("reg") -> 174;
57 | entity("macr") -> 175;
58 | entity("deg") -> 176;
59 | entity("plusmn") -> 177;
60 | entity("sup2") -> 178;
61 | entity("sup3") -> 179;
62 | entity("acute") -> 180;
63 | entity("micro") -> 181;
64 | entity("para") -> 182;
65 | entity("middot") -> 183;
66 | entity("cedil") -> 184;
67 | entity("sup1") -> 185;
68 | entity("ordm") -> 186;
69 | entity("raquo") -> 187;
70 | entity("frac14") -> 188;
71 | entity("frac12") -> 189;
72 | entity("frac34") -> 190;
73 | entity("iquest") -> 191;
74 | entity("Agrave") -> 192;
75 | entity("Aacute") -> 193;
76 | entity("Acirc") -> 194;
77 | entity("Atilde") -> 195;
78 | entity("Auml") -> 196;
79 | entity("Aring") -> 197;
80 | entity("AElig") -> 198;
81 | entity("Ccedil") -> 199;
82 | entity("Egrave") -> 200;
83 | entity("Eacute") -> 201;
84 | entity("Ecirc") -> 202;
85 | entity("Euml") -> 203;
86 | entity("Igrave") -> 204;
87 | entity("Iacute") -> 205;
88 | entity("Icirc") -> 206;
89 | entity("Iuml") -> 207;
90 | entity("ETH") -> 208;
91 | entity("Ntilde") -> 209;
92 | entity("Ograve") -> 210;
93 | entity("Oacute") -> 211;
94 | entity("Ocirc") -> 212;
95 | entity("Otilde") -> 213;
96 | entity("Ouml") -> 214;
97 | entity("times") -> 215;
98 | entity("Oslash") -> 216;
99 | entity("Ugrave") -> 217;
100 | entity("Uacute") -> 218;
101 | entity("Ucirc") -> 219;
102 | entity("Uuml") -> 220;
103 | entity("Yacute") -> 221;
104 | entity("THORN") -> 222;
105 | entity("szlig") -> 223;
106 | entity("agrave") -> 224;
107 | entity("aacute") -> 225;
108 | entity("acirc") -> 226;
109 | entity("atilde") -> 227;
110 | entity("auml") -> 228;
111 | entity("aring") -> 229;
112 | entity("aelig") -> 230;
113 | entity("ccedil") -> 231;
114 | entity("egrave") -> 232;
115 | entity("eacute") -> 233;
116 | entity("ecirc") -> 234;
117 | entity("euml") -> 235;
118 | entity("igrave") -> 236;
119 | entity("iacute") -> 237;
120 | entity("icirc") -> 238;
121 | entity("iuml") -> 239;
122 | entity("eth") -> 240;
123 | entity("ntilde") -> 241;
124 | entity("ograve") -> 242;
125 | entity("oacute") -> 243;
126 | entity("ocirc") -> 244;
127 | entity("otilde") -> 245;
128 | entity("ouml") -> 246;
129 | entity("divide") -> 247;
130 | entity("oslash") -> 248;
131 | entity("ugrave") -> 249;
132 | entity("uacute") -> 250;
133 | entity("ucirc") -> 251;
134 | entity("uuml") -> 252;
135 | entity("yacute") -> 253;
136 | entity("thorn") -> 254;
137 | entity("yuml") -> 255;
138 | entity("fnof") -> 402;
139 | entity("Alpha") -> 913;
140 | entity("Beta") -> 914;
141 | entity("Gamma") -> 915;
142 | entity("Delta") -> 916;
143 | entity("Epsilon") -> 917;
144 | entity("Zeta") -> 918;
145 | entity("Eta") -> 919;
146 | entity("Theta") -> 920;
147 | entity("Iota") -> 921;
148 | entity("Kappa") -> 922;
149 | entity("Lambda") -> 923;
150 | entity("Mu") -> 924;
151 | entity("Nu") -> 925;
152 | entity("Xi") -> 926;
153 | entity("Omicron") -> 927;
154 | entity("Pi") -> 928;
155 | entity("Rho") -> 929;
156 | entity("Sigma") -> 931;
157 | entity("Tau") -> 932;
158 | entity("Upsilon") -> 933;
159 | entity("Phi") -> 934;
160 | entity("Chi") -> 935;
161 | entity("Psi") -> 936;
162 | entity("Omega") -> 937;
163 | entity("alpha") -> 945;
164 | entity("beta") -> 946;
165 | entity("gamma") -> 947;
166 | entity("delta") -> 948;
167 | entity("epsilon") -> 949;
168 | entity("zeta") -> 950;
169 | entity("eta") -> 951;
170 | entity("theta") -> 952;
171 | entity("iota") -> 953;
172 | entity("kappa") -> 954;
173 | entity("lambda") -> 955;
174 | entity("mu") -> 956;
175 | entity("nu") -> 957;
176 | entity("xi") -> 958;
177 | entity("omicron") -> 959;
178 | entity("pi") -> 960;
179 | entity("rho") -> 961;
180 | entity("sigmaf") -> 962;
181 | entity("sigma") -> 963;
182 | entity("tau") -> 964;
183 | entity("upsilon") -> 965;
184 | entity("phi") -> 966;
185 | entity("chi") -> 967;
186 | entity("psi") -> 968;
187 | entity("omega") -> 969;
188 | entity("thetasym") -> 977;
189 | entity("upsih") -> 978;
190 | entity("piv") -> 982;
191 | entity("bull") -> 8226;
192 | entity("hellip") -> 8230;
193 | entity("prime") -> 8242;
194 | entity("Prime") -> 8243;
195 | entity("oline") -> 8254;
196 | entity("frasl") -> 8260;
197 | entity("weierp") -> 8472;
198 | entity("image") -> 8465;
199 | entity("real") -> 8476;
200 | entity("trade") -> 8482;
201 | entity("alefsym") -> 8501;
202 | entity("larr") -> 8592;
203 | entity("uarr") -> 8593;
204 | entity("rarr") -> 8594;
205 | entity("darr") -> 8595;
206 | entity("harr") -> 8596;
207 | entity("crarr") -> 8629;
208 | entity("lArr") -> 8656;
209 | entity("uArr") -> 8657;
210 | entity("rArr") -> 8658;
211 | entity("dArr") -> 8659;
212 | entity("hArr") -> 8660;
213 | entity("forall") -> 8704;
214 | entity("part") -> 8706;
215 | entity("exist") -> 8707;
216 | entity("empty") -> 8709;
217 | entity("nabla") -> 8711;
218 | entity("isin") -> 8712;
219 | entity("notin") -> 8713;
220 | entity("ni") -> 8715;
221 | entity("prod") -> 8719;
222 | entity("sum") -> 8721;
223 | entity("minus") -> 8722;
224 | entity("lowast") -> 8727;
225 | entity("radic") -> 8730;
226 | entity("prop") -> 8733;
227 | entity("infin") -> 8734;
228 | entity("ang") -> 8736;
229 | entity("and") -> 8743;
230 | entity("or") -> 8744;
231 | entity("cap") -> 8745;
232 | entity("cup") -> 8746;
233 | entity("int") -> 8747;
234 | entity("there4") -> 8756;
235 | entity("sim") -> 8764;
236 | entity("cong") -> 8773;
237 | entity("asymp") -> 8776;
238 | entity("ne") -> 8800;
239 | entity("equiv") -> 8801;
240 | entity("le") -> 8804;
241 | entity("ge") -> 8805;
242 | entity("sub") -> 8834;
243 | entity("sup") -> 8835;
244 | entity("nsub") -> 8836;
245 | entity("sube") -> 8838;
246 | entity("supe") -> 8839;
247 | entity("oplus") -> 8853;
248 | entity("otimes") -> 8855;
249 | entity("perp") -> 8869;
250 | entity("sdot") -> 8901;
251 | entity("lceil") -> 8968;
252 | entity("rceil") -> 8969;
253 | entity("lfloor") -> 8970;
254 | entity("rfloor") -> 8971;
255 | entity("lang") -> 9001;
256 | entity("rang") -> 9002;
257 | entity("loz") -> 9674;
258 | entity("spades") -> 9824;
259 | entity("clubs") -> 9827;
260 | entity("hearts") -> 9829;
261 | entity("diams") -> 9830;
262 | entity("quot") -> 34;
263 | entity("amp") -> 38;
264 | entity("lt") -> 60;
265 | entity("gt") -> 62;
266 | entity("OElig") -> 338;
267 | entity("oelig") -> 339;
268 | entity("Scaron") -> 352;
269 | entity("scaron") -> 353;
270 | entity("Yuml") -> 376;
271 | entity("circ") -> 710;
272 | entity("tilde") -> 732;
273 | entity("ensp") -> 8194;
274 | entity("emsp") -> 8195;
275 | entity("thinsp") -> 8201;
276 | entity("zwnj") -> 8204;
277 | entity("zwj") -> 8205;
278 | entity("lrm") -> 8206;
279 | entity("rlm") -> 8207;
280 | entity("ndash") -> 8211;
281 | entity("mdash") -> 8212;
282 | entity("lsquo") -> 8216;
283 | entity("rsquo") -> 8217;
284 | entity("sbquo") -> 8218;
285 | entity("ldquo") -> 8220;
286 | entity("rdquo") -> 8221;
287 | entity("bdquo") -> 8222;
288 | entity("dagger") -> 8224;
289 | entity("Dagger") -> 8225;
290 | entity("permil") -> 8240;
291 | entity("lsaquo") -> 8249;
292 | entity("rsaquo") -> 8250;
293 | entity("euro") -> 8364;
294 | entity(_) -> undefined.
295 |
296 |
--------------------------------------------------------------------------------
/deps/mochiweb/src/mochiweb_cookies.erl:
--------------------------------------------------------------------------------
1 | %% @author Emad El-Haraty
2 | %% @copyright 2007 Mochi Media, Inc.
3 |
4 | %% @doc HTTP Cookie parsing and generating (RFC 2109, RFC 2965).
5 |
6 | -module(mochiweb_cookies).
7 | -export([parse_cookie/1, cookie/3, cookie/2, test/0]).
8 |
9 | -define(QUOTE, $\").
10 |
11 | -define(IS_WHITESPACE(C),
12 | (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)).
13 |
14 | %% RFC 2616 separators (called tspecials in RFC 2068)
15 | -define(IS_SEPARATOR(C),
16 | (C < 32 orelse
17 | C =:= $\s orelse C =:= $\t orelse
18 | C =:= $( orelse C =:= $) orelse C =:= $< orelse C =:= $> orelse
19 | C =:= $@ orelse C =:= $, orelse C =:= $; orelse C =:= $: orelse
20 | C =:= $\\ orelse C =:= $\" orelse C =:= $/ orelse
21 | C =:= $[ orelse C =:= $] orelse C =:= $? orelse C =:= $= orelse
22 | C =:= ${ orelse C =:= $})).
23 |
24 | %% @type proplist() = [{Key::string(), Value::string()}].
25 | %% @type header() = {Name::string(), Value::string()}.
26 |
27 | %% @spec cookie(Key::string(), Value::string()) -> header()
28 | %% @doc Short-hand for cookie(Key, Value, []).
29 | cookie(Key, Value) ->
30 | cookie(Key, Value, []).
31 |
32 | %% @spec cookie(Key::string(), Value::string(), Options::[Option]) -> header()
33 | %% where Option = {max_age, integer()} | {local_time, {date(), time()}}
34 | %% | {domain, string()} | {path, string()}
35 | %% | {secure, true | false}
36 | %%
37 | %% @doc Generate a Set-Cookie header field tuple.
38 | cookie(Key, Value, Options) ->
39 | Cookie = [any_to_list(Key), "=", quote(Value), "; Version=1"],
40 | %% Set-Cookie:
41 | %% Comment, Domain, Max-Age, Path, Secure, Version
42 | %% Set-Cookie2:
43 | %% Comment, CommentURL, Discard, Domain, Max-Age, Path, Port, Secure,
44 | %% Version
45 | ExpiresPart =
46 | case proplists:get_value(max_age, Options) of
47 | undefined ->
48 | "";
49 | RawAge ->
50 | When = case proplists:get_value(local_time, Options) of
51 | undefined ->
52 | calendar:local_time();
53 | LocalTime ->
54 | LocalTime
55 | end,
56 | Age = case RawAge < 0 of
57 | true ->
58 | 0;
59 | false ->
60 | RawAge
61 | end,
62 | ["; Expires=", age_to_cookie_date(Age, When),
63 | "; Max-Age=", quote(Age)]
64 | end,
65 | SecurePart =
66 | case proplists:get_value(secure, Options) of
67 | true ->
68 | "; Secure";
69 | _ ->
70 | ""
71 | end,
72 | DomainPart =
73 | case proplists:get_value(domain, Options) of
74 | undefined ->
75 | "";
76 | Domain ->
77 | ["; Domain=", quote(Domain)]
78 | end,
79 | PathPart =
80 | case proplists:get_value(path, Options) of
81 | undefined ->
82 | "";
83 | Path ->
84 | ["; Path=", quote(Path)]
85 | end,
86 | CookieParts = [Cookie, ExpiresPart, SecurePart, DomainPart, PathPart],
87 | {"Set-Cookie", lists:flatten(CookieParts)}.
88 |
89 |
90 | %% Every major browser incorrectly handles quoted strings in a
91 | %% different and (worse) incompatible manner. Instead of wasting time
92 | %% writing redundant code for each browser, we restrict cookies to
93 | %% only contain characters that browsers handle compatibly.
94 | %%
95 | %% By replacing the definition of quote with this, we generate
96 | %% RFC-compliant cookies:
97 | %%
98 | %% quote(V) ->
99 | %% Fun = fun(?QUOTE, Acc) -> [$\\, ?QUOTE | Acc];
100 | %% (Ch, Acc) -> [Ch | Acc]
101 | %% end,
102 | %% [?QUOTE | lists:foldr(Fun, [?QUOTE], V)].
103 |
104 | %% Convert to a string and raise an error if quoting is required.
105 | quote(V0) ->
106 | V = any_to_list(V0),
107 | lists:all(fun(Ch) -> Ch =:= $/ orelse not ?IS_SEPARATOR(Ch) end, V)
108 | orelse erlang:error({cookie_quoting_required, V}),
109 | V.
110 |
111 | add_seconds(Secs, LocalTime) ->
112 | Greg = calendar:datetime_to_gregorian_seconds(LocalTime),
113 | calendar:gregorian_seconds_to_datetime(Greg + Secs).
114 |
115 | age_to_cookie_date(Age, LocalTime) ->
116 | httpd_util:rfc1123_date(add_seconds(Age, LocalTime)).
117 |
118 | %% @spec parse_cookie(string()) -> [{K::string(), V::string()}]
119 | %% @doc Parse the contents of a Cookie header field, ignoring cookie
120 | %% attributes, and return a simple property list.
121 | parse_cookie("") ->
122 | [];
123 | parse_cookie(Cookie) ->
124 | parse_cookie(Cookie, []).
125 |
126 | %% @spec test() -> ok
127 | %% @doc Run tests for mochiweb_cookies.
128 | test() ->
129 | parse_cookie_test(),
130 | cookie_test(),
131 | ok.
132 |
133 | %% Internal API
134 |
135 | parse_cookie([], Acc) ->
136 | lists:reverse(Acc);
137 | parse_cookie(String, Acc) ->
138 | {{Token, Value}, Rest} = read_pair(String),
139 | Acc1 = case Token of
140 | "" ->
141 | Acc;
142 | "$" ++ _ ->
143 | Acc;
144 | _ ->
145 | [{Token, Value} | Acc]
146 | end,
147 | parse_cookie(Rest, Acc1).
148 |
149 | read_pair(String) ->
150 | {Token, Rest} = read_token(skip_whitespace(String)),
151 | {Value, Rest1} = read_value(skip_whitespace(Rest)),
152 | {{Token, Value}, skip_past_separator(Rest1)}.
153 |
154 | read_value([$= | Value]) ->
155 | Value1 = skip_whitespace(Value),
156 | case Value1 of
157 | [?QUOTE | _] ->
158 | read_quoted(Value1);
159 | _ ->
160 | read_token(Value1)
161 | end;
162 | read_value(String) ->
163 | {"", String}.
164 |
165 | read_quoted([?QUOTE | String]) ->
166 | read_quoted(String, []).
167 |
168 | read_quoted([], Acc) ->
169 | {lists:reverse(Acc), []};
170 | read_quoted([?QUOTE | Rest], Acc) ->
171 | {lists:reverse(Acc), Rest};
172 | read_quoted([$\\, Any | Rest], Acc) ->
173 | read_quoted(Rest, [Any | Acc]);
174 | read_quoted([C | Rest], Acc) ->
175 | read_quoted(Rest, [C | Acc]).
176 |
177 | skip_whitespace(String) ->
178 | F = fun (C) -> ?IS_WHITESPACE(C) end,
179 | lists:dropwhile(F, String).
180 |
181 | read_token(String) ->
182 | F = fun (C) -> not ?IS_SEPARATOR(C) end,
183 | lists:splitwith(F, String).
184 |
185 | skip_past_separator([]) ->
186 | [];
187 | skip_past_separator([$; | Rest]) ->
188 | Rest;
189 | skip_past_separator([$, | Rest]) ->
190 | Rest;
191 | skip_past_separator([_ | Rest]) ->
192 | skip_past_separator(Rest).
193 |
194 | parse_cookie_test() ->
195 | %% RFC example
196 | C1 = "$Version=\"1\"; Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\";
197 | Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\";
198 | Shipping=\"FedEx\"; $Path=\"/acme\"",
199 | [
200 | {"Customer","WILE_E_COYOTE"},
201 | {"Part_Number","Rocket_Launcher_0001"},
202 | {"Shipping","FedEx"}
203 | ] = parse_cookie(C1),
204 | %% Potential edge cases
205 | [{"foo", "x"}] = parse_cookie("foo=\"\\x\""),
206 | [] = parse_cookie("="),
207 | [{"foo", ""}, {"bar", ""}] = parse_cookie(" foo ; bar "),
208 | [{"foo", ""}, {"bar", ""}] = parse_cookie("foo=;bar="),
209 | [{"foo", "\";"}, {"bar", ""}] = parse_cookie("foo = \"\\\";\";bar "),
210 | [{"foo", "\";bar"}] = parse_cookie("foo=\"\\\";bar").
211 |
212 | any_to_list(V) when is_list(V) ->
213 | V;
214 | any_to_list(V) when is_atom(V) ->
215 | atom_to_list(V);
216 | any_to_list(V) when is_binary(V) ->
217 | binary_to_list(V);
218 | any_to_list(V) when is_integer(V) ->
219 | integer_to_list(V).
220 |
221 |
222 | cookie_test() ->
223 | C1 = {"Set-Cookie",
224 | "Customer=WILE_E_COYOTE; "
225 | "Version=1; "
226 | "Path=/acme"},
227 | C1 = cookie("Customer", "WILE_E_COYOTE", [{path, "/acme"}]),
228 | C1 = cookie("Customer", "WILE_E_COYOTE",
229 | [{path, "/acme"}, {badoption, "negatory"}]),
230 | C1 = cookie('Customer', 'WILE_E_COYOTE', [{path, '/acme'}]),
231 | C1 = cookie(<<"Customer">>, <<"WILE_E_COYOTE">>, [{path, <<"/acme">>}]),
232 |
233 | {"Set-Cookie","=NoKey; Version=1"} = cookie("", "NoKey", []),
234 |
235 | LocalTime = calendar:universal_time_to_local_time({{2007, 5, 15}, {13, 45, 33}}),
236 | C2 = {"Set-Cookie",
237 | "Customer=WILE_E_COYOTE; "
238 | "Version=1; "
239 | "Expires=Tue, 15 May 2007 13:45:33 GMT; "
240 | "Max-Age=0"},
241 | C2 = cookie("Customer", "WILE_E_COYOTE",
242 | [{max_age, -111}, {local_time, LocalTime}]),
243 | C3 = {"Set-Cookie",
244 | "Customer=WILE_E_COYOTE; "
245 | "Version=1; "
246 | "Expires=Wed, 16 May 2007 13:45:50 GMT; "
247 | "Max-Age=86417"},
248 | C3 = cookie("Customer", "WILE_E_COYOTE",
249 | [{max_age, 86417}, {local_time, LocalTime}]),
250 | ok.
251 |
--------------------------------------------------------------------------------
/deps/mochiweb/src/mochiweb_echo.erl:
--------------------------------------------------------------------------------
1 | %% @author Bob Ippolito
2 | %% @copyright 2007 Mochi Media, Inc.
3 |
4 | %% @doc Simple and stupid echo server to demo mochiweb_socket_server.
5 |
6 | -module(mochiweb_echo).
7 | -author('bob@mochimedia.com').
8 | -export([start/0, stop/0, loop/1]).
9 |
10 | stop() ->
11 | mochiweb_socket_server:stop(?MODULE).
12 |
13 | start() ->
14 | mochiweb_socket_server:start([{name, ?MODULE},
15 | {port, 6789},
16 | {ip, "127.0.0.1"},
17 | {max, 1},
18 | {loop, {?MODULE, loop}}]).
19 |
20 | loop(Socket) ->
21 | case gen_tcp:recv(Socket, 0, 30000) of
22 | {ok, Data} ->
23 | case gen_tcp:send(Socket, Data) of
24 | ok ->
25 | loop(Socket);
26 | _ ->
27 | exit(normal)
28 | end;
29 | _Other ->
30 | exit(normal)
31 | end.
32 |
--------------------------------------------------------------------------------
/deps/mochiweb/src/mochiweb_headers.erl:
--------------------------------------------------------------------------------
1 | %% @author Bob Ippolito
2 | %% @copyright 2007 Mochi Media, Inc.
3 |
4 | %% @doc Case preserving (but case insensitive) HTTP Header dictionary.
5 |
6 | -module(mochiweb_headers).
7 | -author('bob@mochimedia.com').
8 | -export([empty/0, from_list/1, insert/3, enter/3, get_value/2, lookup/2]).
9 | -export([get_primary_value/2]).
10 | -export([default/3, enter_from_list/2, default_from_list/2]).
11 | -export([to_list/1, make/1]).
12 | -export([test/0]).
13 |
14 | %% @type headers().
15 | %% @type key() = atom() | binary() | string().
16 | %% @type value() = atom() | binary() | string() | integer().
17 |
18 | %% @spec test() -> ok
19 | %% @doc Run tests for this module.
20 | test() ->
21 | H = ?MODULE:make([{hdr, foo}, {"Hdr", "bar"}, {'Hdr', 2}]),
22 | [{hdr, "foo, bar, 2"}] = ?MODULE:to_list(H),
23 | H1 = ?MODULE:insert(taco, grande, H),
24 | [{hdr, "foo, bar, 2"}, {taco, "grande"}] = ?MODULE:to_list(H1),
25 | H2 = ?MODULE:make([{"Set-Cookie", "foo"}]),
26 | [{"Set-Cookie", "foo"}] = ?MODULE:to_list(H2),
27 | H3 = ?MODULE:insert("Set-Cookie", "bar", H2),
28 | [{"Set-Cookie", "foo"}, {"Set-Cookie", "bar"}] = ?MODULE:to_list(H3),
29 | "foo, bar" = ?MODULE:get_value("set-cookie", H3),
30 | {value, {"Set-Cookie", "foo, bar"}} = ?MODULE:lookup("set-cookie", H3),
31 | undefined = ?MODULE:get_value("shibby", H3),
32 | none = ?MODULE:lookup("shibby", H3),
33 | H4 = ?MODULE:insert("content-type",
34 | "application/x-www-form-urlencoded; charset=utf8",
35 | H3),
36 | "application/x-www-form-urlencoded" = ?MODULE:get_primary_value(
37 | "content-type", H4),
38 | ok.
39 |
40 | %% @spec empty() -> headers()
41 | %% @doc Create an empty headers structure.
42 | empty() ->
43 | gb_trees:empty().
44 |
45 | %% @spec make(headers() | [{key(), value()}]) -> headers()
46 | %% @doc Construct a headers() from the given list.
47 | make(L) when is_list(L) ->
48 | from_list(L);
49 | %% assume a tuple is already mochiweb_headers.
50 | make(T) when is_tuple(T) ->
51 | T.
52 |
53 | %% @spec from_list([{key(), value()}]) -> headers()
54 | %% @doc Construct a headers() from the given list.
55 | from_list(List) ->
56 | lists:foldl(fun ({K, V}, T) -> insert(K, V, T) end, empty(), List).
57 |
58 | %% @spec enter_from_list([{key(), value()}], headers()) -> headers()
59 | %% @doc Insert pairs into the headers, replace any values for existing keys.
60 | enter_from_list(List, T) ->
61 | lists:foldl(fun ({K, V}, T1) -> enter(K, V, T1) end, T, List).
62 |
63 | %% @spec default_from_list([{key(), value()}], headers()) -> headers()
64 | %% @doc Insert pairs into the headers for keys that do not already exist.
65 | default_from_list(List, T) ->
66 | lists:foldl(fun ({K, V}, T1) -> default(K, V, T1) end, T, List).
67 |
68 | %% @spec to_list(headers()) -> [{key(), string()}]
69 | %% @doc Return the contents of the headers. The keys will be the exact key
70 | %% that was first inserted (e.g. may be an atom or binary, case is
71 | %% preserved).
72 | to_list(T) ->
73 | F = fun ({K, {array, L}}, Acc) ->
74 | L1 = lists:reverse(L),
75 | lists:foldl(fun (V, Acc1) -> [{K, V} | Acc1] end, Acc, L1);
76 | (Pair, Acc) ->
77 | [Pair | Acc]
78 | end,
79 | lists:reverse(lists:foldl(F, [], gb_trees:values(T))).
80 |
81 | %% @spec get_value(key(), headers()) -> string() | undefined
82 | %% @doc Return the value of the given header using a case insensitive search.
83 | %% undefined will be returned for keys that are not present.
84 | get_value(K, T) ->
85 | case lookup(K, T) of
86 | {value, {_, V}} ->
87 | expand(V);
88 | none ->
89 | undefined
90 | end.
91 |
92 | %% @spec get_primary_value(key(), headers()) -> string() | undefined
93 | %% @doc Return the value of the given header up to the first semicolon using
94 | %% a case insensitive search. undefined will be returned for keys
95 | %% that are not present.
96 | get_primary_value(K, T) ->
97 | case get_value(K, T) of
98 | undefined ->
99 | undefined;
100 | V ->
101 | lists:takewhile(fun (C) -> C =/= $; end, V)
102 | end.
103 |
104 | %% @spec lookup(key(), headers()) -> {value, {key(), string()}} | none
105 | %% @doc Return the case preserved key and value for the given header using
106 | %% a case insensitive search. none will be returned for keys that are
107 | %% not present.
108 | lookup(K, T) ->
109 | case gb_trees:lookup(normalize(K), T) of
110 | {value, {K0, V}} ->
111 | {value, {K0, expand(V)}};
112 | none ->
113 | none
114 | end.
115 |
116 | %% @spec default(key(), value(), headers()) -> headers()
117 | %% @doc Insert the pair into the headers if it does not already exist.
118 | default(K, V, T) ->
119 | K1 = normalize(K),
120 | V1 = any_to_list(V),
121 | try gb_trees:insert(K1, {K, V1}, T)
122 | catch
123 | error:{key_exists, _} ->
124 | T
125 | end.
126 |
127 | %% @spec enter(key(), value(), headers()) -> headers()
128 | %% @doc Insert the pair into the headers, replacing any pre-existing key.
129 | enter(K, V, T) ->
130 | K1 = normalize(K),
131 | V1 = any_to_list(V),
132 | gb_trees:enter(K1, {K, V1}, T).
133 |
134 | %% @spec insert(key(), value(), headers()) -> headers()
135 | %% @doc Insert the pair into the headers, merging with any pre-existing key.
136 | %% A merge is done with Value = V0 ++ ", " ++ V1.
137 | insert(K, V, T) ->
138 | K1 = normalize(K),
139 | V1 = any_to_list(V),
140 | try gb_trees:insert(K1, {K, V1}, T)
141 | catch
142 | error:{key_exists, _} ->
143 | {K0, V0} = gb_trees:get(K1, T),
144 | V2 = merge(K1, V1, V0),
145 | gb_trees:update(K1, {K0, V2}, T)
146 | end.
147 |
148 | %% Internal API
149 |
150 | expand({array, L}) ->
151 | mochiweb_util:join(lists:reverse(L), ", ");
152 | expand(V) ->
153 | V.
154 |
155 | merge("set-cookie", V1, {array, L}) ->
156 | {array, [V1 | L]};
157 | merge("set-cookie", V1, V0) ->
158 | {array, [V1, V0]};
159 | merge(_, V1, V0) ->
160 | V0 ++ ", " ++ V1.
161 |
162 | normalize(K) when is_list(K) ->
163 | string:to_lower(K);
164 | normalize(K) when is_atom(K) ->
165 | normalize(atom_to_list(K));
166 | normalize(K) when is_binary(K) ->
167 | normalize(binary_to_list(K)).
168 |
169 | any_to_list(V) when is_list(V) ->
170 | V;
171 | any_to_list(V) when is_atom(V) ->
172 | atom_to_list(V);
173 | any_to_list(V) when is_binary(V) ->
174 | binary_to_list(V);
175 | any_to_list(V) when is_integer(V) ->
176 | integer_to_list(V).
177 |
178 |
179 |
--------------------------------------------------------------------------------
/deps/mochiweb/src/mochiweb_http.erl:
--------------------------------------------------------------------------------
1 | %% @author Bob Ippolito
2 | %% @copyright 2007 Mochi Media, Inc.
3 |
4 | %% @doc HTTP server.
5 |
6 | -module(mochiweb_http).
7 | -author('bob@mochimedia.com').
8 | -export([start/0, start/1, stop/0, stop/1]).
9 | -export([loop/2, default_body/1]).
10 |
11 | -define(IDLE_TIMEOUT, 30000).
12 |
13 | -define(DEFAULTS, [{name, ?MODULE},
14 | {port, 8888}]).
15 |
16 | set_default({Prop, Value}, PropList) ->
17 | case proplists:is_defined(Prop, PropList) of
18 | true ->
19 | PropList;
20 | false ->
21 | [{Prop, Value} | PropList]
22 | end.
23 |
24 | set_defaults(Defaults, PropList) ->
25 | lists:foldl(fun set_default/2, PropList, Defaults).
26 |
27 | parse_options(Options) ->
28 | {loop, HttpLoop} = proplists:lookup(loop, Options),
29 | Loop = fun (S) ->
30 | ?MODULE:loop(S, HttpLoop)
31 | end,
32 | Options1 = [{loop, Loop} | proplists:delete(loop, Options)],
33 | set_defaults(?DEFAULTS, Options1).
34 |
35 | stop() ->
36 | mochiweb_socket_server:stop(?MODULE).
37 |
38 | stop(Name) ->
39 | mochiweb_socket_server:stop(Name).
40 |
41 | start() ->
42 | start([{ip, "127.0.0.1"},
43 | {loop, {?MODULE, default_body}}]).
44 |
45 | start(Options) ->
46 | mochiweb_socket_server:start(parse_options(Options)).
47 |
48 | frm(Body) ->
49 | [""
50 | ""
54 | " "
55 | ""
61 | "
13 |
14 |
--------------------------------------------------------------------------------
/doc/overview-summary.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davebryson/beepbeep/62db46d268c6cb6ad86345562b3c77f8ff070b27/doc/overview-summary.html
--------------------------------------------------------------------------------
/doc/overview.edoc:
--------------------------------------------------------------------------------
1 | @title BeepBeep
2 | @author Dave Bryson
3 | @copyright Dave Bryson 2008-2009
4 |
5 | @doc
6 | == Contents ==
7 | {@section Introduction}
8 | {@section Creating a new application}
9 | {@section Controllers}
10 | {@section Return values}
11 | {@section Parameters and Session}
12 |
13 | == Introduction ==
14 | BeepBeep is a simple web application designed to provide a familiar environment for developers
15 | coming from Rails. Like Rails, if you follow a few conventions, BeepBeep will automatically
16 | map requests to your controllers and views leaving you to focus on the core of your application.
17 |
18 | Features:
19 |
20 |
Server side session store
21 |
Automatic mapping of requests to controllers and views
22 |
Automatic binding of extra path arguments to controller variables
23 |
Before filter on your controllers
24 |
Django templates provided by erlydtl
25 |
26 |
27 | == Creating a new application ==
28 |
29 | BeepBeep includes a simple script you can use to generate an application. The script will create the base code,
30 | directory structure, and a default controller you can use as a starting point.
31 |
32 | To create a new application, make sure you are in the root directory of BeepBeep and run:
33 |
34 | ```
35 | ./script/new_beepbeep.erl [name of app] [destination directory]
36 | '''
37 |
38 | For example to create the app 'blog' in the directory '/home/dave/' you'd enter:
39 |
40 | ```
41 | ./script/new_beepbeep.erl blog /home/dave
42 | '''
43 |
44 | If you look in the '/home/dave/blog' directory you'll see the application structure along with
45 | a default controller called 'home_controller.erl' in the src directory. Any controllers you create should end with
46 | '_controller.erl'. BeepBeep uses to this convention to map requests to your code.
47 |
48 | Templates are located in the 'views' directory and any static content such as images, stylesheets, etc... are located
49 | in the 'www' directory.
50 |
51 | Now, to run the default application run first run 'make' and then start the server with:
52 |
53 | ```
54 | ./start_server.sh
55 | '''
56 |
57 | This will start the webserver and application on port 8000. You can change the port in the '*_sup.erl' file if
58 | desired.
59 |
60 | Once the server starts go to:
61 |
62 | ```
63 | http://localhost:8000/
64 | '''
65 |
66 | to see the default application.
67 |
68 |
69 | == Controllers ==
70 |
71 | Most of the code you write will be in controllers. Controllers are regular Erlang modules. The exception is they
72 | need to follow a few rules:
73 |
74 |
75 |
The name of the module must end in '_controller.erl'
76 |
The module must export and implement two functions: 'handler_request/2' and 'before_filter/0'
77 |
78 |
79 | The functions 'handle_request/2' are the 'actions' in a controller. For example, a request to:
80 |
81 | ```
82 | /login/new
83 |
84 | /[controller name]/[function]
85 | '''
86 |
87 | would map to the function:
88 |
89 | ```
90 | handle_request("new",[]) ->
91 | ...
92 | '''
93 |
94 | The first parameter 'new' is a String used for matching and the second parameter is an array that will automatically
95 | map any extra components in the path to an Erlang variable. For example:
96 |
97 | home_controller.erl:
98 |
99 | ```
100 | handle_request("show",[Year]) ->
101 | error_logger:info_msg("The year sent is: ~s",[Year]),
102 | ...
103 |
104 | '''
105 |
106 | would map to the request:
107 |
108 | ```
109 | /home/show/2009
110 | '''
111 |
112 | and bind '2009' to the 'Year' variable.
113 |
114 |
115 | The 'before_filter' will run any code you specify 'before' the requested 'handle_request' is run. This is and
116 | ideal place for authentication. The before returns two types of results:
117 |
118 |
119 |
the atom 'ok'. Which means the filter passed or
120 |
a BeepBeep response tuple
121 |
122 |
123 | See the Blog example included with the source code in 'example/blog/src/home_controller.erl'.
124 |
125 |
126 | == Return values ==
127 |
128 | Here are the possible return values from 'handle_request' and 'before_filter':
129 |
130 | ```
131 | {render,View,Data}
132 | '''
133 | View is a string for the template to use: "/home/index.html"
134 | Data is an array of tuples to bind in your template: [{name,"dave"}]. This make the key name, and the value "dave"
135 | available in you Django template.
136 |
137 | ```
138 | {render,View,Data,Options}
139 | '''
140 | Same as above, except Options is an array of tuples to include in the header. For example:
141 |
142 | ```
143 | [{status,201}]
144 | '''
145 |
146 | ```
147 | {text,Content}
148 | '''
149 |
150 | Returns plain text where 'Content' is a String or Binary
151 |
152 | ```
153 | {redirect, Url}
154 | '''
155 |
156 | Redirects (302) to the given Url: {redirect,"/"}
157 |
158 | ```
159 | {static,File}
160 | '''
161 | Render a static file from the 'www' directory: {static,"images/hello.jpg"}
162 |
163 | ```
164 | {error,Reason}
165 | '''
166 | Sends a status code 500 (server error)
167 |
168 |
169 | Future support will include a JSON response.
170 |
171 | == Parameters and Session ==
172 |
173 | See the beepbeep_args for the api to work with sessions and parameters.
174 |
175 |
176 |
177 |
178 |
179 |
180 |
--------------------------------------------------------------------------------
/doc/packages-frame.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The BeepBeep application
5 |
6 |
7 |
8 |