View source with raw comments or as raw
    1/*  Part of SWISH
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2014-2018, VU University Amsterdam
    7			      CWI, Amsterdam
    8    All rights reserved.
    9
   10    Redistribution and use in source and binary forms, with or without
   11    modification, are permitted provided that the following conditions
   12    are met:
   13
   14    1. Redistributions of source code must retain the above copyright
   15       notice, this list of conditions and the following disclaimer.
   16
   17    2. Redistributions in binary form must reproduce the above copyright
   18       notice, this list of conditions and the following disclaimer in
   19       the documentation and/or other materials provided with the
   20       distribution.
   21
   22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   23    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   24    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   25    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   26    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   27    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   28    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   29    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   30    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   32    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   33    POSSIBILITY OF SUCH DAMAGE.
   34
   35    Changes by:    Riccardo Zese
   36    E-mail:        riccardo.zese@unife.it
   37*/
   38
   39:- module(swish_examples, []).   40:- use_module(library(http/http_dispatch)).   41:- use_module(library(http/http_json)).   42:- use_module(library(http/json)).   43:- use_module(library(http/http_path)).   44:- use_module(library(filesex)).   45:- use_module(library(apply)).   46:- use_module(library(option)).   47:- use_module(library(lists)).   48:- if(exists_source(library(atom))).   49:- use_module(library(atom)).   50:- endif.   51
   52:- use_module(storage).   53:- use_module(md_eval).

Serve example files

Locate and serve files for the Examples menu as well as examples included from overview notebooks. The examples come from two sources:

This module also makes the known examples available through swish_provides/1 for supporting conditional statements on example overview notebooks. */

   70:- multifile
   71	user:file_search_path/2,
   72	swish_config:config/2,
   73	swish_config:source_alias/2.   74
   75% make example(File) find the example data
   76user:file_search_path(example, swish(examples)).
   77user:file_search_path(example, swish(examples/trill)).
   78
   79user:file_search_path(e, swish(examples)).
   80user:file_search_path(e, swish(examples/trill)).
   81% make SWISH serve /example/File as example(File).
   82swish_config:source_alias(example, [access(read), search('*.{pl,swinb}')]).
   83swish_config:source_alias(e, [access(read), search('*.{pl,swinb}')]).
   84
   85:- http_handler(swish(list_examples),
   86		list_examples, [id(swish_examples)]).
 list_examples(+Request)
Get a list of registered example code. Examples are described in a file swish_examples('index.json').
   94list_examples(_Request) :-
   95	examples(AllExamples, [community(true)]),
   96	example_menu(AllExamples, Menu),
   97	reply_json(Menu).
   98
   99example_menu(AllExamples, Menu) :-
  100	include(pos_ranked, AllExamples, ForMenu),
  101	insert_group_dividers(ForMenu, Menu).
  102
  103pos_ranked(Ex) :-
  104	Rank = Ex.get(grank),
  105	Rank > 0.
  106
  107insert_group_dividers([], []).
  108insert_group_dividers([H1,H2|T], List) :-
  109	!,
  110	(   H1.grank // 10000 =\= H2.grank // 10000
  111	->  List = [H1, json{type:divider}|Rest]
  112	;   List = [H1|Rest]
  113	),
  114	insert_group_dividers([H2|T], Rest).
  115insert_group_dividers([H], [H]).
 examples(JSON:list, +Options) is det
JSON is a list of JSON dicts containing the keys below. The list is composed from all *.pl files in the search path example.
  129examples(AllExamples, Options) :-
  130	swish_examples(SWISHExamples),
  131	(   option(community(true), Options)
  132	->  community_examples(CommunityEx)
  133	;   CommunityEx = json{}
  134	),
  135	join_examples([CommunityEx|SWISHExamples], AllExamples).
  136
  137:- dynamic
  138	swish_example_cache/2.  139
  140swish_examples(SWISHExamples) :-
  141	swish_example_cache(SWISHExamples, Time),
  142	get_time(Now),
  143	Now - Time < 60,
  144	!.
  145swish_examples(SWISHExamples) :-
  146	swish_examples_no_cache(SWISHExamples),
  147	get_time(Now),
  148	retractall(swish_example_cache(_,_)),
  149	assertz(swish_example_cache(SWISHExamples, Now)).
  150
  151swish_examples_no_cache(SWISHExamples) :-
  152	http_absolute_location(swish(example), HREF, []),
  153	findall(Index,
  154		absolute_file_name(example(.), Index,
  155				   [ access(read),
  156				     file_type(directory),
  157				     file_errors(fail),
  158				     solutions(all)
  159				   ]),
  160		ExDirs),
  161	maplist(index_json(HREF), ExDirs, SWISHExamples).
  162
  163
  164join_examples(PerDir, Files) :-
  165	menu_groups(PerDir, Groups),
  166	maplist(get_or(files, []), PerDir, FilesPerDir),
  167	append(FilesPerDir, Files0),
  168	maplist(add_grank(Groups), Files0, Files1),
  169	sort(grank, =<, Files1, Files).
  170
  171add_grank(Groups, File0, File) :-
  172	get_or(rank,  500,  File0, FRank),
  173	GroupName = File0.get(group),
  174	member(Group, Groups),
  175	Group.get(group) == GroupName,
  176	GRank is FRank + Group.get(rank), !,
  177	File = File0.put(grank, GRank).
  178add_grank(_, File0, File) :-
  179	File = File0.put(grank, -1).
  180
  181menu_groups(PerDir, Groups) :-
  182	maplist(get_or(menu, []), PerDir, GroupsPerDir),
  183	append(GroupsPerDir, Groups0),
  184	sort(group, @>, Groups0, Groups1),
  185	sort(rank,  =<, Groups1, Groups).
  186
  187get_or(Key, Default, Dict, Value) :-
  188	(   is_dict(Dict),
  189	    Value = Dict.get(Key)
  190	->  true
  191	;   Value = Default
  192	).
 index_json(+BaseHREF, +Directory, -JSON)
