app/
iam/
orgs/
models.xyz
roles/
models.xyz
Then any editor with file search can produce the type-based structure on demand.1,130 karma · joined May 28, 2012
app/
iam/
orgs/
models.xyz
roles/
models.xyz
Then any editor with file search can produce the type-based structure on demand.I never even heard about this.
https://www.digitalocean.com/blog/smtp-restricted-by-default
class Author(Model):
books = relationship(..., lazy='select')
fetch_authors = select(Author).options(raiseload(Author.books))
Anything that gets its Authors with fetch_authors will get instances that raise instead of doing a SELECT for the books. You can throw that in a smoke test and see if there's anything sneaking a query. Or if you know you never want to lazy-load, relationship(..., lazy='raise') will stop it at the source.https://docs.sqlalchemy.org/en/20/orm/queryguide/relationshi...
I'm genuinely sorry if it's frustrating, hopefully not, but I'm exiting this conversation now.
Though some would still prefer the tube. That's up to them.
Service tubes and jack points and JTAG and whatever ship in physically assembled artifacts because they have to, not because they should.
But why?
Maybe there's some fundamental difference of opinion here, because I'm thinking definitely not, no way, test-only code paths should never reach prod, and code that ships should never even know that testing is a thing that happens.
I'm seeing three main points in this advice:
1. Don't misuse mocks. (Mocks, fakes, whatever.)
2. Write testable, well-factored code with pluggable dependencies.
3. Don't use mocks at all. Use "nullables" instead.
I'm totally on board with (1) and (2) as good advice in general, no matter how you're testing. But (3) seems like an independent choice. What benefit does (3) specifically deliver that you wouldn't get with (1) and (2) and mocks instead of nullables?
I'm ready to admit I'm missing something, but I don't see it.
I'm sort of tolerant of bits of Session leaking into things. I'd argue that its leaking pieces are the application-level things you'd implement, not versions of them from the lower layers that you need to wrap.
When users filter data and their filters go from POST submissions to some high-level Filter thing I'd pass to a repository query, what does that construct look like? Pretty much Query.filter(). When I pick how many things I want from the repository, it's Query.first() or Query.one(), or Query.filter().filter().filter().all().
Yes, it's tied to SQL, but only in a literal sense. The API would look like that no matter what, even if it wasn't. When the benefit outweighs the cost, I choose to treat it like it is the thing I should have written.
It isn't ideal or ideally correct, but it's fine, and it's simple.
SQLAlchemy is flexible, though. You can map a Person from three tables if you need to. It's a data mapper, then a separate query builder on top, then a separate ORM on top of that, and then Declarative which ties them all together with an ActiveRecord-ish approach.
> I prefer not to use ORMs but just write queries and map the results into value objects. That work I would put into a Repository.
Yep, I hear ya. Maybe if they'd built on top of something lower-level like stdlib sqlite3, it wouldn't be so tempting to dismiss as YAGNI. I think my comment sounded more dismissive than I really meant.
That's aside from their particular example of SQLAlchemy sessions, which is extra weird because a Session is already a repository, more or less.
I mean, sure, there's a difference between your repository for your things and types you might consider foreign, in theory, but how theoretical are we going to get? For what actual gain? How big of an app are we talking?
You could alias Repository = Session, or define a simple protocol with stubs for some of Session's methods, just for typing, and you'd get the same amount of theoretical decoupling with no extra layer. If you want to test without a database, don't bind your models to a session. If you want to use a session anyway but still not touch the database, replace your Session's scopefunc and your tested code will never know the difference.
It's not a convincing example.
Building your repository layer over theirs, admittedly you stop the Query type from leaking out. But then you implement essentially the Query interface in little bits for use in different layers, just probably worse, and lacking twenty years of testing.
Imagine how you'd rush to justify your lack of it here.
Everything you'd say in your defense would equally explain why someone wouldn't know the meaning of a certain unmarked number in the output of a random shell command.
So every time it proves it can, they move the goalposts and invent a truer Scotsman who can never be replaced by a machine. Because they know it can't do person things. It's a machine.
I'm comfortable with saying that this way of answering the question doesn't work, because the argument is simply, "How can it be false if I believe it's true?"
It doesn't.
But if you feel they're magic, you'll never believe that a "random" mechanical process, which you think you can interrogate, could ever really have that spark.
Hah. That sounds like just... debugging.
Maybe I'm wrong, I don't know.
I'm just a person who keeps hearing, from every direction, "Won't someone please think of the assholes?"
---
Edit: My comment above used to say what was quoted. I changed it to be more precise about my issue with the comment I replied to.
Bad actors acting in bad faith, causing damage? Well... you know, it's just how they are... They have a right... to... Who's to say they're really bad? You know? I mean just look at that guy over there. What about him?
Good actors, fed up, responding in a way that doesn't cut the willful hostiles every bit of slack you can imagine, which potentially could maybe cause a little bit of damage, which would stop as soon the attack was over? Punish them, they'll ruin everything.
Shelling out is not the only option. People are just saying not to use that option. Better ones won't save you if you purposely do something stupid. They will save you if the user wants to trick you into doing something else.
run("program --option '{user_input}' > file")
to save some input to a file, and the user's input is: '; bad_command #
then when run() sends that string to the shell, the shell will run: program --option '';
bad_command #' > file
Most languages have something like a safe_exec() that separates the shape of the command from the values of the options, executing "program" with the options and the user_input in the arguments array as data. Skipping the shell step, which would just be building an exec call anyway, removes the opportunity for users to confuse it into doing something else.The list-based API alternative they recommend might look like this:
safe_exec(["program", "--option", user_input], stdout="file")
and it would always exec "program" with argv[1] == "--option" && argv[2] == user_input. If the user_input happens to be: '; bad_command #
...well, then, the user can enjoy the contents of their file.If an interested and reasonably savvy person can't get a program to work as it claims, the problem is the program, not the user.
Git is like that Brooks quote became software. "Show me Git commands and I'll continue to be mystified; show me history and I won't need your commands."
The official book on their website goes through most of Git like this, if you want to know more. It's really night and day once you know what it's actually doing.
A --- B --- C <- [main]
\
X --- Y --- Z <- [feature]
into this: A --- B --- C <- [main]
\
X --- Y --- Z' <- [feature]
\
Z
The old commit is not destroyed, just taken off the path you walk from "feature" back in time. Even if you `rebase -i main` on "feature" and drop Y and Z, they'll still be around, just not in your branch: A --- B --- C <- [main]
\ \
\ X' <- [feature]
\
X --- Y --- Z
If you're worried about rebase going bad, before you start, create a temporary branch (git checkout -b before-risky-rebase; git checkout -) to mark that line of history where "feature" points at the good state. A --- B --- C <- [main]
\
X --- Y --- Z <- [feature,before]
If anything goes wrong that `rebase --abort` doesn't fix, get out of there somehow and `git checkout before-risky-rebase`, or, on "feature," `git reset --hard before-risky-rebase`. Here, the backup branch "before" is still what "feature" was before the rebase: A --- B --- C <- [main]
\ \
\ X' --- (bad) --- (oops) <- [feature]
\
X --- Y --- Z <- [before]
As long as you don't force-push anything, it doesn't really matter if you damage the now-broken branch even more while getting out. Reset "feature" to your backup and it never happened. You can even damage "main" and still `reset --hard` to origin/main (if you have one) or the tip "main" had before you broke it.Even if you don't remember to create the backup branch, the hashes of your old commits now bypassed are still in the reflog. You can always find the hash where "feature" used to point and manually move the pointer back:
git checkout feature
git reflog
(find the hash)
git reset <hash> # --hard or --mixed if needed
Not that this is obvious or trivial or anything. It shouldn't be this hard. But your commits are safe from almost any way you might destroy them once they're somewhere in your history, at least until unreachable commits are eventually garbage-collected.When people with that kind of worldview roll their eyes at empathy, or scoff at any need to see beyond their own opinions[0], they are all but guaranteed to seal themselves inside.
FOSS, decentralized, et al. attract a lot of people with those worldviews, and that story is the story of them failing with consumers over and over and over, doubling down on what failed every time.
Seems like there's domain-specific terminology (almost wrote "language"!) I'm using accidentally or incorrectly and making a mess. I might have stepped in it with "model." Mainly I just think I see a difference between ways of verifying where the work is doing and the work is being. But it sounds like I don't know enough to have this conversation.
Thanks for humoring me, though.
It reminds me of the question about whether programming is math. Depends on what you think you're doing. Are you doing things with state to get results, or building a system of facts that results in the answer?
When combined you get a fundamental metaproblem that:
1. You can't solve a problem you don't understand.
2. Moralizing is more satisfying than understanding.
3. This is a problem, which can't be solved if people choose to moralize instead of understanding.