The time has come when I need to bite the bullet and learn how to do some web development. It's really not my cup of tea. However, I have an idea for an application that needs to have a GUI in a web browser.
There is nothing revolutionary about this. It's pretty simple. There will be some sort of database back end (I'll have to learn that too) and I will write some code to implement the business logic which will run on the server and speak to the database. It will speak to a web server on the other side and there will be a client which will exist in a web page for drawing the pictures, including the buttons and the text boxes to type things in.
The last time I went near any evil web stuff was over a decade ago. We were using Fitnesse to run a bunch of Ruby scripts on a server somewhere. I seem to remember there was a web server (written in Java) that came with it. I quite liked Ruby, but haven't touched it since.
Due to the fact that I am a bit lysdexic (I think) I find programming in dynamic languages very difficult. I also appreciate the extra layers of compile-time checks a compiler gives you. I realise this is not the way web development works, which is why I have avoided it. I prefer to write my code using TDD so I always write a failing unit test case before implementing code to make it pass, rinse and repeat.
So what I want is something simple and powerful to do the browser front end which runs in as many browsers as possible, and will not drive me mad or give me migraines when I'm trying to learn how to do it.
Suggestions please!
(Score: 2) by c0lo on Saturday December 17, @10:07AM (6 children)
Stay away! Run as fast as possible!! Trigger a second Brexit or sabotage a nuke power plant!!!
Have your cyanide capsule ready if you get caught!!!!
DO. NOT. ENGAGE. IN. WEB. PROGRAMMING
https://www.youtube.com/watch?v=aoFiw2jMy-0
(Score: 2) by turgid on Saturday December 17, @10:17AM (5 children)
Yes, I've stuck as close as I can to C but I really need to do this. I have a personal project that I need to do. I've done a bit of research, and I'm looking at Django. I need to learn a bit more Python too anyway, so this looks like it kills two birds with one stone. I could write a GUI for my program in native code using a FOSS GUI toolkit, and I might do that as well, but it really needs a web interface. I've tried to avoid GUIs too. I've just about managed to stay away from them. The last one I did was in Turbo Pascal 7.0 on Windows 3.1 IIRC. I played about with gtk+ years ago just so I wasn't completely ignorant and I have dipped my toe in to QT (*spit*). I wrote my own graphics code from scratch in C. It rendered objects from linked lists and when you moved the mouse it scanned the lists to see what it was over. Then you could click on things and drag them round. The alpha transparency made for good eye candy.
I refuse to engage in a battle of wits with an unarmed opponent [wikipedia.org].
(Score: 2) by c0lo on Saturday December 17, @12:01PM (4 children)
God have mercy of your soul, you are not only engaging in devil's work but you are walking into a dead end too.
I have stepped into Python recently (because FreeCAD and whatnot). It's sticky and stinks to heavens.
And its performance it's abysmal.
I'll switch to "scripting" FreeCAD in C++. Or, I don't know, maybe embed a Lua interpreter in a fork
https://www.youtube.com/watch?v=aoFiw2jMy-0
(Score: 2) by turgid on Saturday December 17, @12:15PM (2 children)
The more I learn about Python the less I like it. It's showing its age and coming up against its limitations. However, like death and taxes, C++ and Microsoft Windows, it's an inevitability in today's world so I'd better learn some. I've already made my first lissdecksick tripo on the tutorial and had to google it. Yes, someone else has also made a similar typo. I don't believe in copying-and-pasting straight out of the examples since the brain seldom takes the information in on the way past.
I refuse to engage in a battle of wits with an unarmed opponent [wikipedia.org].
(Score: 2) by c0lo on Saturday December 17, @01:04PM (1 child)
Get serious, turgid. Python is used for prototyping and verifying some ideas quickly. This is why academics love it - but then, they only need to publish some papers and then they can forget about those quick-and-extremely-dirty scripts.
Or admin scripting - yeah, I have to admit, (ba/z/)sh scripting would be even lower in my preferences.
https://www.youtube.com/watch?v=aoFiw2jMy-0
(Score: 2) by turgid on Saturday December 17, @01:49PM
Some years ago I taught myself shell (bash) scripting to a reasonable standard, what with it being so useful. I use bash scripts quote a lot since they are a cheap and powerful way to automate stuff.
I've recently been dealing with C code written by Python coders. It has been eye-opening.
Regarding web development, I have been wondering whether the whole WWW thing in general has become so baroque and over-complicated that it needs to be burnt down and replaced with something much simpler.
I refuse to engage in a battle of wits with an unarmed opponent [wikipedia.org].
(Score: 1) by khallow on Sunday December 25, @08:21AM
(Score: 0) by Anonymous Coward on Sunday December 18, @07:04AM (16 children)
I'm confused by this part here:
Which is it that you have a problem with: dynamic languages (perhaps you mean dynamically-typed?) or interpreted implementations (n.b. there is no such thing as a interpreted language), or both? Or is the implicit/explicit or strong/weak (probably not since you apparently write in C just fine) or some other aspect of the type system.
That said, I think the best place to start is to understand what architectures, structures, styles, languages, implementations, features, libraries, etc. you do like and then move from there.
(Score: 2) by turgid on Sunday December 18, @10:26AM (15 children)
I'm always making typos, and I find it really hard to see them. My brain seems to automatically correct the garbled spelling. So, for example. when I am shell scripting, I will often misspell a variable name and mayhem will ensue (such as paths missing sections or comparisons having syntax errors).
The other problem I have is that my crazy mind juggles with words and numbers. So if I write a function such as thingy_foo_get() I will inevitably try to call it as thingy_get_foo(). Obviously, a compiler make finding these stupid mistakes much quicker.
I have written my own tools to write boilerplate code which help me to go much faster, eliminating the unpleasant drudge work and reducing the scope for mistakes.
When I was younger I was really good at Maths, but I never got 100% on a test. It would seem that doing the algebra and calculus was fine, but when it came to working out the numbers, I'd swap some of the digits! I struggle with some spellings to: bureaucracy vs. beauty. Is it beaurocracy? No.
I refuse to engage in a battle of wits with an unarmed opponent [wikipedia.org].
(Score: 0) by Anonymous Coward on Monday December 19, @05:23AM (14 children)
I think I understand. So the problem isn't so much the aspect of the language or implementation, but the linting for uninitialized and unused variables. If that is a reoccurring problem, I can see why you'd prefer compiled implementations since they usually spot that for you. If that is the case, I think you are really only limited to those with a good linting and testing environment. BTW, A trick I use in a number of languages is to add a test module that opens various child processes to do the linting and then pass/fail based on the exit code.
So what features do you like in a language? From what I've seen, you have specifically mentioned C, Python, Haskell, Ruby, Java, Pascal, bash, and maybe another I missed. But you haven't really specified restrictions other than: 1) Must allow you to make a web interface; 2) must allow you to connect to an as-of-yet-unspecified database technology; 3) must have linting support for uninitialized or unused variables; 4) must allow you to create your unspecified idea; 5) Will be executable on as many client machines as possible; 6) "simple," however that is defined; 7) powerful, however that is defined; 8) Won't bug you too bad when learning to use it, however that is delineated. Many (most?) "big" languages have an implementation and library that meets those specifications, so without more to go on the answer is basically "all of the above." There are options all over such as Erlang, Elixir, Haskell, C, C++, C#, F#, Ruby, Python, Java, Prolog, Javascript, R, Go, PHP, Perl, Raku, Scheme, Scala, and more. Without more about what you like in a language, an implementation, an architectural pattern, a data structure, etc., it is hard to say what will serve you best.
(Score: 2) by turgid on Monday December 19, @07:12AM (13 children)
I'd really like to have an excuse to write something in Lisp. This is a personal project. However, to be useful to other people in my career, I need to learn some Python. It's very common in industry these days. I know that there are linting tools for Python. I believe there's something called Flake8 and I've seen one or two other things.
The web gui has to be able to do drag-and-drop, purely within the browser window. Like I said, on the other side of the web server between it and the database can be anything. This is going to be a really simple application. It's a personal project.
I refuse to engage in a battle of wits with an unarmed opponent [wikipedia.org].
(Score: 0) by Anonymous Coward on Monday December 19, @09:14AM (12 children)
There are two responses I can give to this. Before I do that, I think that it is worth mentioning that every web browser out there should support the drag and drop events. It is relatively easy to design your UI with a WYSIWYG tool, add a few lines of JS, the proper event on the right elements, and have a working web GUI. Once you figure it out, it isn't much different than using a GUI designer for an OS. The real key is making the browser requests in the JS make sense to the mental model you have for your backend. Depending on exactly what you have in mind, using full-blown web framework may be overkill.
For the backend, I see you with two options. The first is to continue the Python route. It has a good standard library, linters/testing (flake8, mypy, unittest, hypothesis), and community. You can choose a number of frameworks (django, flask, aiohttp, bottle) or use the built-in web server (if you are careful). There are pros and cons to the various routes you go, but I'd be especially weary of bringing in something as large as the django ecosystem if you don't have to. There are plenty of alternatives, including using the standard library alone. However, I'm not sure if a web backend is the best way to learn Python anyway.
On the other side, I'd consider a BEAM-based language. First is that you can have most of the Lisp experience with LFE. It has almost the full capabilities of CL but also the power of the Erlang standard library, OTP's additions, and BEAM's built-in tools. Erlang itself is a great option, especially with the OTP implementation's built-in production web server, as long as your mental model of events matches their message-based or pattern-matching system. If Erlang is too restrictive for you, you could try Elixir, possibly with Phoenix. Clojure is a choice too, but then you are dealing with the good and the bad of the JVM (where is DannyB when you need him?). CL itself could work, but would be a bit more work to get going outside of using a framework (but there are a number of options to choose from). Regardless, all of them have professional-level tooling, implementations, and a great community.
(Score: 2) by turgid on Monday December 19, @08:29PM (11 children)
So for the web front end I need to do it in Javascript?
I refuse to engage in a battle of wits with an unarmed opponent [wikipedia.org].
(Score: 0) by Anonymous Coward on Tuesday December 20, @12:39AM
It depends on what you are targeting and what you are exactly envisioning. If you wanted to, you could write it in a language that compiles to WebAssembly but that is probably overkill. The JavaScript itself needed for the lowest level of drag-and-drop functionality that interfaces with a backend is less than a dozen lines total. The browser's DOM engine does the real work and the JS is just to enable drag-and-drop and to do the XMLHttpRequest() on the ondrop event to signal the backend of the drop. If you look at an example online, I think you'll see what I mean. If you like, I could write an example frontend and backend for you. Shouldn't take too long to bang out.
(Score: 0) by Anonymous Coward on Tuesday December 20, @02:23AM
I forgot to mention, there are a number of web frameworks that will generate the necessary JavaScript or WebAssembly for you as well. Depending on exactly what you are going for, you may find that a benefit or a burden. But the underlying code (that the browser will actually use) would be similar and quite short.
(Score: 0) by Anonymous Coward on Wednesday December 21, @11:05PM (1 child)
Tell you what. An example will be more informative than vague gesturing. I'll code both of the standard types of frontends and see if I can post them here for you to illustrate them for you.
(Score: 2) by turgid on Thursday December 22, @07:35AM
Thanks, that's really helpful. I'll hopefully be able to try them out later today.
I refuse to engage in a battle of wits with an unarmed opponent [wikipedia.org].
(Score: 0) by Anonymous Coward on Wednesday December 21, @11:37PM
import faulthandler
from http.server import BaseHTTPRequestHandler, HTTPServer
from random import sample
from socketserver import ThreadingMixIn
from threading import active_count, Thread, RLock
from time import sleep
from urllib.parse import parse_qs
ADDRESS = ""
PORT = 12345
class GameHTTPServer(ThreadingMixIn, HTTPServer):
block_on_close = True
daemon_threads = False
current_game = None
game_lock = RLock()
class GameRequestHandler(BaseHTTPRequestHandler):
def log_message(*_args, **_kwargs):
pass
def do_GET(self):
if self.path != "/":
self.send_error(404)
return
with self.server.game_lock:
if self.server.current_game is None:
self.server.current_game = SwitchGame(3, 3)
self.send_game(self.server.current_game, with_HTML=True)
def do_POST(self):
if self.path != "/update":
self.send_error(404)
return
length = int(self.headers["Content-Length"])
raw_data = self.rfile.read(length)
as_dict = parse_qs(raw_data.decode())
with self.server.game_lock:
game = self.server.current_game
if game is not None:
game.swap(as_dict)
self.send_game(game, with_HTML=False)
else:
self.send_error(500)
def send_game(self, game, with_HTML):
board = BODY_HTML_BOARD.format_map(game.as_dict)
if with_HTML:
body = BODY_HTML_HEAD + board + BODY_HTML_TAIL
else:
body = board
content = body.encode("utf-8")
self.send_response(200)
self.send_header("Connection", "close")
self.send_header("Content-Type", "text/html; charset=utf-8")
self.send_header("Content-Length", str(len(content)).encode())
self.end_headers()
self.wfile.write(content)
self.close_connection = True
def main():
faulthandler.enable()
server = GameHTTPServer((ADDRESS, PORT), GameRequestHandler)
Thread(target=server.serve_forever).start()
try:
while active_count() > 1:
sleep(0.5)
except KeyboardInterrupt:
print()
Thread(target=server.shutdown()).start()
# CPython will wait for remaining threads then exit
class SwitchGame:
def __init__(self, width, height):
total = width * height
pieces = sample(range(1, total + 1), k=total)
count, board = 0, {r: {} for r in range(height)}
for r in range(height):
for c in range(width):
board[r][c] = pieces[count]
count += 1
self.board = board
@property
def as_dict(self):
ttbr = {}
for r in range(len(self.board)):
row = self.board[r]
for c in range(len(row)):
ttbr[f"n{r}x{c}"] = row[c]
return ttbr
def swap(self, target_map):
old = target_map["old"][0].strip("d")
new = target_map["new"][0].strip("d")
old_row, old_col = old.split("x")
new_row, new_col = new.split("x")
old_row, old_col, new_row, new_col = int(old_row), int(old_col), int(new_row), int(new_col)
print("Swapping", self.board[old_row][old_col], "with", self.board[new_row][new_col])
self.board[old_row][old_col], self.board[new_row][new_col] = self.board[new_row][new_col], self.board[old_row][old_col]
BODY_HTML_HEAD = """<!DOCTYPE HTML>
<html>
<head>
<title>Drag and Drop example by replacing body.</title>
<script>
function allowDrop(event) {
\tevent.preventDefault();
}
function drag(event) {
\tevent.dataTransfer.setData("text", event.target.id + " " + event.target.innerHTML);
}
function drop(event) {
\tevent.preventDefault();
\tvar oldData = event.dataTransfer.getData("text").split(" ");
\tvar newData = [event.target.id, event.target.innerHTML];
\tvar request = new XMLHttpRequest();
\trequest.onreadystatechange = function() {
\t\tif (this.readyState == 4 && this.status == 200) {
\t\t\tdocument.body.innerHTML = this.responseText;
\t\t}
\t};
\trequest.open("POST", "/update");
\trequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
\trequest.send("old=" + oldData[0] + "&new=" + newData[0]);
}
</script>
</head>
<body>
"""
BODY_HTML_BOARD = """<table>
<tr>
<td><div id="d0x0" draggable="true" ondragstart="drag(event)" ondrop="drop(event)" ondragover="allowDrop(event)">{n0x0}</div></td>
<td><div id="d0x1" draggable="true" ondragstart="drag(event)" ondrop="drop(event)" ondragover="allowDrop(event)">{n0x1}</div></td>
<td><div id="d0x2" draggable="true" ondragstart="drag(event)" ondrop="drop(event)" ondragover="allowDrop(event)">{n0x2}</div></td>
</tr>
<tr>
<td><div id="d1x0" draggable="true" ondragstart="drag(event)" ondrop="drop(event)" ondragover="allowDrop(event)">{n1x0}</div></td>
<td><div id="d1x1" draggable="true" ondragstart="drag(event)" ondrop="drop(event)" ondragover="allowDrop(event)">{n1x1}</div></td>
<td><div id="d1x2" draggable="true" ondragstart="drag(event)" ondrop="drop(event)" ondragover="allowDrop(event)">{n1x2}</div></td>
</tr>
<tr>
<td><div id="d2x0" draggable="true" ondragstart="drag(event)" ondrop="drop(event)" ondragover="allowDrop(event)">{n2x0}</div></td>
<td><div id="d2x1" draggable="true" ondragstart="drag(event)" ondrop="drop(event)" ondragover="allowDrop(event)">{n2x1}</div></td>
<td><div id="d2x2" draggable="true" ondragstart="drag(event)" ondrop="drop(event)" ondragover="allowDrop(event)">{n2x2}</div></td>
</tr>
</table>
"""
BODY_HTML_TAIL = """</body>
</html>
"""
if "__main__" == __name__:
main()
(Score: 0) by Anonymous Coward on Wednesday December 21, @11:41PM
< self.send_game(game, with_HTML=False)
< else:
< self.send_error(500)
---
>
> self.send_response(204)
> self.send_header("Connection", "close")
> self.end_headers()
> self.close_connection = True
117c119
< <title>Drag and Drop example by replacing body.</title>
---
> <title>Drag and Drop example by mirroring actions.</title>
130a133,134
> \tdocument.getElementById(newData[0]).innerHTML = oldData[1];
> \tdocument.getElementById(oldData[0]).innerHTML = newData[1];
132,136d135
< \trequest.onreadystatechange = function() {
< \t\tif (this.readyState == 4 && this.status == 200) {
< \t\t\tdocument.body.innerHTML = this.responseText;
< \t\t}
< \t};
(Score: 0) by Anonymous Coward on Thursday December 22, @12:15AM (4 children)
So I attached two different versions (they are very rough, ugly, and racy. But they do work). The first is the style of frontend that sends the events to the server via XHR and then replaces the affected elements based on HTML send by the backed. In this case, I did it the lazy way and just sent the entire body all over again. Usually, you'd have a div or another element further down the DOM tree to replace. The second version can be generated with `patch` from the diff. It represents the second style where the frontend changes the DOM itself and sends updates to the backend via XHR. Each has its pros and cons. In your case, you'd probably be more comfortable with the first style, but the second style isn't uncommon because it is the right choice in the right circumstances.
(Score: 2) by turgid on Thursday December 22, @05:39PM (3 children)
That's great! I have tried the first implementation and it works very well.
I refuse to engage in a battle of wits with an unarmed opponent [wikipedia.org].
(Score: 0) by Anonymous Coward on Saturday December 24, @02:30AM (2 children)
At least one of us likes it. As written, it is dual tier, three layer, but the separation isn't great because it should be closer to a five layers in its architecture to be accurate to how your end result will be. I don't know how much you know about GUI and multi-tier/multi-layer design, but with the right architecture, you should be able to have clean separation of the parts and be able to unit and integration test the way you like with ease. Feel free to comment if you have questions; I'll check here for answers intermittently.
(Score: 2) by turgid on Saturday December 24, @10:05AM (1 child)
I have some books on architecture that I have been reading, including Clean Architecture by Uncle Bob. I want to come up with a good design. But actually having a nice, clear, simple example of the basic functionality, such as the one you have given, is very helpful. This has saved me a lot of time already, trying to figure out what things like Django and Flask do. We seem to live in a world of many mysterious layers of "frameworks" which are black boxes and you're expected to "just use them" without understanding what they're actually doing. That makes me very uncomfortable. If I don't understand what all the parts do, then I can't reasonably design a coherent system or fix problems in it later.
My Python isn't great (I'm a beginner there) but after reading your code a few times it's starting to make sense, and that in itself is really helpful. I might try rewriting it in something else to show that I have properly understood it. Maybe! I'm on holiday now, so let's see if I can stay away from the Christmas whisky!
I refuse to engage in a battle of wits with an unarmed opponent [wikipedia.org].
(Score: 0) by Anonymous Coward on Tuesday December 27, @07:59AM
For the backend frameworks, I think it is easiest to understand how they work when you understand that they are really just another step on of the concept of gateway interfaces. You see, back in the day there were two ways for a server to do dynamic pages: doing it themselves or executing programs and sending back what they gave you. The quintessential example of the former is server-side includes (SSI) and the latter is common gateway interface (CGI). Because CGI was, arguably, the first technology to do so, had support in the major web servers, and had an open specification in the form of an RFC, it became widespread. Later iterations of *GI and similar technologies copy many of the concepts and ideas from the former for both compatibility and familiarity. I think if you want to really get your head around what the various web frameworks are doing, it might be helpful to learn how CGI works and how an implementing child process would need to operate in that environment with the available interfaces. Then it is easier to understand how various frameworks and server technologies emulate a CGI-like environment in various ways.
And there is always the option of forgoing a framework. For a local program, a basic web server implementing GET and POST on hard-coded paths may be easier to understand. There are ones you can include in your program available for many languages. You could even implement it yourself. Its not terribly difficult to do as long as you stick to the basics and already have a decent understanding of socket programming.
(Score: 2) by Mojibake Tengu on Sunday December 18, @09:10AM (1 child)
https://hackage.haskell.org/package/mohws [haskell.org]
https://hackage.haskell.org/package/mysql-haskell [haskell.org]
https://hackage.haskell.org/package/mssql-simple [haskell.org]
Though I prefer PostgreSQL13 for data store just because that version has best ecosystem on FreeBSD (most languages can do client), is easily accessible from LibreOffice and, most importantly, can use pgadmin3 clickety as data designer.
Disclaimer: I do not use haskell for data, actually. I wrote my own accounting by hand in awk producing output forms in pure TeX for printing by sending compiled .pdf to a network printer using netcat...
That works for me perfectly on any machine even without a GPU. That's because once I had a GPU failure on a production system for accounting, and no X no GUI no Web browser for more than a week incident really happened to me. Previous version of said accounting was also written by me, in C++ as KDE3 database application, while KDE3 was later mutilated to death by Gentoo terror ideology. I will not make such error twice.
Now, excuse me, I am back to assembly.
The edge of 太玄 cannot be defined, for it is beyond every aspect of design
(Score: 2) by turgid on Sunday December 18, @10:29AM
I was intending to go with PostgreSQL for the database eventually. I took a diversion yesterday getting it set up. Using something like Haskell would be cool. I was toying with the idea of some kind of Lisp for the application. I'll be developing on Slackware and if I ever get something worth sharing with the world, I'll do an Ubuntu port, I think.
I refuse to engage in a battle of wits with an unarmed opponent [wikipedia.org].
(Score: 2) by acid andy on Thursday December 22, @01:04AM
1. Install Slash.
2. PROFIT.
3. Run( )away.
4. Point and laugh.
5. Use your new found knowledge to take on development of SoylentNews in your (also new found) spare time.
;)
Master of the science of the art of the science of art.