Produce a JSON description for the examples in the directory Dir. This deals with two scenarios: if a file index.json is provided, use this file and add the not-described files as examples that are not included in the menu. If no index.json is present, all files are added as example files.
  202index_json(HREF, Dir, JSON) :-
  203	directory_file_path(Dir, 'index.json', File),
  204	access_file(File, read), !,
  205	read_file_to_json(File, JSON0),
  206	add_examples_href(HREF, JSON0, JSON1),
  207	add_other_files(HREF, Dir, JSON1, JSON).
  208index_json(HREF, Dir, json{menu:[json{group:examples, rank:10000}],
  209			   files:Files}) :-
  210	example_files(HREF, Dir, Files0),
  211	maplist(add_group(examples), Files0, Files).
  212
  213example_files(HREF, Dir, JSON) :-
  214	string_concat(Dir, "/*.{pl,swinb}", Pattern),
  215	expand_file_name(Pattern, Files),
  216	maplist(ex_file_json(HREF), Files, JSON).
  217
  218read_file_to_json(File, JSON) :-
  219	setup_call_cleanup(
  220	    open(File, read, In, [encoding(utf8)]),
  221	    json_read_dict(In, JSON, [default_tag(json)]),
  222	    close(In)).
 add_examples_href(+HREF, +JSON0, -JSON) is det
Add a href key pointing at the example. Also removes all items that are not dicts or have no file key.
  229add_examples_href(HREF, JSON0, JSON) :-
  230	Files0 = JSON0.get(files), !,
  231	convlist(add_href(HREF), Files0, Files),
  232	JSON = JSON0.put(files, Files).
  233add_examples_href(_, JSON, JSON).
  234
  235
  236add_href(HREF0, Dict, Dict2) :-
  237	is_dict(Dict),
  238	directory_file_path(HREF0, Dict.get(file), HREF),
  239	Dict2 = Dict.put(href, HREF).
  240
  241add_group(Group, Dict0, Dict) :-
  242	is_dict(Dict0), !,
  243	Dict = Dict0.put(group, Group).
  244add_group(_, Dict, Dict).
  245
  246add_other_files(HREF, Dir, JSON0, JSON) :-
  247	example_files(HREF, Dir, Files),
  248	get_or(files, [], JSON0, Files0),
  249	exclude(in_ex_list(Files0), Files, New),
  250	append(Files0, New, AllFiles),
  251	JSON = JSON0.put(files, AllFiles).
  252
  253in_ex_list(Examples, Ex) :-
  254	File = Ex.file,
  255	member(Ex2, Examples),
  256	is_dict(Ex2),
  257	File = Ex2.get(file),
  258	!.
 ex_file_json(+ExampleBase, +Path, -JSON) is det
Create a JSON representation for the given example file.
  264ex_file_json(HREF0, Path, json{file:File, href:HREF, title:Title}) :-
  265	file_base_name(Path, File),
  266	file_name_extension(Base, _, File),
  267	file_name_to_title(Base, Title),
  268	directory_file_path(HREF0, File, HREF).
  269
  270:- if(current_predicate(restyle_identifier/3)).  271file_name_to_title(Base, Title) :-
  272	restyle_identifier(style(true,false,' '), Base, Title).
  273:- else.  274file_name_to_title(Base, Base).
  275:- endif.
 md_eval:provides(?Term) is nondet
Make examples available through swish_provides/1. Can be used in dynamic cells as, e.g.,:
:- if(swish_provides(example('chat80.pl',_,_))).
...
:- endif.
  289:- multifile
  290	md_eval:provides/1.  291
  292md_eval:provides(example(Name, Group, Example)) :-
  293	examples(Examples, []),
  294	(   var(Name)
  295	->  member(Example0, Examples),
  296	    atom_string(Name, Example0.get(file))
  297	;   member(Example0, Examples),
  298	    atom_string(Name, Example0.get(file))
  299	->  true
  300	),
  301	atom_string(Group,  Example0.get(group)),
  302	active_example(Example0, Example).
  303
  304active_example(Example0, Example) :-
  305	term_string(Cond, Example0.get(requires)),
  306	\+ swish_provides(Cond),
  307	(   cond_reason(Cond, Fmt, Args)
  308	->  format(string(Reason), Fmt, Args)
  309	;   format(string(Reason), 'missing requirement: ~q', [Cond])
  310	),
  311	Example = Example0.put(blocked, Reason).
  312active_example(Example, Example).
  313
  314cond_reason(plugin(Name), 'missing plugin: ~w', [Name]).
  315
  316
  317
  318		 /*******************************
  319		 *	      STORAGE		*
  320		 *******************************/
 community_examples(-Dict) is det
Extract examples from the gitty store.
  326community_examples(json{menu:[json{group:community, rank:50000}],
  327			files:Files}) :-
  328	swish_config:config(community_examples, true),
  329	!,
  330	findall(Ex, community_example(Ex), Files).
  331community_examples(json{}).
  332
  333community_example(json{title:Title, file:File, group:community, type:store}) :-
  334	storage_file_extension_head(File, _Type, Head),
  335	storage_commit(Head, Meta),
  336	Meta.get(example) == true,
  337	(   Title = Meta.get(title), Title \== ""
  338	->  true
  339	;   file_name_extension(Base, _, File),
  340	    file_name_to_title(Base, Title)
  341	)