Monday, December 18, 2023

Rust - Read a Single Keypress from the Keyboard, ala 'getch'

Rust - Read a Single Keypress from the Keyboard, ala 'getch'

How to Read a Keypress from the Keyboard in Rust, similar to getch()

Kent West - kent.west@{that mail that swore to do no evil}

I struggled for days to figure out how to get a simple keypress from the keyboard. Finally! I found a crate on crates.io that makes it as simple as using "getch()" from the ncurses library.

The reason I did not use ncurses is because it requires an initialization step, which swaps out the screen for a new blank screen, which then disappears when you're finished with the curses mode.

I would have used the termion crate, but search high and low on the 'Net for a simple snippet of code that shows just the absolute bare minimum for getting a keypress, and you'll get a headache, and no solution, unless you're smarter than I am (granted, that's not a very high bar, but still...) I do not understand why coders will not give the simplest, barest-possible, COMMENTED!!!! code snippets so the newbie can get started. (Yes, I'm angry about this; Rust could be such a beautiful platform, if non-life-long-coders could learn how to code in it from simple examples and good explanations.)

Here's how to get a keypress from the keyboard in Rust:

Configure 'Cargo.toml'
$ cargo add getchar
src/main.rs
fn main() {
    let keypress = getchar::getchar().unwrap(); // getchar() returns an "Option<char>" type that must be unwrapped to get to the 'char' goody inside.

    println!("You pressed {}", keypress);
} // end of main()

Saturday, November 11, 2023

Connect to Google Drive using "rclone" on Debian

  1. Install "rclone"

    1. # apt install rclone

  2. Use "rclone" to configure a "remote" for each connection. For example, if you have a work Google account and a personal Google account, you'd need a "remote" for each connection you want to use. Don't worry about making mistakes during this configuration; you can always delete the configuration and start over if need be. As the user with the Google Drive account:

    1. $ rclone --config
    2. Assuming you don't already have a "remote" (you don't, or you wouldn't be following this set of instructions), choose n for "New remote".
    3. The name for the new remote might be something like "GDrive" or "gdrive-work" or "my-personal-google-drive", etc.
    4. "rclone" can connect to all sorts of services; it now gives you a list of services to select from. This list changes from time to time, so don't rely on remembering the number for next time you need to do this. At the time of this writing, the Google Drive item is number 18, so that's the one I'll select.
    5. Setting the "Google Application Client Id" has some advantages, but it's a pretty complicated process for someone who has never done it before. For now, I'd keep it simple, and bypass this option by leaving this "client_id" option empty.
    6. Ditto the "client_secret".
    7. For "scope", you probably want the first option, "Full access [to] all files", because when you access Google Drive from your Debian workstation, you'll probably want full access to those files.
    8. The "service_account_file" should be left empty (unless you know what you're doing; you don't).
    9. Do not "Edit advanced config".
    10. Yes, do "Use auto config". This will open a web-browser window where you should log into your Google account, and then "Allow" rclone to access your Google account.
    11. Assuming this is not a Shared drive (if it is, you'd know it), select "No".
    12. Yes, keep this remote. This is essentially the "Save" step for the configuration of this remote.

  3. You've now configured your first Google Drive "remote". If you have a second Google account, you can configure it now by repeating the above process, starting at step 2, or you can "Quit config", and come back later to configure other remotes as the need arises. This configuration is saved in a plain-text file as "~/.config/rclone/rclone.conf". Instead of using the "rclone --config" "wizard" as we did above, you could've created this config file by hand, but who wants to do that error-prone process?

  4. "$ rclone listremotes" will list the names of the remote[s] you have configured.

  5. "$ rclone ls gdrive-work:" will list the files on the remote named "gdrive-work". Because this "ls" command traverses the file "tree", you may get a bigger listing than you want, so you may want to ls a smaller directory, like "rclone ls gdrive-work:/todo/Week17". Or if you wanted a tree view, "rclone tree gdrive-work:"

  6. But most likely, you want the drive mounted. There are several ways to accomplish this.

    1. Using "rclone mount"

      $ mkdir ~/GDrive4Work
      $ rclone mount gdrive-work:/ ~/GDrive4Work

      Note that you won't see anything happen, but you can open another terminal window (or file manager, etc) and browse to your mounted directory to access the files.

      Ctrl-C will unmount the drive.

      Alternatively you can background the process with an ampersand:

      $ rclone mount gdrive-work:/ ~/GDrive4Work &

      and when you want to unmount, fg to foreground the process, followed by Ctrl-C.

      Alternatively, you can mount it in daemon mode:

      $ rclone mount gdrive-work:/ ~/GDrive4Work --daemon

      and then unmount with:

      $ fusermount -u ~/GDrive4Work

      (you can also use umount instead of fusermount -u, but the documentation says this is unreliable)

      We probably want this mounting to take place at login of the Drive's owner.

      Make a little script and put it where you can access it. I have a ~/bin directory for this purpose. Name the script something like "mount-GDrive.sh", and put the following into it:

      #!/bin/bash

      # Test if GDrive is mounted, and if not, mount it
      mountpoint -q /home/[your user]/GDrive4Work || rclone mount gdrive-work:/ /home/[your user]/GDrive4Work --daemon

      Save the file, and make sure it's executable:

      $ chmod +x ~/bin/mount-GDrive.sh

      Then place a call to this script at the bottom of your ~/.bash_profile file:

      ~/bin/mount-GDrive.sh

      If you don't already have a ~/.bash_profile you can create it, but make sure to source the ~/.bashrc file by adding to the top of ~/.bash_profile this line:

      . ~/.bashrc

      (There's a space after the first dot.)

      Now, reboot your system and make sure it all works.

    2.  Using an /etc/fstab entry

      This method is probably not the best, because it will mount the drive at boot-time, regardless of the Drive's "owner" being logged in or not. Also, the mount will not be owned by the "owner", unless special care is taken in the /etc/fstab file. The following is here just for documentation purposes; you're probably better off using the first method above.

      You can add a line to /etc/fstab like the following:

      gdrive-work:/ /home/[your user]/GDrive4Work rclone rw,noauto,nofail,_netdev,x-systemd.automount,args2env,vfs_cache_mode=writes,config=/home/[your user]/rclone/rclone.conf,cache_dir=/var/cache/rclone 0 0

      If you try to mount the drive now with:

      # mount /home/[your user]GDrive4Work

      you'll get a message that rclone is not a known type, and that fstab has been modified, but that you need to reload the systemd daemons. Nowadays /etc/fstab is really just a backwards-compatible configuration source for systemd. When you reboot, systemd will read this file and convert your added line to a systemd unit. Currently this line has not yet been added to systemd. You can verify this with the command:

      # systemctl list-units | grep -i "gdrive"

      Notice this command is run as "root" (you can also use "sudo" instead). This command should find no units containing the name "gdrive".

      "systemctl" is kind of like the control command for systemd, and we're telling it to list the units that systemd knows about, piping ("|") the results through "grep" to display only those lines that contain the phrase "gdrive", regardless of capitalization ("-i"). If you leave off the | grep -i "gdrive", you'll get a list of all the units that systemd knows about.

      These units are typically stored in /usr/lib/systemd/system (for system-installed units that usually should not be tinkered with) and /etc/systemd/system (for locally-configured units). You can also ls in those directories to verify there are no "gdrive"-related units there.

      But we don't have to wait for a reboot to let systemd know about the changes to fstab. We can just reload the daemons with:

      # systemctl --daemon-reload

      But that still doesn't work, because the old mount command doesn't recognize the "rclone" type (the systemd units still weren't created. But this is easy to fix: we just need to create a symlink, like so:

      # ln -s /usr/bin/rclone /sbin/mount.rclone

      Reload the systemd daemons again:

      # systemctl --daemon-reload

      and now you can mount (as your normal user, not 'root') your "remote":

      $ mount /home/[your user//GDrive4Work

      You should now be able to access your Google Drive from the command-line or from any of your usual file managers, etc. Be aware though, that a reboot will mount the drive as "root", and your normal user probably will no longer have access to the Drive.

      While the drive is mounted, you can also see that systemd knows about this "unit":

      # systemctl list-units | grep -i "gdrive"

      ... even though there still isn't a unit file in the two unit-file directories mentioned above.

      $ ls -R /etc/systemd/system | grep -i gdrive
      $ ls -R /usr/lib/systemd/system | grep -i gdrive


      I was thinking this method created a unit-file automagically in /lib, which you could them move to /etc and tailor to your needs, but apparently I'm wrong about that. I was then going to write in the next section about using a systemd unit to mount the Drive, but after I wrote most of this I realized it also would mount the Drive at boot-up, regardless of the owner being logged in, unless more hoops were jumped through to get all the t's crossed and all the i's dotted.

    3. Other methods such as a cron job to mount the drive on login, etc, but the above methods are probably your best bet.

Saturday, October 14, 2023

Understanding "Church"

I normally don't like to link to outside content for fear of bit-rot, but this article is worth pointing to, and I don't want to just copy/paste the contents here and potentially take away from their visits.

https://woodlandhillsnashville.com/topical/understanding-church/understanding-church.html

Saturday, September 23, 2023

 

Nathan Cox had recommended a video. It's a good lesson, but with bad audio that made it very hard for me to follow. However, I made notes that I believe got the gist of the presentation, and share them here.
 
 https://www.youtube.com/watch?v=9SJ4h8qy5mg

A lot of guys preach to have a job.
 
My job's to stir up the ant hill. And leave town.
 
Before becoming a Christian, I was an atheist.
 
A concept I was impressed with was "Calling Bible things by Bible Names", which comes out of 1 Cor 2, "words taught by the Holy Spirit".
[
13 We also speak these things, not in words which man’s wisdom teaches, but which the Holy Spirit teaches, comparing spiritual things with spiritual things.
]
 
He calls it an "immersistry" instead of a"baptistry".
 
He was asked if he used a piano. Saying "yes", he was challenged to look into the scriptures. They quit using the piano until they could do some research. He studied the Greek, etc, and came to conclude that when somebody says, "The Greek says", you better be wary.
 
Talking to a CoC preacher, he wondered why it was okay for the preacher's young girls to listen to instrumental "Crocodile Rock", but a stringed instrumental version of "The Old Rugged Cross" was not okay; it didn't make sense to him.
 
That led to Heb 13:15....
[
15 Through him, then, let’s offer up a sacrifice of praise to God continually, that is, the fruit of lips which proclaim allegiance to his name.
]
 
And 1 Cor 16...
 [
Now concerning the collection for the saints, as I commanded the assemblies of Galatia, you do likewise. 2 On the first day of every week, let each one of you save, as he may prosper, that no collections are made when I come. 3 When I arrive, I will send whoever you approve with letters to carry your gracious gift to Jerusalem. 4 If it is appropriate for me to go also, they will go with me.
]
 
He asked if there was any scripture that supported the idea of a collection in worship for the local church; this passage concerns a special collection for a distant church.
 
Everyone's asking the question about instrumental music in worship; maybe we should ask for the New Testament definition of "worship".
 
"That'd probably be a good question, wouldn't it? If we're gonna call Bible things by Bible names, well then let's, let's get this definition figured out. ... Let the Bible define it."
 
The particular Greek word is proskenueo. Alexander the Great, as Emperor; the Persians bowed their faces to the ground, and Alexander kindda liked that. He tried to get his loyal Macedonian cavalry to do the same thing, and they wouldn't do it, because they thought proskenueo was reserved for the gods, and they knew Alexander was just a man.
 
Heb 11:
[
21 By faith, Jacob, when he was dying, blessed each of the sons of Joseph, and worshiped [proskeneuo], leaning on the top of his staff.
]
 
Patriarchs - Adam-Moses - Worship 1.0
 
Law of Moses - Moses to death of Christ - Worship 2.0
 
Death of Christ forward - New Covenant - Worship 3.0
 
proskeneuo is a physical act, of bowing. This was Worship 1.0
 
Worship 2.0 is an upgrade to become a spiritual act.
 
A Principle of scripture: first the physical, then the spiritual.
 
Worship 2.0 - John 4:24 - The only place a Jew can worship is in the temple, and the only time a Jew can worship is on the feast days. The synagogue meeting on the Sabbath was not for worship, but for reading of the law and the prophets (Acts 13 - 15 After the reading of the law and the prophets, the rulers of the synagogue sent to them, saying, “Brothers, if you have any word of exhortation for the people, speak.”).
 
"And you see that terminology all the way through."
Ex: John 12:20 "They are going up to worship at the feast."
Ex: Acts 8 "[The Ethiopian" had come to Jerusalem to worship."
 
That's why the woman at the well said , "You Jews say we must worship at Jerusalem."
 
Worship 2.0 = at the temple, during feasts; go up and participate in all the doings. The priests serve; the people bow in the presence of the Lord, in the temple, singing the Songs of Ascent as they wind their way up the stairs to the courtyard on the temple mount - "I was glad when they said to me, 'Let’s go to Yahweh’s house!'” - Psalm 122:1).
 
Worship 3.0
 
Worship is the most important thing in life. The "covering cherub"? wanted worship so badly he began a rebellion that is going on still today.
 
The only place that talks about "new covenant worship" is John 4:23-24.
"in spirit and in truth"
 
There's an outer-man and an inner-man. We live according to the inner-man. This is the spirit - the inner-man.
 
What are you going to put your emphasis on? That which is decaying, or that which isn't?
 
We pay a lot of attention to the outer-man, but it's decaying.
 
What are you going to put your emphasis on? What's God gonna put his emphasizes on?
 
The spirit of God lives in our carcass, having cleaned up the inside in order to move in.
 
The veil of the temple was torn, making the temple one room.
 
The Bible reveals to us the realm we can't see, the spiritual realm.
 
We're ushered into the one room of the temple by the blood of Jesus.
 
Heb 10:19 Having therefore, brothers, boldness to enter into the holy place by the blood of Jesus, 20 by the way which he dedicated for us, a new and living way, through the veil, that is to say, his flesh, 21 and having a great priest over God’s house, 22 let’s draw near with a true heart in fullness of faith, having our hearts sprinkled from an evil conscience, and having our body washed with pure water, 23 let’s hold fast the confession of our hope without wavering; for he who promised is faithful.
 
When our bodies are washed, our hearts are sprinkled. We are given immediate, instant, and present access, because we're in the presence of God.
 
Any mental image you have, Ezekiel or whatever, of someone being in the presence of God, what is it? Proskeneuo.
 
"The point is... our inner-person is in perpetual proskuneo in the presence of him who is and was and is to come. And that's Worship 3.0."
 
Can you show me any place in the New Testament where the church game together to worship?
 
I can understand how the Lord's Supper might be considered worship.
 
But there's another word, latreo, and that has to do with "service".
 
In the Old Testament, the priests served, and the people worshiped. So collectively, the people of Israel worshiped and served.
 
King Uzziah gets too big for his britches, and says, "I want to waltz into the temple, and I want to burn incense to the Lord." The priests resisted, but he took the censor in hand and walked across the threshold, and leprosy broke out all over him. The priests hustled him out into isolation, where he remained the rest of his life. [Not quite an accurate retelling, but the gist is correct. 2 Chron 26]
 
The message is that only the priests offer service.
 
Sacrifice is service.
 
<mumble mumble Christians mumble people <mumble> priests mumble>
 
Rom 12:1 Therefore I urge you, brothers, by the mercies of God, to present your bodies a living sacrifice, holy, acceptable to God, which is your spiritual service.
 
<Thanuel's Big Boys? added "worship" ... could do that?> But they did it.
 
"Old King James does the best" at rendering the word for "service".
 
The New Testament priest offers his body in service to God. Everything external is service. Internal is "worship". External is "service". On the inside are "the people"; on the outside are "the priests".
 
<So let me kinks then mumble?> about singing, praying, giving, and Lord's Supper. When we start doing those things, is that going to be "service"? Or is it going to be "worship"?
 
My guess would be, "service". Let's check it out.
 
Heb 13:15 ("which we quoted earlier") 15 Through him, then, let’s offer up a sacrifice of praise to God continually, that is, the fruit of lips which proclaim allegiance to his name.
 
"Sacrifice"; that's what a priest does; that's "service".
 
16 But don’t forget to be doing good and sharing, for with such sacrifices God is well pleased.
 
"Doing the Lord's work" and everything that goes along with it: "service".
 
How about "preaching"? That's one of the so-called five channels of public worship. <mumble where words come from some time.> They come from the same place as "rapture" and "accept Jesus into your heart", is where they come from.
 
Rom 15:15 But I write the more boldly to you in part, as reminding you, because of the grace that was given to me by God, 16 that I should be a servant of Christ Jesus to the Gentiles, serving as a priest of the Good News of God, that the offering up of the Gentiles might be made acceptable, sanctified by the Holy Spirit.
 
See, "preaching" is "service".
 
1 Cor 10:14 Therefore, my beloved, flee from idolatry. 15 I speak as to wise men. Judge what I say. 16 The cup of blessing which we bless, isn’t it a sharing of the blood of Christ? The bread which we break, isn’t it a sharing of the body of Christ? 17 Because there is one loaf of bread, we, who are many, are one body; for we all partake of the one loaf of bread. 18 Consider Israel according to the flesh. Don’t those who eat the sacrifices participate in the altar?
 
Whazzat? <mumble> priest.
 
Eating food sacrificed is a participation in the sacrifice. Personal anecdote about altar in Russia.
 
Heb 13:10 We have an altar from which those who serve the holy tabernacle have no right to eat.
 
My point is that the Lord's Supper is "service". It's a sacrifice participated in by new covenant priests. The old covenant priests are out; they don't get in.
 
We "know"...
- prayer
- singing
- collection
- preaching
- the Lord's Supper
... is worship, don't we?
 How do we "know" that? There's not a verse in scripture that says any of that is "worship".
 
<False formility is a false dot.> False terminology creates false <concepts?>.

Monday, September 18, 2023

The following is written by Brian Zahnd.


I have a problem with the Bible. Here’s my problem…

I’m an ancient Egyptian. I’m a comfortable Babylonian. I’m a Roman in his villa.

That’s my problem. See, I’m trying to read the Bible for all it’s worth, but I’m not a Hebrew slave suffering in Egypt. I’m not a conquered Judean deported to Babylon. I’m not a first century Jew living under Roman occupation.

I’m a citizen of a superpower. I was born among the conquerors. I live in the empire. But I want to read the Bible and think it’s talking to me. This is a problem.

One of the most remarkable things about the Bible is that in it we find the narrative told from the perspective of the poor, the oppressed, the enslaved, the conquered, the occupied, the defeated. This is what makes it prophetic. We know that history is written by the winners. This is true — except in the case of the Bible it’s the opposite! This is the subversive genius of the Hebrew prophets. They wrote from a bottom-up perspective.

Imagine a history of colonial America written by Cherokee Indians and African slaves. That would be a different way of telling the story! And that’s what the Bible does. It’s the story of Egypt told by the slaves. The story of Babylon told by the exiles. The story of Rome told by the occupied. What about those brief moments when Israel appeared to be on top? In those cases the prophets told Israel’s story from the perspective of the peasant poor as a critique of the royal elite. Like when Amos denounced the wives of the Israelite aristocracy as “the fat cows of Bashan.”

Every story is told from a vantage point; it has a bias. The bias of the Bible is from the vantage point of the underclass. But what happens if we lose sight of the prophetically subversive vantage point of the Bible? What happens if those on top read themselves into the story, not as imperial Egyptians, Babylonians, and Romans, but as the Israelites? That’s when you get the bizarre phenomenon of the elite and entitled using the Bible to endorse their dominance as God’s will. This is Roman Christianity after Constantine. This is Christendom on crusade. This is colonists seeing America as their promised land and the native inhabitants as Canaanites to be conquered. This is the whole history of European colonialism. This is Jim Crow. This is the American prosperity gospel. This is the domestication of Scripture. This is making the Bible dance a jig for our own amusement.

As Jesus preached the arrival of the kingdom of God he would frequently emphasize the revolutionary character of God’s reign by saying things like, “the last will be first and the first last.” How does Jesus’ first-last aphorism strike you? I don’t know about you, but it makes this modern day Roman a bit nervous.

Imagine this: A powerful charismatic figure arrives on the world scene and amasses a great following by announcing the arrival of a new arrangement of the world where those at the bottom are to be promoted and those on top are to have their lifestyle “restructured.” How do people receive this? I can imagine the Bangladeshis saying, “When do we start?!” and the Americans saying, “Hold on now, let’s not get carried away!”

Now think about Jesus announcing the arrival of God’s kingdom with the proclamation of his counterintuitive Beatitudes. When Jesus said, “Blessed are the meek, for they shall inherit the earth,” how was that received? Well, it depends on who is hearing it. The poor Galilean peasant would hear it as good news (gospel), while the Roman in his villa would hear it with deep suspicion. (I know it’s an anachronism, but I can imagine Claudius saying something like, “sounds like socialism to me!”)

And that’s the challenge I face in reading the Bible. I’m not the Galilean peasant. Who am I kidding! I’m the Roman in his villa and I need to be honest about it. I too can hear the gospel of the kingdom as good news (because it is!), but first I need to admit its radical nature and not try to tame it to endorse my inherited entitlement.

I am a (relatively) wealthy white American male. Which is fine, but it means I have to work hard at reading the Bible right. I have to see myself basically as aligned with Pharaoh, Nebuchadnezzar, and Caesar. In that case, what does the Bible ask of me? Voluntary poverty? Not necessarily. But certainly the Bible calls me to deep humility — a humility demonstrated in hospitality and generosity. There’s nothing necessarily wrong with being a relatively well-off white American male, but I better be humble, hospitable, and generous!

If I read the Bible with the appropriate perspective and humility I don’t use the story of the Rich Man and Lazarus as a proof-text to condemn others to hell. I use it as a reminder that I’m a rich man and Lazarus lies at my door. I don’t use the conquest narratives of Joshua to justify Manifest Destiny. Instead I see myself as a Rahab who needs to welcome newcomers. I don’t fancy myself as Elijah calling down fire from heaven. I’m more like Nebuchadnezzar who needs to humble himself lest I go insane.

I have a problem with the Bible, but all is not lost. I just need to read it standing on my head. I need to change my perspective. If I can accept that the Bible is trying to lift up those who are unlike me, then perhaps I can read the Bible right.”

Tuesday, July 25, 2023

Old Covenant vs New

As per Jeremiah 31, the difference between the old and new covenants is that the first consisted of God's law written on stone tablets, whereas the second consists of God's law written in the inward parts and in the heart.

Ten Commandments Nailed to the Cross?

It is a common misunderstanding that Col 2:14-17 says that the Ten Commandments were nailed to the cross. But that's not what Col 2:14-17 says. Because of the similarity in wording between this passage and the passage in Ephesians chapter 2, we need to look at both, starting with the Ephesians passage. But before getting to that, let's look at the word "ordinances".

The Word "Ordinances"

The word "ordinances" (dogmasin) in v. Col 2:14 is used in the "New Testament" five times, three of which clearly refer to human-uttered decrees, not God-uttered ones, and two of them being found here and in the sister passage of Eph 2:15. In the Greek Septuagint version of the "Old Testament", this word is only used in the book of Daniel, and only to refer to human decrees, not decrees from God. (A different word, dikaioma, is used ten times in the NT, and refers to God-uttered decrees and righteousness.) Paul knew his Greek, and he knew his Greek scriptures, and it seems odd that he would use the word "ordinances" to refer to God-uttered ordinances when it seems to only be used every where else in the scriptures for human-uttered ordinances.

The Ephesians Passage

WEB Eph 2:11 Therefore remember that once you, the Gentiles in the flesh, who are called “uncircumcision” by that which is called “circumcision” (in the flesh, made by hands), 12 that you were at that time separate from Christ, alienated from the commonwealth of Israel, and strangers from the covenants of the promise, having no hope and without God in the world. 13 But now in Christ Jesus you who once were far off are made near in the blood of Christ. 14 For he is our peace, who made both one, and broke down the middle wall of separation, 15 having abolished in his flesh the hostility, the law of commandments contained in ordinances, that he might create in himself one new man of the two, making peace, 16 and might reconcile them both in one body to God through the cross, having killed the hostility through it. 17 He came and preached peace to you who were far off and to those who were near. 18 For through him we both have our access in one Spirit to the Father. 19 So then you are no longer strangers and foreigners, but you are fellow citizens with the saints and of the household of God, 20 being built on the foundation of the apostles and prophets, Christ Jesus himself being the chief cornerstone; 21 in whom the whole building, fitted together, grows into a holy temple in the Lord; 22 in whom you also are built together for a habitation of God in the Spirit.

If "ordinances" refers to human-uttered decrees rather than God-uttered ones, then Paul is saying here that whatever is abolished is contained in human-uttered decrees.

Whatever was abolished was abolished for the purpose of making one new man out of the two (Jew and Gentile), as well as for the purpose of reconciling that one new man with God. So whatever the hostility is that was abolished, it kept a middle wall of separation between Jew and Gentile. The God-uttered law of Moses (and/or the Ten Commandments) did not create this separation; in fact, the law repeatedly enjoins the Jew to treat the non-Jew as one of their own.

However, the human-uttered Oral Law of the scribes and Pharisees did create this wall between Jew and non-Jew. Some of their ancient documents refer to this wall as a "fence", and interestingly enough, in the Greek of the phrase "middle wall of separation" is the word for "fence'. When Peter visited the non-Jew, God-fearing Cornelius, he opened the discussion by saying it was not lawful for a Jew to visit a non-Jew, but God had made him to understand that God did not agree with this "law"; this law can not be found in the God-uttered scriptures, but it can be found in the human-uttered Oral Law of the ancient Pharisees.

The "law of commandments contained in ordinances" phrase seems to refer to human-uttered Oral Law that separated Gentile from Jew. God's law never made this separation, but the Pharisees with their traditions did make the separation.

By drawing Gentile believers into the commonwealth of Israel, and making them part of the Israelite family of God, this "fence" no longer applied to them. The Oral Law, with its fence, it's middle wall of separation, is untouched, but the people whom it would have affected is no longer affected by it, because they are no longer foreigners.

So in effect, the division, the hostility, the separation, enforced by these human-uttered ordinances was nailed to the cross, breaking down the middle wall of separation between Jew and Gentile. It was not a law, either the Oral Law or the Ten Commandments law, which was nailed to the cross, but rather the division between Jew and non-Jew that was nailed to the cross. This nailing is about peace between Jew and non-Jew, and then peace between that united new man and God; it is not about doing away with a legal system. On the cross, Jesus bore our sins against a legal system, not the legal system itself.

The Colossians Passage

WEB Col 2:11 In him you were also circumcised with a circumcision not made with hands, in the putting off of the body of the sins of the flesh, in the circumcision of Christ, 12 having been buried with him in baptism, in which you were also raised with him through faith in the working of God, who raised him from the dead. 13 You were dead through your trespasses and the uncircumcision of your flesh. He made you alive together with him, having forgiven us all our trespasses, 14 wiping out the handwriting in ordinances which was against us. He has taken it out of the way, nailing it to the cross. 15 Having stripped the principalities and the powers, he made a show of them openly, triumphing over them in it. 

The phrase here in Colossians is similar to the phrase in Ephesians, but it turns out to be an ancient Jewish idiom. As many Bible translations now render it, it refers to a "criminal record", a list of the laws we have broken, an "IOU", or a "mortgage debt". The Phillips version, while not at all a literal rendering, colors the meaning in quite nicely: "Christ has utterly wiped out the damning evidence of broken laws and commandments which always hung over our heads, and has completely annulled it by nailing it over his own head on the cross".

Conclusion

This passage, Col 2:14-17, is not telling us that the Ten Commandments have been nailed to the cross. It's telling us that that the legal charges against us in a court of law, which normally would be nailed above the head of the person being executed on a cross (as Jesus' "charges" were nailed above his head on his cross - Luke 23:38), were nailed above Jesus' head instead of ours, freeing us from the charges and placing them on him.

The sister passage in Eph 2, also is not telling us that the Ten Commandments have been nailed to the cross. It's telling us that non-Jewish believers in the Messiah are just as Jewish as the native Jews, and that the separation between the two, as contained in human-uttered oral traditions, has been nailed to the cross.


Originally published at: https://kentwest.blogspot.com/2023/07/ten-commandments-nailed-to-cross.html

Friday, May 05, 2023

A Simple Introduction to CLAP in the Rust Programming Language

"CLAP" stands for Command-Line Argument Parser.

In order to get started using clap, you need to understand three things:

1 - How to Run a Simple Rust Program

Suppose you want to create a program called "fun". You should have a working installation of Rust and Cargo (the web is full of explanations of how to install this). In your file directory where you want to create the "fun" project:

$ cargo new fun

$ cargo run

This should print "Hello, World!", because every cargo-created project has this simple capability as a starting point.

2 - How to Work with a struct

This is a little bit more advanced. Here's the "src/main.rs" program from our "cargo new fun" command above:

src/main.rs
fn main()
	println!("Hello, world!");
}

A structure is simply a variable of a customized type, which often holds other variables. You should be familiar in a general way with variables, and know that they come in various kinds, or types: there are String variables and there are i32 variables and there are usize variables, etc. A struct simply allows you to create your own customized variable type, and fill it with a collection of other variables of various types.

Say you have a variable called "last" which holds a person's last name, and a variable called "age" which holds that person's age in years. The first variable might be a type of String (which holds a string of text), and the second might be a type of i8 (which holds an integer in the range of -128 to +127; a u8, which holds a range of 0 to 255, might be a better option).

Think of these two variables as two different types of fruit. We can carry them around in our hands if we like, or we can create a shopping bag to put them into. This shopping bag is analogous to a struct, except that a struct is well-defined as to what it can hold, whereas a shopping bag will hold just about anything pretty much, willy-nilly.

So a better way to think of it is as a custom-tool case, with exact-fitting compartments for the tools.

Example:

// This is the "master design" from which all toolboxes will be built.
struct toolbox_template {
  owner: String, // It will belong to a specific person,
  hammer: String, // and will hold a hammer,
  screwdriver: i8, // and a screwdriver of a certain size.
}

Note that this is just the definition of the "template" for a toolbox; it doesn't actually create a toolbox. Let's put this template into our "main.rs" file, along with the creation of two toolboxes based on this template, and then we'll print out a couple of messages about those tools. (We no longer need the "Hello, World!" println, so we'll delete it as per the Strikethru marking.)

src/main.rs
struct ToolboxTemplate {
  owner: String,
  hammer: String,
  screwdriver: i8,
}

fn main() {
  println!("Hello, world!");

  // Let's build a custom toolbox for Him.
  let his_box: ToolboxTemplate = ToolboxTemplate {
    owner: "Joe".to_string(),
    hammer: "sledge".to_string(),
    screwdriver: 8,
  };
  
  // And one for Her.
  let her_box: ToolboxTemplate = ToolboxTemplate {
    owner: "Jane".to_string(),
    hammer: "pink-handled".to_string(),
    screwdriver: 4
  };

  // Print the details of his toolbox.
  println!("{} has a {} hammer and a Number {} screwdriver.",
	his_box.owner, his_box.hammer, his_box.screwdriver
  );

  // And of hers.
  println!("{} has a {} hammer and a Number {} screwdriver.",
    her_box.owner, her_box.hammer, her_box.screwdriver
  );
} // end of main()

So you can see that a struct is just a custom-built variable, a "carrying case", that holds various other variables. The "struct" part defines the type, and then you have to create variables of that type which actually hold the desired data.

A Basic clap Setup

So, you can write a simple Rust program, and you kindda understand a struct. Good. Now lets add clap into the mix.

We first have to tell Cargo (the Rust "compiler" (sort of, but not really)) about Clap. This is done by adding some information to the "Cargo.toml" file. Before doing this, my Cargo.toml file for the "fun" program looks like this:

$ cat Cargo.toml 
[package]
name = "fun"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
$

If you have a recent version of Clap/Cargo, you can add Clap to your "fun" project with this command:

$ cargo add clap --features derive

That'll generate some churn, after which your "Cargo.toml" file will look more like this (the new stuff is in hilite):

$ cat Cargo.toml 
[package]
name = "fun"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = { version = "4.2.7", features = ["derive"] }

Now Cargo knows about clap. Your program does not, but Cargo does. When you compile your program again, Cargo will include all the clap stuff. Try it. Your program will run just as it did before, except for all the extra compiling of Clap.

We want to be able to specify our hammer and screwdriver on the command-line. The command will look something like this:

$ code run -- --name=Bob --hammer=peen --screwdriver=9

Add the hi-lighted code below, and delete the code that is in Strikethru. The "use" line simply tells our program the path to find parse-related stuff in the Clap crate. The "derive" line magically makes our struct definition able to associate its components with information coming from the Command-Line Argument Parser (clap).

src/main.rs
use clap::Parser;

#[derive(Parser)]
struct ToolboxTemplate {
  owner: String,
  hammer: String,
  screwdriver: i8,
}

fn main() {

  // Let's build a custom toolbox for Him.
  let his_box: ToolboxTemplate = ToolboxTemplate {
    owner: "Joe".to_string(),
    hammer: "sledge".to_string(),
    screwdriver: 8,
  };
  
  // And one for Her.
  let her_box: ToolboxTemplate = ToolboxTemplate {
    owner: "Jane".to_string(),
    hammer: "pink-handled".to_string(),
    screwdriver: 4
  };
  
  println!("{} has a {} hammer and a Number {} screwdriver.",
	his_box.owner, his_box.hammer, his_box.screwdriver
  );
  
  // Print the details of his toolbox.
  println!("{} has a {} hammer and a Number {} screwdriver.",
	his_box.owner, his_box.hammer, his_box.screwdriver
  );

  
  // Let's build a toolbox based on command-line arguments.
  let toolbox: ToolboxTemplate = ToolboxTemplate::parse();

  // And of hers.
  // And then print out the details of that toolbox.
  println!("{} has a {} hammer and a Number {} screwdriver.",
    her_box.owner, her_box.hammer, her_box.screwdriver
    toolbox.owner, toolbox.hammer, toolbox.screwdriver
  );
} // end of main()

If you try to run this with arguments, like so:

$ cargo run -- --name=Bob hammer=peen screwdriver=9

you'll get a message about an unexpected argument, and a tip that tells you to do what you're already doing, along with a "usage" blurb.

However, if you run your program this way:

$ cargo run -- --Bob peen 9

it works!

And that's all that's needed for a very basic clap setup. It's reading your command-line arguments according to the position they're given. If you change the order, like this:

$ cargo run -- --9 Bob peen

you'll break your program. Like this, though:

$ cargo run -- --peen Bob 9

and you'll just get unwanted results.

So we want to be able to name our arguments. That's done like this:

src/main.rs
use clap::Parser;

#[derive(Parser)]
struct ToolboxTemplate {
  #[arg(long)]
  owner: String,
  #[arg(long)]
  hammer: String,
  #[arg(long)]
  screwdriver: i8,
};
...

Try running that with $ cargo run -- --name=Bob hammer=peen screwdriver=9.

Hmm, a different error message. And again, it doesn't really make sense. Ah, but now I see it. We defined an argument named "owner", but we're typing in an argument named "name". Let's try this:

$ cargo run -- --ownername=Bob hammer=peen screwdriver=9.

Yay! That works!

But what if we really want to type in "name" instead of "owner", but don't want to change the variable name? Easy. Just do this:

src/main.rs
use clap::Parser;

#[derive(Parser)]
struct ToolboxTemplate {
  #[arg(long="name")]
  owner: String,
  #[arg(long)]
  hammer: String,
  #[arg(long)]
  screwdriver: i8,
};
...

What if we want to just use "s" for "screwdriver"?

src/main.rs
use clap::Parser;

#[derive(Parser)]
struct ToolboxTemplate {
  #[arg(long="name")]
  owner: String,
  #[arg(long)]
  hammer: String,
  #[arg(longshort)]
  screwdriver: i8,
};
...

Then our command-line would like look: cargo run -- --name=Bob --hammer=peen -s=9 Notice that single-letter arguments are introduced with a single-hyphen (e.g. -s=9), rather than a double-hyphen (--s=9).

What if we want to just use "d" for "screwDriver"?

src/main.rs
use clap::Parser;

#[derive(Parser)]
struct ToolboxTemplate {
  #[arg(long="name")]
  owner: String,
  #[arg(long)]
  hammer: String,
  #[arg(short='d')]
  screwdriver: i8,
};
...

Notice that the definition uses single-quotes around "d" rather than double-quotes.

What if we want to allow either a short form or a long form?

src/main.rs
use clap::Parser;

#[derive(Parser)]
struct ToolboxTemplate {
  #[arg(short,long="name")]
  owner: String,
  
  #[arg(short,long)]
  hammer: String,
    
  #[arg(short='d',long)]
  screwdriver: i8,
};
...

What if we want to make the name optional, with a default?

src/main.rs
use clap::Parser;

#[derive(Parser)]
struct ToolboxTemplate {
  #[arg(short,long="name",default_value_t=String::from("Bubba"))]
  owner: String,
 
  #[arg(short,long)]
  hammer: String,

  #[arg(short,long)]
  screwdriver: i8,
};
...

Note that "Bubba".to_string() won't work in this case, so we had to use an alternative method of converting a string literal to a String-type. Don't worry for now about understanding that; just know that usually it doesn't matter which conversion method you use, and that if one method doesn't work, try another.

This short tutorial won't answer all your questions, but it should get you started. Have fun, Rustacean!

OCR On Your Smart Phone

Did you know that your phone can possibly do Optical Character Recognition (OCR)?

Mine, a Samsung Galaxy S22 Ultra, can.

  • I took a picture of an informative display hanging on the wall, that had two columns of side-by-side text.
The image hanging on the wall.
The image
  • I then used the camera's photo editing software (the pen icon at the bottom of the image below) to crop the image to just one of the text columns, and saved (upper-right corner of the cropped image below) that image.
The image in my phone's Gallery.
Cropping the image to just one of the columns.
  • Then at the bottom right corner of that same editing window is a little yellow "T" in a broken-outline box (see the Gallery image above). When I clicked on that "T", it OCR'd the text and highlighted it.
  • I was then able to single-press on the text, which popped up a menu allowing me to "Select All", which popped up another menu allowing me to "Copy".
  • I could then go to an editor of some sort, and "Paste" the text into the editor.
  • I then went back to my image, and edited it again, and "Revert"ed it back to the original.
  • I then repeated the process for the second column.

In just a minute or two, I had the full text of the two columns of the informative display in an editor. With a clean original image with clean-looking text, the accuracy is very high.

Finished text.

The first unit of the hospital was erected in September 1924, at a cost of $150,000. West Texas Baptist Sanitarium had five stories, 72 rooms and admitted more than 800 patients during the first year.

When it opened, West Texas Baptist Sanitarium touted: hot and cold running water in each room; excellent nursing services; three modern elevators; three well-equipped operating rooms; capable physicians and surgeons; and an obstetrical department.

Labor and delivery services were quickly utilized. The first baby was born at Hendrick less than one month after the doors opened. Pauline Marie Turnidge, daughter of Mr. and Mrs. W.A. Turnidge, was born on October 17, 1924.

The vision of a hospital for the Texas Midwest was well under way as the vision of Reverend Millard Jenkins became a reality. The motto for West Texas Baptist Sanitarium was that it opened its doors to everyone, "no matter what your belief or creed."

Friday, February 03, 2023

Using Clap to Parse Rust Program Arguments

 

The native Rust argument-parsing capability is pretty limited, so we are now turning to the third-party crate, "Clap" (which stands for Command-Line Argument Parser). To do that, let's create a new project:
$ cd ~/projects/RUST
$ cargo new parse_clap
$ cd parse_clap

You should pretty much know what the "src/main.rs" file looks like in a new Cargo-created Rust project. Before we begin working with that file, we need to let Cargo know we're going to use the "Clap" crate. Since this is a new project, the "Cargo.toml" file has no dependencies listed. We'll need to add a dependency to the "Cargo.toml" file for Clap.

Clap is found at "crates.io" (a Rust-maintained web site for all things Rustacean). If you web-browse to that site, you can search for "clap", and you'll find (at least near the time of this writing) both a version 3 and a version 4. We want the most recent version, which at the time of this writing is 4.0.29. If you'll click on it to get more details, you'll see in the right-hand column a line that needs to be added to your "Cargo.toml" file, specifically to the "[dependencies]" section, in order to tell Cargo how to use the Clap crate. Up until very recent versions of Cargo, this had to be added to the file manually, but with more recent versions of Cargo, you can just run:

$ cargo add clap@4.0.29

If you don't want a specific version, but rather prefer the newest one available, you can instead run:

$ cargo add clap

Be aware, this may take a few minutes. If you do this, and then examine your "Cargo.toml" file again, you'll discover that the needed line has automatically been added to that file.

But, unfortunately, this instruction does not tell you that you need to add more to that command, in order to get all that we need. You can learn this by clicking on the "docs.rs" link at the "crates.io" website. The command you really need (you can run it even if you ran the previous command) is:

$ cargo add clap --features derive

The second run goes much faster than the first, because most of the work has already been done in our first attempt to add this to "Cargo.toml".

Now look again at your "Cargo.toml" file; if it looks pretty much like below, we should be good to go.

[package]
name = "parse_clap"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = { version = "4.0.29", features = ["derive"] }

Now we're ready to edit your "src/main.rs" file to be identical to the following:

use clap::Parser;

#[derive(Parser)]

struct ArgsType {
  /// Are you happy or sad?
  #[arg(short, long)]
  mood: String,
}

fn main() {
  let switches: ArgsType = ArgsType::parse();

  if switches.mood == "happy"
    { println!("Whoo-hoo! I am {}! {}! {}! {}!", switches.mood, switches.mood, switches.mood, switches.mood) };

  if switches.mood == "sad"
    { println!("Boo-hoo! I am {}!", switches.mood) };
  
  println!("Hello, world!");
} // end of main()

Now compile and run this with the indicated program arguments, like so:

cargo run -- --mood happy

Try providing different switches, in different order, in different numbers. Try the "-V" option, as well as the "-v" non-option. Try the "-h" option.

You can see that this Clap crate is already pretty useful, in that it provides some help screens when the arguments are not what are expected. It doesn't handle every wrong argument (as the program is currently written), but you can see that there's some potential here.

Let's try to understand this program, and then explore a bit of that potential.

The first line, "use clap::Parser;" preps the system for the other two "Parse"-related statements. Just know it's needed.

The next line tells the system that the parser will be deriving its arguments from the struct we build next. Clap can be configured using the Builder Application Programming Interface (API) or the Derive API (or a mix, as I understand it). As a general rule, unless you need to get deeper under the hood of Clap, you'll probably want to use the Derive API. The FAQ at https://docs.rs/clap/4.1.4/clap/_faq/index.html#when-should-i-use-the-builder-vs-derive-apis says this:

When should I use the builder vs derive APIs?

Our default answer is to use the Derive API:

  • Easier to read, write, and modify
  • Easier to keep the argument declaration and reading of argument in sync
  • Easier to reuse, e.g. clap-verbosity-flag

The Builder API is a lower-level API that someone might want to use for

So in other words, do it the way we're doing here, with a struct, not the way other tutorials might show you, without a struct. At least until you want/need to dive deeper.

The "struct" section provides a defining template for a new type of variable. This section does not declare an actual variable (we'll declare that later), but only a new type of variable. This new type of variable is based on a struct format. (A struct is a custom-made variable that holds other variables.)

Any variables declared to be of this new type are defined by this "struct' section, which defines what arguments are allowed to be given as the program's command-line arguments, and what the internal variable names are that will hold those arguments for use in the program. The Clap Derive API uses this struct type of structure to define and build this new type of variable. We could call this new type of variable anything we wanted, like "progInputs" or "options" or "OptionsType", etc. We're calling it "ArgsType". Currently this new type of variable defines one internal variable, named "mood", which is designated to hold String data.

The section that defines this inner "mood" variable is introduced by a line with three forward slashes (///). Whereas two slashes are the beginning of a "comment", which is ignored by the compiler but helps the programmer to keep notes about the code, a three-slash line functions as both a comment and a documentation line, which can be used by the compiler and by Clap to create help text. If you run cargo run -- --help, you can see that text, "Are you happy or sad?", in the output.

The line beginning with a hash mark tells Clap how to handle this program argument: whether it can be entered as a long form (--mood), or as a short form (-m), or must it be required, or should it have a default value, etc.

We can add additional internal variables (and therefore additional program input possibilities) by adding more "#[arg..." sections to the struct design. For example, in addition to the user's mood, perhaps we'd like to know the person's name and age:

struct ArgsType {
  /// Are you happy or sad?
  #[arg(short, long)]
  mood: String,
    
  /// What is your name?
  #[arg(short, long)]
  name: String,
  
  /// What is your age?
  #[arg(short, long, default_value_t = 16)]
  age: u8,
} 

Notice that the "age" variable has a default value (which is a bit silly, but this is just an example). Because of this, Clap won't require the user to enter that option, but it will the other two. You can force it to be required like this:

#[arg(short, long, default_value_t = 3), required(true)]

but that kind of defeats the purpose of having a default.

Although technically an age entered on the command line in a command such as cargo run -- --name Kent --age 35 starts out as a "String" (everything entered on the command line starts out as a "String"), by the time it gets to our "age" variable, Clap will have converted it from a "String" to a "u8" (which is an unsigned (i.e., positive) integer in the range of 0 to 255).

Note again that we have not yet declared a variable of this new type; we have only defined a new type of variable. We actually declare a variable in the main() function. Note also that since the struct is defined outside of the main() function, this definition of a new type of variable is "visible" (or "is in scope") to all parts of the program within this "main.rs" file. If we should create a new function later on in this same file, say, a function called "part_two()", that function will be able to access this "ArgsType" definition; had we put this definition within the main() function, it would only be visible to the main() function itself, but not to the "part_two()" function.

Now let's look at the main() function. The let switches: ArgsType = ArgsType::parse(); line actually defines our variable. The name of the variable is "switches", and the type of the variable is, not String and not i32 and not u8 or etc, but "ArgsType", the type we just invented. If this line seems complicated to you, take out the ": ArgsType", to make the line be just let switches = ArgsType::parse(); which may be less daunting to look at and therefore less daunting to understand. It's basically just calling a "function" named "parse" that is "located" in the "ArgsType" struct we just built (not exactly, but close enough), and assigning the results of that "function" to the variable "switches".

The variable "switches" now holds three variables within it (assuming three options are given as program inputs), which we can access as "switches.mood", "switches.name", and "switches.age". Here are some mods to our program, including a boolean flag to specify if the user is human or not, which defaults to "no":

use clap::Parser;

#[derive(Parser)]

struct ArgsType {
  /// Are you happy or sad?
  #[arg(short, long)]
  mood: String,
  
  /// What is your name?
  #[arg(short, long, value_name = "What yo momma called you...")]
  name: String,
  
  /// What is your age?
  #[arg(short, long, default_value_t = 16)]
  age: u8,

  /// Are you a human?
  #[arg(short = 'H', long, default_value_t = false)]  // 'h' would have conflicted with "help".
  human: bool,
}

fn main() {
  let switches: ArgsType = ArgsType::parse();

  if switches.human {
    println!("Hi, {}! You seem very {} to be {} years old, but that's understandable, since you are a human.",
      switches.name,
      switches.mood,
      switches.age
    );
  } else {
    println!("Hi, {}! You seem very {} to be {} years old, but that's understandable, since you are not a human.",
      switches.name,
      switches.mood,
      switches.age
    );
}

  if switches.mood == "happy"
    { println!("Whoo-hoo! I am {}! {}! {}! {}!", switches.mood, switches.mood, switches.mood, switches.mood) };

  if switches.mood == "sad"
    { println!("Boo-hoo! I am {}!", switches.mood) };
  
} // end of main()

Running this program results in:

$ cargo run -- --mood happy --name Kent --age 253
Compiling parse_clap v0.1.0 (/home/westk/projects/RUST/parse_clap)
Finished dev [unoptimized + debuginfo] target(s) in 0.56s
Running `target/debug/parse_clap --mood happy --name Kent --age 253`
Hi, Kent! You seem very happy to be 253 years old.
Whoo-hoo! I am happy! happy! happy! happy!
$
$ cargo run -- --name=Kent --age 253 -m happy --human -h
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/parse_clap --name=Kent --age 253 -m happy --human -h`
Usage: parse_clap [OPTIONS] --mood <MOOD> --name <What yo momma called you...>

Options:
  -m, --mood <MOOD>                         Are you happy or sad?
  -n, --name <What yo momma called you...>  What is your name?
  -a, --age <AGE>                           What is your age? [default: 16]
  -H, --human                               Are you a human?
  -h, --help                                Print help information
  -V, --version                             Print version information
$

Note also that various formats can be used for entering the arguments:

--name Kent
-nKent
-n=Kent
--name=Kent

But --nameKent won't work.

And that's pretty much it. We've got our feet wet with parsing arguments in Rust using the Clap crate.

Monday, January 09, 2023

A Man's Most Important Relationships

The most important relationship a man has is with his God.

But the relationship he should focus most on is that with his wife.

God doesn't need a man's attention. A wife does.

Men, your wife likely has two fundamental needs that you need to meet:

  • the need to feel secure (finances seem to you like a huge part of this, but there are bigger security issues for her)
  • the need to feel valued (and listening to her and considering her viewpoint is a huge part of this)

Focus on these things. Make her feel valued; make her feel safe.

 

Originally published at:
https://kentwest.blogspot.com/2023/01/a-mans-most-important-relationships.html